/*
 * This source file is Copyright 1995 by Evan Scott.
 * All rights reserved.
 * Permission is granted to distribute this file provided no
 * fees beyond distribution costs are levied.
 */

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/alerts.h>

#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>

#include <intuition/intuition.h>

#include <libraries/commodities.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/commodities.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "evtypes.h"
#include "verify.h"
#include "tcp.h"

#include "site.h"
#include "ftp.h"
#include "split.h"
#include "request.h"

#include "globals.h"

struct DosPacket *fh_listen(void)
{
	struct DosPacket *dp;
	struct MsgPort *reply;
	struct Message *msg;
	struct InfoData *id;
	struct FileHandle *fh;
	file_info *fi;
	b32 signals;
	split sd, sd2;
	lock *my_lock, *lock2;
	site *my_site;
	status_message *sm;
	b8 *s, *name;
	
	b32 pass_key = 0;
	boolean write_protect = false, disabled = false;
	
	signals = (1 << ftp_port->mp_SigBit) | (1 << status_control->mp_SigBit);
	
	while (1) {
		Wait(signals);
		
		while (sm = (status_message *)GetMsg(status_control)) {
			verify(sm, V_status_message);
			
			switch (sm->command) {
			case SM_KILL:
				ReplyMsg(&sm->header);
				return nil;
			case SM_SUSPEND:
				disabled = true;
				
				suspend_sites();
				
				break;
			case SM_RESUME:
				disabled = false;
				break;
			}
			ReplyMsg(&sm->header);
		}
		
		while (msg = GetMsg(ftp_port)) {
			dp = (struct DosPacket *)msg->mn_Node.ln_Name;
			
			reply = dp->dp_Port;
			
			truth(dp->dp_Link == msg);
			
			if (disabled && dp->dp_Type != action_IDLE) {
				dp->dp_Res1 = DOSFALSE;
				dp->dp_Res2 = ERROR_DEVICE_NOT_MOUNTED;
				
				dp->dp_Port = ftp_port;
				PutMsg(reply, dp->dp_Link);
				continue;
			}
			
			switch (dp->dp_Type) {
			case ACTION_NIL:
				dp->dp_Res1 = DOSTRUE;
				dp->dp_Res2 = 0;
				break;
			case ACTION_DIE:
				return dp;
			case ACTION_LOCATE_OBJECT:	/* Lock() */
				if (dp->dp_Arg3 != SHARED_LOCK && dp->dp_Arg3 != EXCLUSIVE_LOCK) {
					dp->dp_Res1 = 0;
					dp->dp_Res2 = ERROR_BAD_NUMBER;
					break;
				}
				
				if (!split_data((lock *)(dp->dp_Arg1 << 2), 
							(b8 *)(dp->dp_Arg2 << 2), &sd)) {
					/* might be ERROR_NO_FREE_STORE, but hey */
					dp->dp_Res1 = 0;
					dp->dp_Res2 = ERROR_INVALID_COMPONENT_NAME;
					break;
				}
				
				PutMsg(sd.port, dp->dp_Link);
				
				end_split(&sd);

				continue;
			case ACTION_RENAME_DISK:	/* Relabel() */
				name = (b8 *)(dp->dp_Arg1 << 2);
				s = (b8 *)allocate(name[0] + 2, V_bstr);
				if (!s) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_NO_FREE_STORE;
					break;
				}
				
				s[0] = name[0];
				memcpy(&s[1], &name[1], s[0]);
				s[1 + s[0]] = 0;
				
				/* perhaps should do some mutual exclusion here ... */
				ftp_volume->dol_Name = (b32)s >> 2;
				
				deallocate(volume_name, V_bstr);
				volume_name = s;
				
				dp->dp_Res1 = DOSTRUE;
				dp->dp_Res2 = 0;
				
				break;
			case ACTION_FREE_LOCK:		/* UnLock() */
			case ACTION_COPY_DIR:		/* DupLock() */
			case ACTION_EXAMINE_OBJECT:	/* Examine() */
			case ACTION_EXAMINE_NEXT:	/* ExNext() */
			case ACTION_PARENT:		/* ParentDir() */
			case ACTION_SET_DATE:		/* SetFileDate() */
				my_lock = (lock *)(dp->dp_Arg1 << 2);
				
				if (!my_lock) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_LOCK;
					break;
				}
				
				verify(my_lock, V_lock);
				
				PutMsg(my_lock->port, dp->dp_Link);
				continue;
			case ACTION_SET_PROTECT:
			case ACTION_SET_COMMENT:
				if (!split_data((lock *)(dp->dp_Arg2 << 2),
							(b8 *)(dp->dp_Arg3 << 2), &sd)) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_COMPONENT_NAME;
					break;
				}
				
				PutMsg(sd.port, dp->dp_Link);
				
				end_split(&sd);
				
