home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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 <devices/timer.h>
-
- #include <dos/dos.h>
- #include <dos/dosextens.h>
- #include <dos/dostags.h>
-
- #include <proto/exec.h>
- #include <proto/dos.h>
- #include <proto/intuition.h>
- #include <proto/gadtools.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 "ftpinfo.h"
- #include "connect.h"
- #include "request.h"
-
- #include "globals.h"
- #include "strings.h"
-
- #define ERROR_GARBAGE_RECEIVED 15
-
- b8 *grow_info(b8 *a, b8 *b, int n)
- /*
- * concatenates a and b (not null terminated of length n) and returns
- * an allocated string with the result. a is freed.
- * a may be nil
- * b may not
- */
- {
- b8 *c, *d;
- int len;
-
- if (a) len = strlen(a);
- else len = 0;
-
- len += n + 1;
-
- c = (b8 *)allocate(len, V_cstr);
- if (!c) {
- if (a) deallocate(a, V_cstr);
- return nil;
- }
-
- if (a) {
- strcpy(c, a);
- d = c + strlen(c);
- } else {
- d = c;
- }
-
- if (n)
- memcpy(d, b, n);
- d[n] = 0;
-
- if (a) {
- deallocate(a, V_cstr);
- }
-
- a = c;
- while (*a) {
- if (*a == '\t') *a = ' '; /* hmmm */
- if (*a == '\r') *a = '\n';
- a++;
- }
-
- return c;
- }
-
- boolean substr(b8 *s, b8 *ss)
- /*
- * returns true if s contains ss (non-case-sensitive)
- * s may be nil, in which case false is returned
- */
- {
- int len;
-
- if (s == nil) return false;
-
- len = strlen(ss);
-
- while (*s) {
- if (strnicmp(s, ss, len) == 0) return true;
- s++;
- }
-
- return false;
- }
-
- void inform(struct IntuitionBase *IntuitionBase, b8 *title, b8 *text, b8 *site, b32 errno)
- {
- struct EasyStruct es;
-
- es.es_StructSize = sizeof(struct EasyStruct);
- es.es_Flags = 0;
- es.es_Title = title;
- es.es_GadgetFormat = strings[MSG_OK];
- es.es_TextFormat = text;
-
- EasyRequest(nil, &es, nil, site, errno);
- }
-
- tcpmessage *new_message(site *sp)
- /*
- * get a new tcpmessage
- */
- {
- tcpmessage *intr;
- struct MsgPort *sync;
-
- verify(sp, V_site);
-
- intr = sp->intr;
- verify(intr, V_tcpmessage);
-
- sync = sp->sync;
-
- /* "re-use" intr to get a new tcpmessage */
- intr->command = TCP_NEWMESSAGE;
- intr->header.mn_ReplyPort = sync;
-
- PutMsg(tcp, &intr->header);
- WaitPort(sync); GetMsg(sync); /* this _should_ never block */
-
- return (tcpmessage *)intr->data;
- }
-
- void interrupt_message(site *sp, tcpmessage *tm)
- /*
- * interrupt tm
- */
- {
- tcpmessage *intr;
- struct MsgPort *sync;
-
- verify(sp, V_site);
- verify(tm, V_tcpmessage);
-
- intr = sp->intr;
- verify(intr, V_tcpmessage);
-
- sync = sp->sync;
-
- intr->command = TCP_INTERRUPT;
- intr->header.mn_ReplyPort = sync;
- intr->interrupt = tm;
-
- PutMsg(tcp, &intr->header);
-
- /*
- * NB: I could probably assume tm->header.mn_ReplyPort == sync safely, but
- * this seems a bit more "correctly generic" (although potentially more buggy)
- */
- WaitPort(tm->header.mn_ReplyPort); GetMsg(tm->header.mn_ReplyPort); /* aborted tm coming back */
- WaitPort(sync); GetMsg(sync); /* intr coming back successful */
-
- return;
- }
-
- b32 control_write(site *sp, b8 *command, b32 csig)
- /*
- * writes the string command to the control connection
- * Inputs:
- * sp : site pointer
- * command : null terminated command string
- * csig : additional cancel signals, 0 is ok
- *
- * returns the tcp error
- */
- {
- tcpmessage *tm;
- struct MsgPort *sync;
- b32 signals, rsigs;
-
- verify(sp, V_site);
- truth(command != nil);
-
- // truth(sp->connected);
-
- tm = sp->control;
- verify(tm, V_tcpmessage);
-
- sync = sp->sync;
-
- tm->command = TCP_WRITE;
- tm->length = strlen(command);
- tm->flags = 0;
- tm->data = command;
- tm->header.mn_ReplyPort = sync;
-
- csig |= sp->abort_signals | sp->disconnect_signals;
- signals = (1 << sync->mp_SigBit) | csig;
-
- PutMsg(tcp, &tm->header);
- do {
- rsigs = Wait(signals);
- if (rsigs & csig) {
- interrupt_message(sp, tm);
-
- if (rsigs & sp->disconnect_signals) {
- disconnect(sp);
- }
-
- return ERROR_INTERRUPTED;
- }
- } while (!GetMsg(sync));
-
- return tm->error;
- }
-
- b32 make_connection(site *sp, tcpmessage *tm, b8 *addr, b16 port, b32 csig)
- /*
- * make a connection to a remote host
- * Inputs:
- * sp : site pointer
- * tm : an unused tcpmessage
- * addr : null terminated string address
- * port : port number to connect to
- * csig : additional cancel signals (may be 0)
- *
- * Returns:
- * standard tcp error
- */
- {
- struct MsgPort *sync;
- b32 signals, rsigs, asigs;
-
- verify(sp, V_site);
- verify(tm, V_tcpmessage);
- truth(addr != nil);
-
- sync = sp->sync;
-
- tm->header.mn_ReplyPort = sync;
- tm->command = TCP_CONNECT;
- tm->data = addr;
- tm->port.w = port;
- tm->flags = 0;
-
- asigs = csig | sp->abort_signals | sp->disconnect_signals;
- signals = asigs | (1 << sync->mp_SigBit);
-
- PutMsg(tcp, &tm->header);
- do {
- rsigs = Wait(signals);
- if (rsigs & asigs) {
- interrupt_message(sp, tm);
-
- if (rsigs & sp->disconnect_signals) {
- disconnect(sp);
- }
-
- return ERROR_INTERRUPTED;
- }
- } while (!GetMsg(sync));
-
- return tm->error;
- }
-
- void break_connection(site *sp, tcpmessage *tm)
- /*
- * do a TCP_CLOSE on tm
- */
- {
- struct MsgPort *sync;
-
- verify(sp, V_site);
- verify(tm, V_tcpmessage);
-
- sync = sp->sync;
-
- tm->command = TCP_CLOSE;
- tm->header.mn_ReplyPort = sync;
-
- PutMsg(tcp, &tm->header);
- WaitPort(sync); GetMsg(sync);
-
- return;
- }
-
- boolean passive_response(b8 *s, b8 *addr, b16 *portp)
- /*
- * parse the response to a PASV command
- * Inputs:
- * s : the response to the PASV (null terminated)
- * addr : a buffer to hold the address (should be as long as s)
- * portp : where to put the port number
- *
- * Returns:
- * true if it was a valid PASV response
- */
- {
- b8 *t;
- b16 ncommas, portn;
-
- truth(s != nil);
- truth(addr != nil);
- truth(portp != nil);
-
- while (*s && *s != '(') s++;
- if (!*s) return false;
-
- /* first calculate port number ... skip the first 4 commas */
-
- ncommas = 0;
- t = s;
- while (*t && *t != ')' && ncommas < 4) {
- if (*t == ',') ncommas++;
- t++;
- }
-
- portn = atoi(t) * 256; /* possibly a more thorough check of whether these are legit numbers */
- while (*t && *t != ',' && *t != ')') t++;
- if (*t == ',') portn += atoi(t+1);
-
- /*
- * now copy the first 4 fields to addr, changing commas to periods
- * (hopefully making a legitimate ip address)
- */
-
- ncommas = 0;
- s++; /* move s past the '(' */
- while (*s && ncommas < 4) {
- if (*s == ',') {
- ncommas++;
- if (ncommas == 4) *addr = 0;
- else *addr++ = '.';
- s++;
- } else {
- *addr++ = *s++;
- }
- }
-
- *portp = portn;
-
- return true;
- }
-
- b32 response(site *sp, b32 csig, b8 **infop, b8 *code)
- /*
- * reads response from remote server on sp->control
- * Inputs:
- * sp : site pointer
- * csig : cancel signals (in addition to standard sp->abort etc ... usually a window) 0 is ok.
- * infop : pointer to a string pointer to store the servers response message
- * code : 3 byte array for the response code (eg 257 "/usr/dm/pathname" directory created)
- *
- * Returns:
- * standard tcp error code with the additional error ERROR_GARBAGE_RECEIVED
- */
- {
- tcpmessage *tm;
- struct MsgPort *sync;
- b32 signals, rsigs, asigs;
- b8 *info, *z;
-
- verify(sp, V_site);
-
- truth(code != nil);
- truth(infop != nil);
-
- *infop = nil;
-
- tm = sp->control;
- sync = sp->sync;
-
- verify(tm, V_tcpmessage);
-
- asigs = csig | sp->disconnect_signals | sp->abort_signals; /* abort signals */
- signals = asigs | (1 << sync->mp_SigBit);
-
- tm->command = TCP_READ;
- tm->flags = FLAG_READLINE;
- tm->data = sp->read_buffer;
- tm->length = READ_BUFFER_LENGTH;
- tm->header.mn_ReplyPort = sync;
-
- PutMsg(tcp, &tm->header);
- do {
- rsigs = Wait(signals);
- if (rsigs & asigs) {
- state_change(sp, SS_ABORTING);
-
- interrupt_message(sp, tm);
-
- /* cancelling the read of the response is guaranteed fatal anyway ... but ... */
-
- if (rsigs & sp->disconnect_signals) {
- disconnect(sp);
- }
-
- return ERROR_INTERRUPTED;
- }
- } while (!GetMsg(sync));
-
- if (tm->error != NO_ERROR) {
- show_int(tm->error);
- return tm->error;
- }
-
- z = sp->read_buffer;
-
- if (tm->result < 4 ||
- z[0] < '0' || z[0] > '9' ||
- z[1] < '0' || z[1] > '9' ||
- z[2] < '0' || z[2] > '9') {
- show_int(tm->result);
- return ERROR_GARBAGE_RECEIVED;
- }
-
- code[0] = z[0];
- code[1] = z[1];
- code[2] = z[2];
-
- info = grow_info(nil, &z[4], tm->result - 4);
- if (z[3] == '-') { /* we have a continuation message */
- while (1) {
- PutMsg(tcp, &tm->header);
- do {
- rsigs = Wait(signals);
- if (rsigs & asigs) {
- state_change(sp, SS_ABORTING);
-
- if (info)
- deallocate(info, V_cstr);
-
- interrupt_message(sp, tm);
-
- if (rsigs & sp->disconnect_signals) {
- disconnect(sp);
- }
-
- return ERROR_INTERRUPTED;
- }
- } while (!GetMsg(sync));
-
- if (tm->error != NO_ERROR) { /* tell them about the error */
- if (info)
- deallocate(info, V_cstr);
-
- return tm->error;
- }
-
- if (tm->result < 4) { /* not enough to even check if codes are equal */
- info = grow_info(info, z, tm->result);
- continue;
- }
-
- if (z[0] == code[0] &&
- z[1] == code[1] &&
- z[2] == code[2]) {
- info = grow_info(info, &z[4], tm->result - 4);
- if (z[3] == ' ') break; /* end of continuation */
- } else {
- info = grow_info(info, z, tm->result);
- }
- }
- }
-
- *infop = info;
- return NO_ERROR;
- }
-
- b16 numeric_reply(b8 *s)
- {
- return (b16)((s[0] - '0') * 100 + (s[1] - '0') * 10 + (s[2] - '0'));
- }
-
- boolean retry_cancel(struct IntuitionBase *IntuitionBase, b8 *title, b8 *info)
- /*
- * paged information with retry/cancel buttons
- * returns true for retry
- * info may be nil (well, sortof)
- */
- {
- b8 *z, *s, tmp;
- struct EasyStruct es;
- int nlines;
-
- es.es_StructSize = sizeof(struct EasyStruct);
- es.es_Flags = 0;
- es.es_Title = title;
- es.es_TextFormat = "%s";
-
- if (info)
- z = info;
- else
- z = strings[MSG_UNKNOWN];
-
- more:
- s = z;
- nlines = 0;
-
- while (*z && nlines < MORE_LINES) {
- if (*z == '\n') nlines++;
- z++;
- }
-
- if (*z) {
- es.es_GadgetFormat = strings[MSG_RETRY_MORE_CANCEL];
- } else {
- es.es_GadgetFormat = strings[MSG_RETRY_CANCEL];
- }
-
- tmp = *z;
- *z = 0;
-
- switch (EasyRequest(nil, &es, nil, s)) {
- case 0: /* cancel */
- *z = tmp;
- return false;
- case 1: /* retry */
- *z = tmp;
- return true;
- case 2: /* more */
- *z = tmp;
- if (!*z) return true;
- goto more;
- }
- }
-
- void ok(struct IntuitionBase *IntuitionBase, b8 *title, b8 *info)
- /*
- * paged information with ok button
- * info may be nil (sortof)
- */
- {
- b8 *z, *s, tmp;
- struct EasyStruct es;
- int nlines;
-
- es.es_StructSize = sizeof(struct EasyStruct);
- es.es_Flags = 0;
- es.es_Title = title;
- es.es_TextFormat = "%s";
-
- if (info)
- z = info;
- else
- z = strings[MSG_UNKNOWN];
-
- more:
- s = z;
-
- nlines = 0;
- while (*z && nlines < MORE_LINES) {
- if (*z == '\n') nlines++;
- z++;
- }
-
- if (*z) {
- es.es_GadgetFormat = strings[MSG_MORE_OK];
- } else {
- es.es_GadgetFormat = strings[MSG_OK];
- }
-
- tmp = *z;
- *z = 0;
-
- if (EasyRequest(nil, &es, nil, s) && tmp) {
- *z = tmp;
- goto more;
- }
-
- *z = tmp;
- return;
- }
-
- void disconnect(site *sp)
- /*
- * rudely close control connection and clean up state information on site sp
- */
- {
- tcpmessage *tm;
- struct MsgPort *sync;
-
- verify(sp, V_site);
-
- if (!sp->connected) return;
-
- sync = sp->sync;
-
- state_change(sp, SS_DISCONNECTING);
-
- tm = sp->cfile; /* file open, have to close it */
- if (tm) {
- verify(tm, V_tcpmessage);
- verify(sp->file_list, V_file_info);
-
- tm->command = TCP_CLOSE;
- tm->header.mn_ReplyPort = sync;
-
- PutMsg(tcp, &tm->header); /* send CLOSE on file tm */
- WaitPort(sync); GetMsg(sync);
-
- tm->command = TCP_DISPOSE;
- PutMsg(tcp, &tm->header);
-
- sp->cfile = nil;
-
- deallocate(sp->file_list, V_file_info);
- sp->file_list = nil;
- }
-
- tm = sp->control;
- verify(tm, V_tcpmessage);
-
- tm->command = TCP_CLOSE;
- tm->header.mn_ReplyPort = sync;
-
- PutMsg(tcp, &tm->header); /* send CLOSE */
-
- sp->connected = false;
-
- if (sp->cwd) {
- deallocate(sp->cwd, V_cstr);
- sp->cwd = nil;
- }
-
- while (sp->infos) free_info_header(sp->infos);
-
- WaitPort(sync); GetMsg(sync); /* wait for CLOSE to come back */
-
- /* it shouldn't really fail ... not sure if we can do anything if it has */
-
- state_change(sp, SS_DISCONNECTED);
-
- return;
- }
-
- b32 read_file(site *sp, b8 *s, b32 *length)
- /*
- * read *length bytes from open file
- * Inputs:
- * sp : site pointer
- * s : data buffer
- * length : pointer to length to read, changed to length actually read
- *
- * Result:
- * tcp error is returned, *length is modified to be actual length read
- */
- {
- tcpmessage *tm;
- struct MsgPort *sync;
- b32 signals, asigs, rsigs;
-
- verify(sp, V_site);
- truth(s != nil);
- truth(length != nil);
-
- tm = sp->cfile;
- verify(tm, V_tcpmessage);
-
- sync = sp->sync;
-
- tm->command = TCP_READ;
- tm->flags = 0;
- tm->data = s;
- tm->length = *length;
- tm->header.mn_ReplyPort = sync;
-
- asigs = sp->abort_signals | sp->disconnect_signals;
- signals = asigs | (1 << sync->mp_SigBit);
-
- PutMsg(tcp, &tm->header);
- do {
- rsigs = Wait(signals);
- if (rsigs & asigs) {
- state_change(sp, SS_ABORTING);
-
- interrupt_message(sp, tm);
-
- if (rsigs & sp->disconnect_signals) {
- disconnect(sp);
- } else {
- close_file(sp, false);
- }
-
- *length = 0;
- return ERROR_INTERRUPTED;
- }
- } while (!GetMsg(sync));
-
- if (tm->result > 0) {
- *length = tm->result;
- return NO_ERROR;
- } else {
- *length = 0;
- return tm->error;
- }
- }
-
- b32 write_file(site *sp, b8 *s, b32 *length)
- /*
- * write *length bytes to an open file (almost identical copy to read_file above)
- * Inputs:
- * sp : site pointer
- * s : data buffer
- * length : pointer to length to write, changed to length actually written
- *
- * Result:
- * tcp error is returned, *length is modified to be actual length written
- */
- {
- tcpmessage *tm;
- struct MsgPort *sync;
- b32 signals, asigs, rsigs;
-
- verify(sp, V_site);
- truth(s != nil);
- truth(length != nil);
-
- tm = sp->cfile;
- verify(tm, V_tcpmessage);
-
- sync = sp->sync;
-
- tm->command = TCP_WRITE;
- tm->flags = 0;
- tm->data = s;
- tm->length = *length;
- tm->header.mn_ReplyPort = sync;
-
- asigs = sp->abort_signals | sp->disconnect_signals;
- signals = asigs | (1 << sync->mp_SigBit);
-
- PutMsg(tcp, &tm->header);
- do {
- rsigs = Wait(signals);
- if (rsigs & asigs) {
- state_change(sp, SS_ABORTING);
-
- interrupt_message(sp, tm);
-
- if (rsigs & sp->disconnect_signals) {
- disconnect(sp);
- } else {
- close_file(sp, false);
- }
-
- *length = 0;
- return ERROR_INTERRUPTED;
- }
- } while (!GetMsg(sync));
-
- if (tm->result > 0) {
- *length = tm->result;
- return NO_ERROR;
- } else {
- *length = 0;
- return tm->error;
- }
- }
-
- b32 open_file(site *sp, b8 *s, boolean writing, b8 *leaf_name)
- /*
- * open file with name in s
- * Inputs:
- * sp : site pointer
- * s : file name
- * writing : true if opened for writing, false if for reading
- *
- * Returns:
- * 0 : no error
- * non-0 : standard file system errors (ERROR_OBJECT_NOT_FOUND etc)
- */
- {
- tcpmessage *tm, *newtm;
- struct MsgPort *sync;
- b8 reply[4], *info;
- b8 *leaf;
- b32 error;
- b16 port_number;
- file_info *fi;
-
- verify(sp, V_site);
- truth(s != nil);
-
- /* a few conditions we are assuming */
- truth(sp->connected);
- truth(sp->cfile == nil);
- truth(sp->file_list == nil);
-
- if (s[0] == 0) {
- /* they are trying to open the root of this site as a file ... */
- return ERROR_OBJECT_WRONG_TYPE;
- }
-
- tm = sp->control;
- verify(tm, V_tcpmessage);
-
- sync = sp->sync;
-
- tm->header.mn_ReplyPort = sync;
-
- leaf = cd_parent(sp, s);
- if (!leaf) {
- /* there are other possible reasons here, but I'm being lazy ... */
- return ERROR_DIR_NOT_FOUND;
- }
-
- if (leaf_name) leaf = leaf_name;
-
- state_change(sp, SS_OPENING);
-
- newtm = new_message(sp);
- if (newtm) {
- fi = (file_info *)allocate(sizeof(*fi) + strlen(s) + 1, V_file_info);
- if (fi) {
- strcpy(fi->fname, s);
-
- if (control_write(sp, "PASV\r\n", 0) == NO_ERROR) {
- if (writing) { /* yes, I do this twice :( */
- sprintf(sp->read_buffer, "STOR %s\r\n", leaf);
- } else {
- sprintf(sp->read_buffer, "RETR %s\r\n", leaf);
- }
- if (!sp->quick || control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
- if (response(sp, 0, &info, reply) == NO_ERROR) {
- if (reply[0] == '2') {
- if (info) {
- if (passive_response(info, sp->read_buffer, &port_number)) {
- deallocate(info, V_cstr);
-
- if (make_connection(sp, newtm, sp->read_buffer, port_number, 0) == NO_ERROR) {
- if (writing) { /* and again */
- sprintf(sp->read_buffer, "STOR %s\r\n", leaf);
- } else {
- sprintf(sp->read_buffer, "RETR %s\r\n", leaf);
- }
- if (sp->quick || control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
- /* this next response will be to the RETR/STOR */
- if (response(sp, 0, &info, reply) == NO_ERROR) {
- if (info) {
- #ifdef VERIFY
- if (reply[0] != '1') {
- reply[3] = 0;
- show_string(reply);
- show_string(info);
- }
- #endif
- deallocate(info, V_cstr);
- }
- if (reply[0] == '1') {
- ensure(fi, V_file_info);
-
- fi->rfarg = 0;
- fi->rpos = 0;
- fi->vpos = 0;
- fi->end = 0;
- fi->closed = false;
- fi->seek_end = false;
- fi->eof = false;
- fi->port = nil;
- fi->type = (writing) ? ACTION_FINDOUTPUT : ACTION_FINDINPUT;
-
- sp->cfile = newtm;
- sp->file_list = fi;
- fi->next = nil;
-
- return 0;
- } else {
- switch (numeric_reply(reply)) {
- case 450:
- case 520:
- case 550:
- if (writing) {
- error = ERROR_INVALID_COMPONENT_NAME;
- } else {
- error = ERROR_OBJECT_NOT_FOUND;
- }
- break;
- case 521:
- case 532:
- case 533:
- if (writing) {
- error = ERROR_WRITE_PROTECTED;
- } else {
- error = ERROR_READ_PROTECTED;
- }
- break;
- case 452:
- case 552:
- error = ERROR_DISK_FULL;
- break;
- case 553:
- if (writing) {
- error = ERROR_WRITE_PROTECTED;
- } else {
- error = ERROR_INVALID_COMPONENT_NAME;
- }
- break;
- default:
- error = ERROR_OBJECT_NOT_FOUND;
- break;
- }
- }
-
- /* no need to disconnect sp */
-
- deallocate(fi, V_file_info);
-
- break_connection(sp, newtm);
-
- newtm->command = TCP_DISPOSE;
- PutMsg(tcp, &newtm->header);
-
- return error;
- } else {
- show_string("Error reading response to RETR/STOR");
- error = ERROR_OBJECT_NOT_FOUND;
- }
- break_connection(sp, newtm);
- } else {
- show_string("error writing RETR/STOR");
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- show_string("Error making connection");
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- show_string("Bad PASV response");
- deallocate(info, V_cstr);
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- show_string("no info");
- error = ERROR_NO_FREE_STORE;
- }
- } else {
- show_string("non-'2' response to PASV");
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- show_string("error reading response to PASV");
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- show_string("error writing RETR/STOR");
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- show_string("error writing PASV");
- error = ERROR_OBJECT_NOT_FOUND;
- }
-
- deallocate(fi, V_file_info);
- } else error = ERROR_NO_FREE_STORE;
-
- newtm->command = TCP_DISPOSE;
- PutMsg(tcp, &newtm->header);
-
- disconnect(sp);
- } else error = ERROR_NO_FREE_STORE;
-
- return error;
- }
-
- /* this is how large our flushing buffer is when attempting an abort */
- #define FLUSH_SIZE 100
-
- void close_file(site *sp, boolean normal_close)
- /*
- * close currently open file for site
- * Inputs:
- * sp : site pointer
- * normal_close : true if closed normally, false if closed by async abort
- */
- {
- tcpmessage *tm, *filetm, *ret;
- file_info *fi;
- struct MsgPort *sync;
- b8 *info, reply[4], flush[FLUSH_SIZE];
- b32 signals, asigs, rsigs;
-
- verify(sp, V_site);
-
- tm = sp->control;
- filetm = sp->cfile;
- fi = sp->file_list;
-
- verify(tm, V_tcpmessage);
- verify(filetm, V_tcpmessage);
- verify(fi, V_file_info);
-
- if (normal_close) {
- sp->cfile = nil;
- sp->cfile_type = 0;
- sp->file_list = 0;
- }
-
- state_change(sp, SS_CLOSING);
-
- sync = sp->sync;
-
- signals = (1 << sync->mp_SigBit) | sp->disconnect_signals | sp->abort_signals;
- asigs = sp->disconnect_signals | sp->abort_signals;
-
- #ifdef VERIFY
- if (fi->eof && fi->rpos < fi->end) {
- show_string("Closing : EOF before fi->end");
- show_int(fi->rpos);
- show_int(fi->end);
- }
- #endif
-
- if (fi->type == ACTION_FINDINPUT && fi->rpos < fi->end && !fi->eof) {
- if (normal_close) {
- deallocate(fi, V_file_info);
- } else {
- fi->eof = true;
- }
-
- /* have to ABOR :( */
- show_string("Attempting ABOR");
-
- if (control_write(sp, "ABOR\r\n", 0) != NO_ERROR) {
- show_string("close file failed X1");
-
- break_connection(sp, filetm);
-
- disconnect(sp);
-
- filetm->command = TCP_DISPOSE;
- PutMsg(tcp, &filetm->header);
-
- return;
- }
-
- /* can't use response because we need to flush filetm at the same time */
-
- filetm->command = TCP_READ;
- filetm->flags = 0;
- filetm->data = flush;
- filetm->length = FLUSH_SIZE;
- filetm->header.mn_ReplyPort = sync;
-
- tm->command = TCP_READ;
- tm->flags = FLAG_READLINE;
- tm->data = sp->read_buffer;
- tm->length = READ_BUFFER_LENGTH;
- tm->header.mn_ReplyPort = sync;
-
- PutMsg(tcp, &tm->header);
- PutMsg(tcp, &filetm->header);
-
- while (1) {
- rsigs = Wait(signals);
- if (rsigs & asigs) {
- state_change(sp, SS_ABORTING);
-
- interrupt_message(sp, tm);
- interrupt_message(sp, filetm);
-
- break;
- }
-
- ret = (tcpmessage *)GetMsg(sync);
- if (ret == tm) {
- /* wait for filetm */
- WaitPort(sync); GetMsg(sync);
- break;
- }
- if (ret->error != NO_ERROR) { /* filetm is done */
- /* wait for tm */
- WaitPort(sync); GetMsg(sync);
- break;
- }
- PutMsg(tcp, &filetm->header);
- }
-
- break_connection(sp, filetm);
-
- if (normal_close) {
- filetm->command = TCP_DISPOSE;
- PutMsg(tcp, &filetm->header);
- }
-
- if (tm->error != NO_ERROR) {
- show_string("close file failed X2");
- disconnect(sp);
-
- return;
- }
-
- show_string("First ABOR response");
- #ifdef VERIFY
- if (sp->read_buffer[3] == '-') show_string("continuation reply on ABOR");
- #endif
- if (!normal_close) {
- /* leave the close response until we do the real close later */
- return;
- }
-
- switch (response(sp, 0, &info, reply)) {
- case NO_ERROR:
- break;
- default:
- show_string("close file failed X3");
- disconnect(sp);
-
- return;
- }
-
- show_string("Second ABOR response");
-
- #ifdef VERIFY
- if (reply[0] != '2') {
- reply[3] = 0;
- show_string(reply);
- show_string(info);
- }
- #endif
-
- if (info) deallocate(info, V_cstr);
-
- show_string("ABOR completed");
- return;
- }
-
- if (normal_close) {
- deallocate(fi, V_file_info);
- } else {
- fi->eof = true;
- }
-
- break_connection(sp, filetm);
-
- if (!normal_close) {
- /* leave the final close response until we do the real close later */
- return;
- }
-
- filetm->command = TCP_DISPOSE;
- PutMsg(tcp, &filetm->header);
-
- switch (response(sp, 0, &info, reply)) {
- case NO_ERROR:
- break;
- default:
- show_string("close failed 1");
-
- disconnect(sp);
- return;
- }
-
- if (info) deallocate(info, V_cstr);
-
- /* we don't really care what they returned here */
-
- #ifdef VERIFY
- if (reply[0] != '2') {
- show_string("Non '2' close of file");
- sp->read_buffer[0] = reply[0];
- sp->read_buffer[1] = reply[1];
- sp->read_buffer[2] = reply[2];
- sp->read_buffer[3] = 0;
- show_string(sp->read_buffer);
- }
- #endif
-
- return;
- }
-
- b32 delete_file(site *sp, b8 *s)
- /*
- * delete file with name in s
- * Inputs:
- * sp : site pointer
- * s : full path name
- *
- * Returns:
- * standard file system errors
- */
- {
- b8 *leaf;
- b32 error;
- boolean perm;
- b8 *info, reply[3];
- ftpinfo *fi;
-
- verify(sp, V_site);
- truth(s != nil);
-
- if (s[0] == 0) {
- /* trying to delete root */
- return ERROR_OBJECT_WRONG_TYPE;
- }
-
- leaf = cd_parent(sp, s);
- if (!leaf) {
- /* again, being lazy here */
- return ERROR_DIR_NOT_FOUND;
- }
-
- state_change(sp, SS_DELETING);
-
- fi = get_info(sp, s);
- if (fi) {
- leaf = fi->name;
- }
-
- sprintf(sp->read_buffer, "DELE %s\r\n", leaf);
-
- if (control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
- if (response(sp, 0, &info, reply) == NO_ERROR) {
- perm = substr(info, "perm");
- if (info) deallocate(info, V_cstr);
-
- switch (reply[0]) {
- case '2':
- return 0; /* success */
- case '4':
- /* temp failure ... */
- /* most likely reason */
- return ERROR_OBJECT_IN_USE;
- default:
- if (numeric_reply(reply) == 502) return ERROR_ACTION_NOT_KNOWN;
- if (perm)
- return ERROR_DELETE_PROTECTED;
- else
- return ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- disconnect(sp);
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- disconnect(sp);
- error = ERROR_OBJECT_NOT_FOUND;
- }
-
- return error;
- }
-
- b32 delete_directory(site *sp, b8 *s)
- /*
- * delete directory with name in s
- * Inputs:
- * sp : site pointer
- * s : full path name
- *
- * Returns:
- * standard file system errors
- */
- {
- b8 *leaf;
- b32 error;
- boolean perm, no_such;
- b8 *info, reply[3];
- ftpinfo *fi;
-
- verify(sp, V_site);
- truth(s != nil);
-
- if (s[0] == 0) {
- /* trying to delete root */
- return ERROR_OBJECT_WRONG_TYPE;
- }
-
- leaf = cd_parent(sp, s);
- if (!leaf) {
- /* again, being lazy here */
- return ERROR_DIR_NOT_FOUND;
- }
-
- state_change(sp, SS_DELETING);
-
- fi = get_info(sp, s);
- if (fi) {
- leaf = fi->name;
- }
-
- sprintf(sp->read_buffer, "RMD %s\r\n", leaf);
- if (control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
- if (response(sp, 0, &info, reply) == NO_ERROR) {
- perm = substr(info, "perm");
- no_such = substr(info, "no such");
- if (info) deallocate(info, V_cstr);
-
- switch (reply[0]) {
- case '2':
- return 0; /* success */
- case '4':
- /* temp failure ... */
- /* most likely reason */
- return ERROR_OBJECT_IN_USE;
- default:
- if (numeric_reply(reply) == 502) return ERROR_ACTION_NOT_KNOWN;
- if (perm) {
- return ERROR_DELETE_PROTECTED;
- } else if (no_such) {
- return ERROR_OBJECT_NOT_FOUND;
- } else {
- return ERROR_DIRECTORY_NOT_EMPTY;
- }
- }
- } else {
- disconnect(sp);
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- disconnect(sp);
- error = ERROR_OBJECT_NOT_FOUND;
- }
-
- return error;
- }
-
- b32 make_directory(site *sp, b8 *s)
- /*
- * make directory with name in s
- * Inputs:
- * sp : site pointer
- * s : full path name
- *
- * Returns:
- * standard file system errors
- */
- {
- b8 *leaf;
- b32 error;
- boolean exists;
- b8 *info, reply[3];
-
- verify(sp, V_site);
- truth(s != nil);
-
- if (s[0] == 0) {
- /* trying to mkd root */
- return ERROR_OBJECT_WRONG_TYPE;
- }
-
- leaf = cd_parent(sp, s);
- if (!leaf) {
- /* again, being lazy here */
- return ERROR_DIR_NOT_FOUND;
- }
-
- state_change(sp, SS_MAKEDIR);
-
- sprintf(sp->read_buffer, "MKD %s\r\n", leaf);
- if (control_write(sp, sp->read_buffer, 0) == NO_ERROR) {
- if (response(sp, 0, &info, reply) == NO_ERROR) {
- exists = substr(info, "exist");
-
- if (info) deallocate(info, V_cstr);
-
- switch (reply[0]) {
- case '2':
- return 0; /* success */
- case '4':
- /* temp failure ... */
- /* most likely reason */
- return ERROR_OBJECT_IN_USE;
- default:
- if (numeric_reply(reply) == 502) return ERROR_ACTION_NOT_KNOWN;
- if (exists)
- return ERROR_OBJECT_EXISTS;
- else
- return ERROR_WRITE_PROTECTED;
- }
- } else {
- disconnect(sp);
- error = ERROR_OBJECT_NOT_FOUND;
- }
- } else {
- disconnect(sp);
- error = ERROR_OBJECT_NOT_FOUND;
- }
-
- return error;
- }
-
- b32 rename_object(site *sp, b8 *from, b8 *to)
- /*
- * renames file 'from' to 'to'
- * Inputs:
- * sp : site pointer
- * from, to: null terminated file names
- *
- * Returns:
- * file system error, or 0 indicating success
- */
- {
- b8 *leaf1, *leaf2;
- b8 *info, reply[3];
- boolean perm, exist;
- ftpinfo *fi;
-
- if (sp->unix_paths) {
- if (!change_dir(sp, "")) {
- return ERROR_DIR_NOT_FOUND;
- }
-
- leaf1 = from;
- leaf2 = to;
- } else {
- leaf1 = cd_parent(sp, from);
- if (!leaf1) {
- return ERROR_DIR_NOT_FOUND;
- }
-
- leaf2 = to + strlen(to) - 1;
- while (leaf2 > to && *leaf2 != '/') leaf2--;
-
- if (leaf2 > to) *leaf2 = 0;
-
- if (strcmp(to, sp->cwd) == 0) { /* they are in the same directory, we can do it */
- if (leaf2 > to) *leaf2 = '/';
- } else {
- return ERROR_ACTION_NOT_KNOWN;
- }
- }
-
- state_change(sp, SS_RENAMING);
-
- sprintf(sp->read_buffer, "RNFR %s\r\n", leaf1);
- if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
- disconnect(sp);
- return ERROR_OBJECT_NOT_FOUND;
- }
-
- if (sp->quick) {
- sprintf(sp->read_buffer, "RNTO %s\r\n", leaf2);
- if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
- disconnect(sp);
- return ERROR_OBJECT_NOT_FOUND;
- }
- }
-
- /* response to RNFR */
- if (response(sp, 0, &info, reply) != NO_ERROR) {
- disconnect(sp);
- return ERROR_OBJECT_NOT_FOUND;
- }
-
- perm = substr(info, "perm");
-
- if (info) deallocate(info, V_cstr);
-
- if (reply[0] != '3') {
- if (perm) {
- return ERROR_WRITE_PROTECTED;
- }
- return ERROR_OBJECT_NOT_FOUND;
- }
-
- if (!sp->quick) {
- sprintf(sp->read_buffer, "RNTO %s\r\n", leaf2);
- if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
- disconnect(sp);
- return ERROR_OBJECT_NOT_FOUND;
- }
- }
-
- /* response to RNTO */
- if (response(sp, 0, &info, reply) != NO_ERROR) {
- disconnect(sp);
- return ERROR_OBJECT_NOT_FOUND;
- }
-
- perm = substr(info, "perm");
- exist = substr(info, "exist");
-
- if (info) deallocate(info, V_cstr);
-
- if (reply[0] != '2') {
- if (perm) {
- return ERROR_WRITE_PROTECTED;
- }
- if (exist) {
- return ERROR_OBJECT_EXISTS;
- }
- return ERROR_INVALID_COMPONENT_NAME;
- }
-
- return 0;
- }
-
- boolean change_dir(site *sp, b8 *new_dir)
- /*
- * change directory to new_dir
- * Inputs:
- * sp : site pointer
- * new_dir : null terminated path name
- *
- * Returns:
- * true if change_dir was successful
- */
- {
- tcpmessage *tm;
- struct MsgPort *sync;
- b8 *info, reply[4];
- b8 *z, *s;
-
- verify(sp, V_site);
- truth(new_dir != nil);
-
- tm = sp->control;
- sync = sp->sync;
-
- verify(tm, V_tcpmessage);
-
- /* check to see if we are already there */
-
- if (!sp->cwd && new_dir[0] == 0) return true;
- if (sp->cwd && strcmp(sp->cwd, new_dir) == 0) return true;
-
- /* have to explicitly change there */
-
- if (sp->cfile) { /* can't do _anything_ while we have a file opened */
- if (sp->file_list->closed) {
- close_file(sp, true);
- } else {
- return false;
- }
- }
-
- if (!sp->connected) {
- return false;
- }
-
- state_change(sp, SS_CWD);
-
- if (sp->cwd) {
- deallocate(sp->cwd, V_cstr);
- sp->cwd = nil;
- }
-
- /* first we change to the root */
-
- if (sp->root) {
- sprintf(sp->read_buffer, "CWD %s\r\n", sp->root);
- } else {
- strcpy(sp->read_buffer, "CWD\r\n");
- }
-
- if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
- show_string("change dir failed 1");
-
- disconnect(sp);
-
- return false;
- }
-
- /* this CWD is vital */
-
- if (response(sp, 0, &info, reply) != NO_ERROR) {
- show_string("change dir failed 2");
-
- disconnect(sp);
-
- return false;
- }
-
- if (info) deallocate(info, V_cstr);
-
- if (reply[0] != '2') {
- show_string("change dir failed 3");
-
- disconnect(sp);
-
- return false;
- }
-
- /*
- * ok, we are at (should be at :) the root (or at least what
- * we consider to be the root) of the FS
- */
-
- if (new_dir[0] == 0) {
- /* they wanted to change to the root ... so nothing further need be done */
- return true;
- }
-
- if (sp->unix_paths) {
- sprintf(sp->read_buffer, "CWD %s\r\n", new_dir);
-
- if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
- show_string("change dir failed 4");
-
- disconnect(sp);
-
- return false;
- }
-
- if (response(sp, 0, &info, reply) != NO_ERROR) {
- show_string("change dir failed 5");
-
- disconnect(sp);
-
- return false;
- }
-
- if (info) deallocate(info, V_cstr);
-
- if (reply[0] == '2') {
- sp->cwd = (b8 *)allocate(strlen(new_dir) + 1, V_cstr);
- if (sp->cwd) {
- strcpy(sp->cwd, new_dir);
- return true;
- }
- goto fail_to_root;
- }
-
- /* ok, our clumped cwd didn't work, lets try it the slow way */
- }
-
- s = z = new_dir;
- while (*z) {
- while (*s && *s != '/') s++;
-
- if (*s == '/') {
- *s = 0;
- sprintf(sp->read_buffer, "CWD %s\r\n", z);
- *s++ = '/';
- } else {
- sprintf(sp->read_buffer, "CWD %s\r\n", z);
- }
-
- if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
- show_string("change dir failed 6");
-
- disconnect(sp);
-
- return false;
- }
-
- if (response(sp, 0, &info, reply) != NO_ERROR) {
- show_string("change dir failed 7");
-
- disconnect(sp);
-
- return false;
- }
-
- if (info) deallocate(info, V_cstr);
-
- if (reply[0] != '2') {
- goto fail_to_root;
- }
-
- z = s;
- }
-
- /* we've succeeded where unix_paths failed, so ... */
-
- sp->unix_paths = false;
-
- sp->cwd = (b8 *)allocate(strlen(new_dir) + 1, V_cstr);
- if (sp->cwd) {
- strcpy(sp->cwd, new_dir);
- return true;
- }
-
- fail_to_root:
- /* something went wrong ... who knows where we are? ... go back to the root */
-
- if (sp->root) {
- sprintf(sp->read_buffer, "CWD %s\r\n", sp->root);
- } else {
- strcpy(sp->read_buffer, "CWD\r\n");
- }
-
- if (control_write(sp, sp->read_buffer, 0) != NO_ERROR) {
- show_string("change dir failed 8");
-
- disconnect(sp);
-
- return false;
- }
-
- if (response(sp, 0, &info, reply) != NO_ERROR) {
- show_string("change dir failed 9");
-
- disconnect(sp);
-
- return false;
- }
-
- if (info) deallocate(info, V_cstr);
-
- if (reply[0] == '2') {
- return false;
- }
-
- show_string("change dir failed 10");
-
- disconnect(sp);
-
- return false;
- }
-
- b8 *cd_parent(site *sp, b8 *path)
- /*
- * change to the parent dir of the object described by path
- * Inputs:
- * sp : site pointer
- * path : string describing object
- *
- * Returns:
- * pointer to leaf name (last component of path)
- * or nil ... generally indicates gross error or dir not found
- */
- {
- b8 *leaf;
- boolean cd;
-
- verify(sp, V_site);
- truth(path != nil);
-
- /* start at end of pathname and work back til we find a / */
-
- leaf = path + strlen(path) - 1;
- while (leaf > path && *leaf != '/') leaf--;
-
- if (leaf == path) {
- /* no /, so we are talking about an object in the root dir */
-
- cd = change_dir(sp, "");
- } else {
- /* temporarily knock out / to get parent path */
-
- *leaf = 0;
- cd = change_dir(sp, path);
-
- /* then restore the / and move over it */
- *leaf++ = '/';
- }
-
- if (cd) return leaf;
- else return nil;
- }
-
- #define LAST_FIELDS 5
-
- void add_info(struct info_header *ih, b8 *s)
- /*
- * parses s and adds the information to header ih
- * Inputs:
- * ih : info_header
- * s : line returned from LIST
- */
- {
- b32 perm; /* permission bits */
- b32 size;
- b8 *fields[LAST_FIELDS], *z; /* want the last 5 fields */
- b8 tempd[15], tempt[10];
- int i, num_fields;
- struct DateTime dtime;
-
- if (s[0] <= ' ') return;
-
- for (i = 0; i < LAST_FIELDS; i++) fields[i] = s; /* safety */
-
- perm = 0;
- if (*s == 'd') perm |= MYFLAG_DIR;
- if (*s == 'l') { /* throw away yucky unix soft links */
- perm |= MYFLAG_DIR; /* assume its a directory ... it _may_ be a file ... */
- z = s + strlen(s) - 1;
- while (z > s && !(z[0] == '-' && z[1] == '>')) z--;
- if (z > s) *z = 0;
- }
- s++;
- if (*s < 'A') perm |= FIBF_READ;
- s++;
- if (*s < 'A') perm |= FIBF_WRITE | FIBF_DELETE;
- s++;
- if (*s < 'A') perm |= FIBF_EXECUTE;
- s++;
-
- while (*s > ' ') s++;
-
- num_fields = 1;
-
- do {
- while (*s > 0 && *s <= ' ') s++;
-
- if (!*s) break;
-
- for (i = 0; i < LAST_FIELDS - 1; i++) {
- fields[i] = fields[i+1];
- }
- num_fields++;
- fields[LAST_FIELDS - 1] = s;
- while (*s > ' ') s++;
- } while (*s);
-
- /* ok, we now have the last 5 fields, so process them */
-
- size = atoi(fields[0]);
-
- s = fields[4];
- while (*s > ' ') s++;
- *s = 0;
-
- if (num_fields > 4) {
-
- /* throw away . & .. */
- if (fields[4][0] == '.') {
- if (fields[4][1] == '\0') return;
- if (fields[4][1] == '.' && fields[4][2] == '\0') return;
- }
-
- dtime.dat_Format = FORMAT_INT;
- dtime.dat_Flags = 0;
- dtime.dat_StrDay = nil;
- dtime.dat_StrDate = tempd;
- dtime.dat_StrTime = tempt;
-
- s = fields[3];
-
- if (s[1] == ':' || s[2] == ':') {
- tempd[0] = (year / 10) % 10 + '0';
- tempd[1] = year % 10 + '0';
- tempd[2] = '-';
- tempd[3] = fields[1][0];
- tempd[4] = fields[1][1];
- tempd[5] = fields[1][2];
- tempd[6] = '-';
- tempd[7] = fields[2][0];
- tempd[8] = fields[2][1];
- if (tempd[8] < '0') tempd[8] = 0;
- else tempd[9] = 0;
-
- while (*s > ' ') s++;
- *s = 0;
-
- strcpy(tempt, fields[3]);
- strcat(tempt, ":00");
- } else {
- tempd[0] = fields[3][2];
- tempd[1] = fields[3][3];
- tempd[2] = '-';
- tempd[3] = fields[1][0];
- tempd[4] = fields[1][1];
- tempd[5] = fields[1][2];
- tempd[6] = '-';
- tempd[7] = fields[2][0];
- tempd[8] = fields[2][1];
-
- if (tempd[8] < '0') tempd[8] = 0;
- else tempd[9] = 0;
-
- strcpy(tempt, "12:00:00");
- }
-
- StrToDate(&dtime);
-
- add_ftpinfo(ih, fields[4], dtime.dat_Stamp, size, (size + 1023) / 1024, perm);
- }
- }
-
- boolean get_list(site *sp, struct info_header *ih)
- /*
- * gets LIST in cwd and puts it in ih
- * Inputs:
- * sp : site pointer
- * ih : info_header to hold list information
- *
- * Returns:
- * true if LIST was successful
- */
- {
- tcpmessage *tm, *listm;
- struct MsgPort *sync;
- b8 reply[3], *info;
- b16 portn;
- b32 signals, asigs, rsigs;
-
- verify(sp, V_site);
- verify(ih, V_info_header);
-
- truth(sp->connected);
- truth(sp->cfile == nil);
-
- state_change(sp, SS_LISTING);
-
- tm = sp->control;
- verify(tm, V_tcpmessage);
-
- sync = sp->sync;
-
- asigs = sp->disconnect_signals | sp->abort_signals;
- signals = (1 << sync->mp_SigBit) | asigs;
-
- listm = new_message(sp);
- if (!listm) return false;
-
- if (control_write(sp, "PASV\r\n", 0) == NO_ERROR) {
- if (!sp->quick || control_write(sp, "LIST\r\n", 0) == NO_ERROR) {
- if (response(sp, 0, &info, reply) == NO_ERROR) {
- if (reply[0] == '2' && info) {
- if (passive_response(info, sp->read_buffer, &portn)) {
-
- deallocate(info, V_cstr);
-
- if (make_connection(sp, listm, sp->read_buffer, portn, 0) == NO_ERROR) {
- if (sp->quick || control_write(sp, "LIST\r\n", 0) == NO_ERROR) {
- /* this next response will be to the LIST */
- if (response(sp, 0, &info, reply) == NO_ERROR) {
- if (info) deallocate(info, V_cstr);
-
- if (reply[0] == '1') {
- /* list should be coming through listm now */
-
- goto read_list;
- }
- }
- }
- break_connection(sp, listm);
- }
- } else {
- if (info) deallocate(info, V_cstr);
- }
- } else {
- if (info) deallocate(info, V_cstr);
- }
- }
- }
- }
-
- listm->command = TCP_DISPOSE;
- PutMsg(tcp, &listm->header);
-
- disconnect(sp);
-
- return false;
-
- read_list:
- listm->command = TCP_READ;
- listm->data = sp->read_buffer;
- listm->flags = FLAG_READLINE;
- listm->length = READ_BUFFER_LENGTH;
- listm->header.mn_ReplyPort = sync;
-
- do {
- PutMsg(tcp, &listm->header);
- rsigs = Wait(signals);
- if (rsigs & asigs) {
- state_change(sp, SS_ABORTING);
-
- interrupt_message(sp, listm);
-
- if (rsigs & sp->disconnect_signals) {
- break_connection(sp, listm);
-
- listm->command = TCP_DISPOSE;
- PutMsg(tcp, &listm->header);
-
- disconnect(sp);
- return false;
- }
- } else {
- GetMsg(sync);
- }
-
- if (listm->result > 0) {
- sp->read_buffer[listm->result] = 0;
- add_info(ih, sp->read_buffer);
- }
- } while (listm->error == NO_ERROR);
-
- break_connection(sp, listm);
-
- listm->command = TCP_DISPOSE;
- PutMsg(tcp, &listm->header);
-
- if (response(sp, 0, &info, reply) != NO_ERROR) {
- show_string("get list failed 8");
-
- disconnect(sp);
-
- return true;
- }
-
- if (info) deallocate(info, V_cstr);
-
- #ifdef VERIFY
- if (reply[0] != '2') {
- show_string("received non-2 for end of LIST");
- }
- #endif
- return true;
- }
-
- boolean prelim(site *sp, struct Window *w)
- /*
- * once logged in, does preliminary setup stuff ... for now
- * sets TYPE I and figures out where the root of the fs is
- * Inputs:
- * sp : site pointer
- * w : the connection cancel window
- *
- * Returns:
- * true if setup was successful
- */
- {
- b32 csig;
- b8 *info, reply[3];
- b8 *s, *z;
-
- csig = (1 << w->UserPort->mp_SigBit);
-
- if (control_write(sp, "TYPE I\r\n", csig) == NO_ERROR) {
- /* we either need to change to root, or work out where root is */
- if (sp->root) {
- sprintf(sp->read_buffer, "CWD %s\r\n", sp->root);
- } else {
- strcpy(sp->read_buffer, "PWD\r\n");
- }
- if (!sp->quick || control_write(sp, sp->read_buffer, csig) == NO_ERROR) {
- /* first response is to TYPE I */
- if (response(sp, csig, &info, reply) == NO_ERROR) {
- /* we don't really care what they replied */
- if (info) deallocate(info, V_cstr);
- if (sp->root) {
- sprintf(sp->read_buffer, "CWD %s\r\n", sp->root);
- } else {
- strcpy(sp->read_buffer, "PWD\r\n");
- }
- if (sp->quick || control_write(sp, sp->read_buffer, csig) == NO_ERROR) {
- /* ... next response is to CWD/PWD */
- if (response(sp, csig, &info, reply) == NO_ERROR) {
- if (reply[0] == '2') {
- if (sp->root) {
- /* was the CWD ... was successful */
- if (info) deallocate(info, V_cstr);
-
- return true;
- } else if (info) {
- /* was the PWD ... have to extract the root path */
- s = info;
- while (*s && *s != '"') s++;
- if (*s) {
- s++;
- z = s;
- while (*z && *z != '"') z++;
- if (*z) {
- sp->root = (b8 *)allocate(z - s + 1, V_cstr);
- if (sp->root) {
- if (z != s)
- memcpy(sp->root, s, z - s);
-
- sp->root[z - s] = 0;
-
- deallocate(info, V_cstr);
- return true;
- } else inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_OOM_ROOT], nil, 0);
- } else inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_PWD_GARBAGE], nil, 0);
- } else inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_PWD_GARBAGE], nil, 0);
- deallocate(info, V_cstr);
- } else {
- inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_FAILED_PWD], nil, 0);
- }
- } else {
- ok(sp->IBase, strings[MSG_OPERATIONAL_ERROR], info);
- if (info) deallocate(info, V_cstr);
- }
- } else inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_READING_PWD], nil, 0);
- } else inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_REQUESTING_PWD], nil, 0);
- } else inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_READING_TYPE], nil, 0);
- } else inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_REQUESTING_PWD], nil, 0);
- } else inform(sp->IBase, strings[MSG_OPERATIONAL_ERROR], strings[MSG_ERROR_SETTING_TYPE], nil, 0);
-
- return false;
- }
-
- void login(site *sp, struct Window *w)
- /*
- * goes through the login sequence once a successful connection has been established
- * Inputs:
- * sp : site pointer
- * w : the connection cancel window
- */
- {
- tcpmessage *tm;
- struct MsgPort *sync;
- b8 reply[4], *info;
- b32 csig;
- boolean early_success = false;
-
- tm = sp->control;
- sync = sp->sync;
-
- state_change(sp, SS_LOGIN);
-
- csig = 1 << w->UserPort->mp_SigBit;
-
- retry_login:
- if (sp->needs_user || sp->needs_password) {
- if (!user_pass_request(sp, w)) {
- tm->command = TCP_CLOSE;
- PutMsg(tcp, &tm->header);
- WaitPort(sync); GetMsg(sync);
-
- close_req(sp, w);
-
- state_change(sp, SS_DISCONNECTED);
- return;
- }
- }
-
- if (sp->user) {
- sprintf(sp->read_buffer, "USER %s\r\n", sp->user);
- } else {
- strcpy(sp->read_buffer, "USER ftp\r\n");
- }
-
- if (control_write(sp, sp->read_buffer, csig) == NO_ERROR) {
- if (sp->password) {
- sprintf(sp->read_buffer, "PASS %s\r\n", sp->password);
- } else {
- sprintf(sp->read_buffer, "PASS %s\r\n", anon_login);
- }
- if (control_write(sp, sp->read_buffer, csig) == NO_ERROR) {
- /* first response should be to the USER */
-
- switch (response(sp, csig, &info, reply)) {
- case NO_ERROR:
- switch (reply[0]) {
- case '2':
- early_success = true;
- /* the welcome banner will come here I guess */
- if (!sp->read_banners) {
- ok(sp->IBase, strings[MSG_LOGIN_SUCCEEDED_NO_PASS], info);
- sp->read_banners = true;
- }
- /* fall through */
- case '3':
- /* ignore the banner here ... usually its just "Anonymous login ok, send ident ..." */
- if (info) deallocate(info, V_cstr);
-
- /* now read pass response */
- switch (response(sp, csig, &info, reply)) {
- case NO_ERROR:
- /* if we succeeded early, we don't care what they tell us */
- if (!early_success) {
- if (reply[0] == '2') {
- if (!sp->read_banners) {
- ok(sp->IBase, strings[MSG_LOGIN_SUCCEEDED], info);
- sp->read_banners = true;
- }
- } else if (reply[0] == '3') {
- /* they want an ACCT ... fuck 'em */
-
- inform(sp->IBase, strings[MSG_LOGIN_FAILED], strings[MSG_ACCT_REQUESTED], nil, 0);
- if (info) deallocate(info, V_cstr);
- break;
- } else {
- if (reply[0] == '5' && reply[1] == '3' && reply[2] == '0') {
- /* this is login incorrect */
- if (retry_cancel(sp->IBase, strings[MSG_LOGIN_INCORRECT], info)) {
- if (info) deallocate(info, V_cstr);
- sp->needs_password = true;
- if (sp->password) deallocate(sp->password, V_cstr);
- sp->password = nil;
- goto retry_login;
- }
- if (info) deallocate(info, V_cstr);
- break;
- }
-
- ok(sp->IBase, strings[MSG_LOGIN_FAILED_PASS], info);
- if (info) deallocate(info, V_cstr);
- break;
- }
- }
-
- if (info) deallocate(info, V_cstr);
-
- if (prelim(sp, w)) {
- close_req(sp, w);
- sp->connected = true;
-
- state_change(sp, SS_IDLE);
- return;
- }
-
- break;
- case ERROR_INTERRUPTED:
- break;
- case ERROR_LOST_CONNECTION:
- case ERROR_EOF:
- case ERROR_UNREACHABLE:
- inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_LOST_CONN_DURING_LOGIN_PASS], nil, 0);
- break;
- case ERROR_GARBAGE_RECEIVED:
- inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_GARBAGE_RECEIVED_PASS], nil, 0);
- break;
- default:
- inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_ERROR_RESPONSE_PASS], nil, 0);
- break;
- }
- break;
- case '4':
- if (retry_cancel(sp->IBase, strings[MSG_TEMP_LOGIN_FAILURE_USER], info)) {
- if (info) deallocate(info, V_cstr);
- goto retry_login;
- }
- if (info) deallocate(info, V_cstr);
- break;
- default:
- ok(sp->IBase, strings[MSG_LOGIN_FAILED_USER], info);
- if (info) deallocate(info, V_cstr);
- break;
- }
- break;
- case ERROR_INTERRUPTED:
- break;
- case ERROR_LOST_CONNECTION:
- case ERROR_EOF:
- case ERROR_UNREACHABLE:
- inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_LOST_CONN_DURING_LOGIN], nil, 0);
- break;
- case ERROR_GARBAGE_RECEIVED:
- inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_GARBAGE_RECEIVED_USER], nil, 0);
- break;
- default:
- inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_ERROR_USER_RESPONSE], nil, 0);
- break;
- }
- } else inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_ERROR_WRITING_PASS], nil, 0);
- } else inform(sp->IBase, strings[MSG_LOGIN_ERROR], strings[MSG_ERROR_WRITING_USER], nil, 0);
-
- tm->command = TCP_CLOSE;
- PutMsg(tcp, &tm->header);
- WaitPort(sync); GetMsg(sync);
-
- close_req(sp, w);
-
- state_change(sp, SS_DISCONNECTED);
-
- return;
- }
-
- void init_connect(site *sp)
- {
- struct Window *w;
- b8 *z;
- tcpmessage *tm, *intr;
- struct MsgPort *sync;
- b8 reply[3], *info;
- b32 signals, csig;
-
- verify(sp, V_site);
-
- z = sp->host;
-
- while (sp->infos) free_info_header(sp->infos);
-
- w = connect_req(sp, z);
- if (!w) {
- show_string("connect req failed");
- return;
- }
-
- state_change(sp, SS_CONNECTING);
-
- tm = sp->control;
- sync = sp->sync;
- intr = sp->intr;
-
- csig = (1 << w->UserPort->mp_SigBit) | sp->abort_signals | sp->disconnect_signals;
- signals = (1 << sync->mp_SigBit) | csig;
-
- if (ftp_port_number == 0) {
- tm->command = TCP_SERVICE;
- tm->data = strings[MSG_SERVICE];
- tm->header.mn_ReplyPort = sync;
-
- PutMsg(tcp, &tm->header);
- WaitPort(sync); GetMsg(sync);
-
- if (tm->result) {
- ftp_port_number = tm->port.w;
- } else if (tm->error == ERROR_NO_CONNECTION) {
- close_req(sp, w);
-
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_AMITCP_NOT_RUNNING], nil, 0);
-
- state_change(sp, SS_DISCONNECTED);
-
- return;
- } else {
- ftp_port_number = 21;
- }
- }
-
- tm->command = TCP_CONNECT;
- tm->header.mn_ReplyPort = sync;
- tm->data = z;
- tm->port.w = ftp_port_number;
-
- PutMsg(tcp, &tm->header);
-
- do {
- if (Wait(signals) & csig) {
- intr->interrupt = tm;
- PutMsg(tcp, &intr->header);
- WaitPort(sync); GetMsg(sync);
- WaitPort(sync); GetMsg(sync);
-
- if (tm->result) { /* it succeeded in connecting */
- tm->command = TCP_CLOSE;
- PutMsg(tcp, &tm->header);
- WaitPort(sync); GetMsg(sync);
- }
-
- close_req(sp, w);
-
- state_change(sp, SS_DISCONNECTED);
-
- return;
- }
- } while (!GetMsg(sync));
-
- if (!tm->result) { /* the connect failed ... tell the user why */
- close_req(sp, w);
-
- switch (tm->error) {
- case ERROR_NO_CONNECTION:
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_AMITCP_NOT_RUNNING], nil, 0);
- break;
- case ERROR_UNKNOWN_HOST:
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_HOST_UNKNOWN], z, 0);
- break;
- case ERROR_UNREACHABLE:
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_HOST_UNREACHABLE], z, 0);
- break;
- case ERROR_CONNECT_REFUSED:
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_FTP_REFUSED], z, 0);
- break;
- default:
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_CANT_CONNECT], z, tm->error);
- break;
- }
-
- state_change(sp, SS_DISCONNECTED);
-
- return;
- }
-
- /* ok, we've connected ... look at the greeting */
-
- retry_intro:
- switch (response(sp, csig, &info, reply)) {
- case NO_ERROR:
- break;
- case ERROR_INTERRUPTED:
- close_req(sp, w);
- goto close_and_exit;
- case ERROR_LOST_CONNECTION:
- case ERROR_EOF:
- case ERROR_UNREACHABLE:
- close_req(sp, w);
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_LOST_CONN_DURING_INTRO], nil, 0);
- goto close_and_exit;
- case ERROR_GARBAGE_RECEIVED:
- close_req(sp, w);
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_GARBAGE_DURING_INTRO], z, 0);
- goto close_and_exit;
- default:
- close_req(sp, w);
- inform(sp->IBase, strings[MSG_CONNECT_ERROR], strings[MSG_ERROR_DURING_INTRO], nil, 0);
- goto close_and_exit;
- }
-
- switch (reply[0]) {
- case '1':
- if (retry_cancel(sp->IBase, strings[MSG_CONN_DELAY], info)) {
- goto retry_intro;
- }
- close_req(sp, w);
- if (info)
- deallocate(info, V_cstr);
-
- goto close_and_exit;
- case '2':
- case '3':
- /* This banner appears to be generally pretty dull, but if
- * you really want to see it then remove the comments ...
- * if (!sp->read_banners) {
- * ok(sp->IBase, "Connected", info);
- * }
- */
-
- if (info)
- deallocate(info, V_cstr);
-
- login(sp, w);
- return;
- case '4':
- if (retry_cancel(sp->IBase, strings[MSG_TEMP_CONN_FAILURE], info)) {
- goto retry_intro;
- }
- close_req(sp, w);
- if (info)
- deallocate(info, V_cstr);
-
- goto close_and_exit;
- case '5':
- default:
- close_req(sp, w);
-
- ok(sp->IBase, strings[MSG_CONN_FAILED], info);
-
- if (info)
- deallocate(info, V_cstr);
-
- break;
- }
-
- close_and_exit:
- tm->command = TCP_CLOSE;
- PutMsg(tcp, &tm->header);
- WaitPort(sync); GetMsg(sync);
-
- state_change(sp, SS_DISCONNECTED);
-
- return;
- }
-
-
-