				continue;
			case ACTION_RENAME_OBJECT:
				if (!split_data((lock *)(dp->dp_Arg1 << 2),
							(b8 *)(dp->dp_Arg2 << 2), &sd)) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_COMPONENT_NAME;
					break;
				}
				
				if (!split_data((lock *)(dp->dp_Arg3 << 2),
							(b8 *)(dp->dp_Arg4 << 2), &sd2)) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_COMPONENT_NAME;
					
					end_split(&sd);
					break;
				}
				
				if (sd.port == local_port &&
					!sd2.path) {	/* special case */
					
					PutMsg(local_port, dp->dp_Link);
					
					end_split(&sd);
					end_split(&sd2);
					continue;
				}
				
				if (sd.port != sd2.port) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_RENAME_ACROSS_DEVICES;
					
					end_split(&sd);
					end_split(&sd2);
					break;
				}
				
				PutMsg(sd.port, dp->dp_Link);
				
				end_split(&sd);
				end_split(&sd2);
				
				continue;
			case ACTION_DELETE_OBJECT:
				if (!split_data((lock *)(dp->dp_Arg1 << 2),
							(b8 *)(dp->dp_Arg2 << 2), &sd)) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_COMPONENT_NAME;
					break;
				}
				
				PutMsg(sd.port, dp->dp_Link);
				
				end_split(&sd);
				
				continue;
			case ACTION_CREATE_DIR:
				if (!split_data((lock *)(dp->dp_Arg1 << 2),
							(b8 *)(dp->dp_Arg2 << 2), &sd)) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_COMPONENT_NAME;
					break;
				}
				
				if (!sd.path)
				{
					PutMsg(local_port, dp->dp_Link);
				}
				else
				{
					PutMsg(sd.port, dp->dp_Link);
				}
				
				end_split(&sd);
				
				continue;
			case ACTION_DISK_INFO:
				id = (struct InfoData *)(dp->dp_Arg1 << 2);
				truth(id != nil);

				id->id_NumSoftErrors = 0;
				id->id_UnitNumber = 0;
				if (write_protect)
					id->id_DiskState = ID_WRITE_PROTECTED;
				else
					id->id_DiskState = ID_VALIDATED;
				id->id_NumBlocks = 1;
				id->id_NumBlocksUsed = 1;
				id->id_BytesPerBlock = 1024;
				id->id_DiskType = ID_DOS_DISK;
				id->id_VolumeNode = (b32)ftp_volume >> 2;
				id->id_InUse = 0;
				
				dp->dp_Res1 = DOSTRUE;
				dp->dp_Res2 = 0;

				break;
			case ACTION_INFO:
				id = (struct InfoData *)(dp->dp_Arg2 << 2);
				truth(id != nil);

				id->id_NumSoftErrors = 0;
				id->id_UnitNumber = 0;
				if (write_protect)
					id->id_DiskState = ID_WRITE_PROTECTED;
				else
					id->id_DiskState = ID_VALIDATED;
				id->id_NumBlocks = 1;
				id->id_NumBlocksUsed = 1;
				id->id_BytesPerBlock = 1024;
				id->id_DiskType = ID_DOS_DISK;
				id->id_VolumeNode = (b32)ftp_volume >> 2;
				id->id_InUse = 0;
				
				dp->dp_Res1 = DOSTRUE;
				dp->dp_Res2 = 0;

				break;
			case ACTION_SAME_LOCK:
				my_lock = (lock *)(dp->dp_Arg1 << 2);
				lock2 = (lock *)(dp->dp_Arg2 << 2);
				
				verify(my_lock, V_lock);
				verify(lock2, V_lock);
				
#ifdef SLDFKJ
				if (my_lock->port == local_port) {
					if (my_lock->rfsl == ftphosts_lock)
						{ show_string("lock 1 is ROOT"); }
					else
						{ show_string("lock 1 is local lock"); }
				} else {
					show_string(my_lock->fname);
				}
				
				if (lock2->port == local_port) {
					if (lock2->rfsl == ftphosts_lock)
						{ show_string("lock 2 is ROOT"); }
					else
						{ show_string("lock 2 is local lock"); }
				} else {
					show_string(lock2->fname);
				}
#endif
				
				if (my_lock->port != lock2->port) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_LOCK;
					break;
				}
				
				PutMsg(my_lock->port, dp->dp_Link);
				continue;
			case ACTION_READ:
			case ACTION_WRITE:
				fi = (file_info *)dp->dp_Arg1;
				
				verify(fi, V_file_info);
				
				PutMsg(fi->port, dp->dp_Link);
				
				continue;
			case ACTION_FINDUPDATE:
			case ACTION_FINDINPUT:
			case ACTION_FINDOUTPUT:
				if (!split_data((lock *)(dp->dp_Arg2 << 2), 
							(b8 *)(dp->dp_Arg3 << 2), &sd)) {
					/* might be ERROR_NO_FREE_STORE, but hey */
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_COMPONENT_NAME;
					break;
				}
				
				PutMsg(sd.port, dp->dp_Link);
				
				end_split(&sd);

				continue;
			case ACTION_END:
				fi = (file_info *)dp->dp_Arg1;
				
				verify(fi, V_file_info);
				
				PutMsg(fi->port, dp->dp_Link);
				
				continue;
			case ACTION_SEEK:
				fi = (file_info *)dp->dp_Arg1;
				
				verify(fi, V_file_info);
				
				PutMsg(fi->port, dp->dp_Link);
				
				continue;
			case ACTION_WRITE_PROTECT:
				if (write_protect && dp->dp_Arg1) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_DISK_WRITE_PROTECTED;
					break;
				} else if (!write_protect && !dp->dp_Arg1) {
					dp->dp_Res1 = DOSTRUE;
					dp->dp_Res2 = 0;
					break;
				} else if (!write_protect && dp->dp_Arg1) {
					dp->dp_Res1 = DOSTRUE;
					dp->dp_Res2 = 0;
					write_protect = 1;
					pass_key = dp->dp_Arg2;
					break;
				} else {
					if (pass_key == 0 || pass_key == dp->dp_Arg2) {
						dp->dp_Res1 = DOSTRUE;
						dp->dp_Res2 = 0;
						write_protect = 0;
						pass_key = 0;
						break;
					} else {
						dp->dp_Res1 = DOSFALSE;
						dp->dp_Res2 = ERROR_DISK_WRITE_PROTECTED;
						break;
					}
				}
				break;
			case ACTION_FH_FROM_LOCK:
				my_lock = (lock *)(dp->dp_Arg2 << 2);
				
				if (!my_lock) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_LOCK;
					break;
				}
				
				verify(my_lock, V_lock);
				
				PutMsg(my_lock->port, dp->dp_Link);
				continue;
			case ACTION_IS_FILESYSTEM:
				dp->dp_Res1 = DOSTRUE;
				dp->dp_Res2 = 0;
				break;
			case ACTION_COPY_DIR_FH:
			case ACTION_PARENT_FH:
//			case ACTION_EXAMINE_FH:
				fh = (struct FileHandle *)(dp->dp_Arg1 << 2);
				
				fi = (file_info *)fh->fh_Args;
				verify(fi, V_file_info);
				
				PutMsg(fi->port, dp->dp_Link);
				
				continue;
			case ACTION_EXAMINE_ALL:
				my_lock = (lock *)(dp->dp_Arg1 << 2);
				
				if (!my_lock) {
					dp->dp_Res1 = DOSFALSE;
					dp->dp_Res2 = ERROR_INVALID_LOCK;
					break;
				}
				
				verify(my_lock, V_lock);
				
				PutMsg(my_lock->port, dp->dp_Link);
				continue;
			case action_IDLE:
				my_site = (site *)dp->dp_Arg1;
				
				dp->dp_Port = ftp_port;
				PutMsg(reply, dp->dp_Link);	/* send the IDLE back */
				
				dp = &my_site->death_packet->sp_Pkt;
				dp->dp_Type = action_IDLE_DEATH;
				
				dp->dp_Port = startup_sync;
				PutMsg(my_site->port, dp->dp_Link);
				WaitPort(startup_sync); GetMsg(startup_sync);
				
				if (dp->dp_Res1) {
					my_lock = (lock *)dp->dp_Res2;
					while (my_lock) {
						adopt(my_lock, V_lock);
						my_lock = my_lock->next;
					}
					
					remove_site((site *)my_site);
				}
				
				continue;
			default:
				show_int(dp->dp_Type);
				dp->dp_Res1 = DOSFALSE;
				dp->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
				break;
			}
			
			dp->dp_Port = ftp_port;
			
			PutMsg(reply, dp->dp_Link);
		}
	}
}

void fh_ignore(void)
/* sits on our message port and cancels all actions */
{
	struct Message *msg;
	struct MsgPort *reply;
	struct DosPacket *dp;
	b32 signals;
	lock *l;
	
	signals = (1 << ftp_port->mp_SigBit);
	
	while (1) {
		Wait(signals);
		
		while (msg = GetMsg(ftp_port)) {
			dp = (struct DosPacket *)msg->mn_Node.ln_Name;
			
			if (dp->dp_Type == ACTION_FREE_LOCK) {
				l = (lock *)(dp->dp_Arg1 << 2);
				verify(l, V_lock);
				
				deallocate(l, V_lock);
				
				dp->dp_Res1 = DOSTRUE;
				dp->dp_Res2 = 0;
			} else {
				dp->dp_Res1 = DOSFALSE;
				dp->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
			}
			
			reply = dp->dp_Port;
			dp->dp_Port = ftp_port;
			
			PutMsg(reply, dp->dp_Link);
		}
	}
}
