home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-01-26 | 27.1 KB | 1,005 lines |
- #import <FTPManager.h>
- #import <ClockView.h>
- #import <PercentView.h>
- #import <ProsperoVLINK.h>
-
- #import <objc/NXStringTable.h>
- #import <appkit/Application.h>
- #import <appkit/Matrix.h>
- #import <appkit/OpenPanel.h>
- #import <appkit/appkit.h>
- #import <appkit/nextstd.h>
- #import <ctype.h>
- #import <fcntl.h>
- #import <regex.h>
- #import <signal.h>
- #import <sys/param.h>
- #import <sys/types.h>
- #import <sys/stat.h>
-
- #define DEFAULT_DIR "."
- #define NIB_PATH "FTPManager.nib"
- /* The owner of the application defaults */
- extern const char *NeXTArchieOwner;
-
- /* Code returned when modal loop is aborted */
- #define ABORT_TRANSFER 1
-
- /* Number of calls to the dirListingTE before we say quit */
- #define LISTING_TIMEOUT_COUNT 6 // = 30 seconds
-
- /* Import the error message NXStringTable keys */
- #import "errMessages.keys"
-
- /* Ftp command result codes */
- #define CONNECTED 220
- #define LOGIN_OK 331
- #define PASSWORD_OK 230
- #define CWD_OK 250
- #define CWD_OK2 200
- #define TRANSFER_COMPLETE 226
- #define GOODBYE 221
- #define DATA_CONNECTION 150
- #define PORT_OK 200
- /* Ftp error codes I know of, most from looking at ftpd.c source */
- #define FTP_ERR0 500
- #define FTP_ERR1 502
- #define FTP_ERR2 503
- #define FTP_ERR3 530
- #define FTP_ERR4 550
- #define FTP_ERR5 551
- #define FTP_ERR6 553
- #define FTP_ERR7 421 // Some hosts send this if there are too many users
-
- /* ftpMode flag defines */
- #define BEGIN_SESSION 10
- #define CONNECT_SUCCESS 11
- #define USER_LOGIN 20
- #define LOGIN_SUCCESS 21
- #define USER_PASSWORD 30
- #define PASSWORD_SUCCESS 31
- #define REMOTE_CHDIR 40
- #define CHDIR_SUCCESS 41
- #define GET_TARGET_SIZE 50
- #define SIZE_SUCCESS 51
- #define FILE_TRANSFER 60
- #define TRANSFER_SUCCESS 61
- #define FILE_LISTING 62
- #define LISTING_SUCCESS 63
- #define DISCONNECT 70
- #define DISCONNECT_SUCCESS 71
- #define FTP_ERROR 100
- #define FTP_SELF_ERROR 110
-
- /* Debugging level defines */
- #define FTP_RESULT 4 // Write results from ftp program
- #define FTP_COMMAND 8 // Write command sent to ftp program
- #define LOW 10
- #define MED 20
- #define HIGH 30
- #define MEGA 40
-
- /* Ftp output buffers and variables */
- static char *ftpFullErrMessage;
- static char ftpErrMessage[128];
- static char *listingBuffer;
- static int listingSize,listingBufferSize;
-
- /* The code number of the last ftp output result */
- static int lastFtpResult;
- /* Regular expression structs for patterns of interest */
- static struct regex *ftpResultExpr;
- static struct regex *unreachableNetworkExpr;
- static struct regex *transferSizeExpr;
- static struct regex *unkownHostExpr;
- static struct regex *transferCompleteExpr;
-
- /* Timed entry prototypes */
- void FtpDaemon(DPSTimedEntry te, double timeNow, FTPManager *self);
- void ListingDaemon(DPSTimedEntry te, double timeNow, FTPManager *self);
-
- /* Import routines which parse the ftp dir command output into DirEntry's. */
- #import "ftpParse.c"
-
- @implementation FTPManager
-
- - init
- {
- [super init];
- getwd(ftpDirectory);
- return self;
- }
-
- - abortTransfer: sender
- {
- kill(ftpProcessPID,SIGKILL);
- [NXApp stopModal : ABORT_TRANSFER];
-
- return self;
- }
-
- - displayErrOutput: sender
- {
- id textField;
- /* Display the error panel and display the last ftp output in
- its textfield */
- textField = [ftpErrViewID docView];
- [textField setText: ftpFullErrMessage];
- [ftpErrPanelID makeKeyAndOrderFront: self];
- return self;
- }
-
- - setLocalDir: sender
- {
- [self setLocalPath: ftpDirectory file: NULL panel: YES];
- return self;
- }
-
- - setLocalPath: (const char *) dirName file: (const char *) fileName panel: (BOOL) display;
- {
- int saveResult,fileLength;
-
- if(display == YES)
- {
- if(fileName == NULL)
- { // Set local directory
- if(pwdPanelID == nil)
- {
- pwdPanelID = [OpenPanel new];
- if(pwdPanelID == nil)
- return [self error: SORRY key: OPEN_PANEL_FAILED];
- }
- [pwdPanelID setTitle: "Set Local Directory"];
- saveResult = [pwdPanelID runModalForDirectory:ftpDirectory file:NULL];
- if(saveResult == 1)
- {
- strcpy(ftpDirectory,[pwdPanelID directory]);
- return self;
- }
- return nil;
- }
- else
- { // Prompt the user for the filename
- if(saveAsPanelID == nil)
- {
- saveAsPanelID = [SavePanel new];
- if(saveAsPanelID == nil)
- return [self error: SORRY key: SAVE_PANEL_FAILED];
- }
- saveResult = [saveAsPanelID runModalForDirectory: (const char *) ftpDirectory
- file: fileName];
- if(saveResult == 1)
- {
- strcpy(ftpDirectory,[saveAsPanelID directory]);
- fileName = strrchr([saveAsPanelID filename],'/');
- fileName ++;
- }
- else
- return nil;
- }
- }
-
- /* Copy the local file name to fileName */
- fileLength = strlen(ftpDirectory) + strlen(fileName) + 1;
- if(localFilePath)
- NX_FREE(localFilePath);
- NX_MALLOC(localFilePath,char, fileLength);
- if(localFilePath == NULL)
- return [self error: ALERT key: LOCALPATH_ALLOC_FAILED];
-
- sprintf(localFilePath,"%s/%s",ftpDirectory,fileName);
-
- return self;
- }
-
- /* Write a command line to the ftp program */
- - writeFtp : sender
- {
- [self writeFtpCommand: "%s", [sender stringValue]];
- return self;
- }
- - writeFtpCommand : (char *) formatString, ...
- {
- va_list ap;
-
- va_start(ap, formatString);
- vfprintf(stdout, formatString,ap);
- if(ftpDebugLevel > FTP_COMMAND)
- vfprintf(errFile,formatString,ap);
- va_end(ap);
-
- return self;
- }
-
- /* Cover methods for the full blown runModal::::: */
- - runModal: (const char *) hostName get: (const char *) targetPathName
- {
- [self runModal: hostName get: targetPathName anon: YES
- write: NO binary: YES vlink: nil];
- return self;
- }
- - runModal: (const char *) hostName get: (const char *) targetPathName dir: (BOOL) listing
- {
- [self runModal: hostName get: targetPathName anon: YES
- write: NO binary: YES vlink: nil];
- return self;
- }
- - runModal: (const char *) hostName get: (const char *) targetPathName anon: (BOOL) anonymous
- {
- [self runModal: hostName get: targetPathName anon: anonymous
- write: NO binary: YES vlink: nil];
- return self;
- }
-
- - runModal: (const char *) hostName get: (const char *) targetPathName anon: (BOOL) anonymous
- write: (BOOL) overWrite binary: (BOOL) mode vlink: dirVLINK
- {
- /* pty channels as returned from Stevens pty code */
- int masterChannel,slaveChannel;
- int pty_master(), pty_slave();
- int currentFlags,lastSize, modalStatus = 0;
- float currentSize,percent;
- NXModalSession theSession;
- struct stat transferStatus;
- char localHostname[64],*localUsername;
- char *remoteDirectory,*remoteFilename,*tmpBuffer,sizeBuffer[16];
-
- /* Ignore the anonymous flag for now */
-
- /* Load my nib panel */
- if(ftpPanelID == nil)
- {
- if([NXApp loadNibSection: NIB_PATH owner: self withNames: NO] == nil)
- [self error: ALERT key: FTP_PANEL_FAILED];
- }
-
- /* Set the errFile to stderr */
- errFile = stderr;
- setvbuf(errFile,NULL,_IOLBF,0);
- /* Read the FtpDebugLevel defaults setting */
- ftpDebugLevel = atoi(NXGetDefaultValue(NeXTArchieOwner, "FtpDebugLevel"));
-
- /* Write a message logging the start of the ftp session */
- [self debug: 0, "Starting ftp session: UNIX clock = %d\n",time( (long *) 0)];
-
- /* Open the master pty and set raw mode */
- masterChannel = pty_master();
- currentFlags = fcntl(masterChannel,F_GETFL,0);
- fcntl(masterChannel,F_SETFL,currentFlags | O_NDELAY);
- /* Fork the child process */
- if( (ftpProcessPID = vfork()) < 0)
- return [self error: ALERT key: FTP_FORK_FAILED];
-
- /* Reset the parents stdin and stdout to the pty channel */
- if(ftpProcessPID > 0)
- {
- close(0);
- close(1);
- dup(masterChannel); // Set this as stdin
- dup(masterChannel); // & to stdout
- /* Setup line oriented communication */
- setvbuf(stdin,NULL,_IOLBF,0);
- }
- else
- { /* The child process opens the slave pty */
- int controlTerm;
- slaveChannel = pty_slave(masterChannel);
- if(slaveChannel < 0)
- {
- fprintf(stderr,"Failed to open pty slave\n");
- _exit(1);
- }
- close(masterChannel);
- /* Get rid of the controlling terminal so that password prompts
- are redirected to the pty. */
- if( (controlTerm = open("/dev/tty", O_RDWR)) >= 0 )
- {
- ioctl(controlTerm,TIOCNOTTY,NULL);
- close(controlTerm);
- }
- [self debug: LOW, "Starting ftp...\n"];
-
- /* Set std everything to the slave pty */
- close(0);
- close(1);
- close(2);
- dup(slaveChannel); // Set this as stdin
- dup(slaveChannel); // & stdout
- dup(slaveChannel); // & stderr
- /* Start the ftp program */
- execlp("ftp","ftp", "-n", hostName,NULL);
- fprintf(stderr,"Failed to exec ftp\n");
- _exit(1);
- }
-
- /* Determine the remote and local file names */
- if(dirVLINK == nil)
- {
- remoteFilename = strrchr(targetPathName,'/'); remoteFilename ++;
- [self debug: LOW, "remoteFilename: %s\n", remoteFilename];
- [self setLocalPath: ftpDirectory file: remoteFilename panel: NO];
- [self debug: LOW, "localFilePath: %s\n", localFilePath];
-
- NX_MALLOC(tmpBuffer,char,strlen(targetPathName)+1);
- if(tmpBuffer == NULL)
- {
- kill(ftpProcessPID,SIGKILL);
- return [self error: ALERT key: REMOTE_ALLOC_FAILED];
- }
- strcpy(tmpBuffer, targetPathName);
- remoteDirectory = tmpBuffer;
- remoteDirectory[strlen(targetPathName) - strlen(remoteFilename)] = '\0';
- [self debug: LOW, "remoteDirectory: %s\n", remoteDirectory];
- }
- else
- {
- remoteDirectory = targetPathName;
- remoteFilename = NULL;
- localFilePath = NULL;
- tmpBuffer = NULL;
- }
-
- /* Add a timed entry for reading & displaying child's output */
- ftpMode = BEGIN_SESSION;
- ftpDaemonTE = DPSAddTimedEntry(1.0, FtpDaemon,self,
- NX_MODALRESPTHRESHOLD);
-
- /* Initialize some variables */
- transferComplete = NO;
- lastSize = 0;
- lastFtpResult = 0;
- listingTEStarted = NO;
- listingTECalls = 0;
- dirListingTE = (DPSTimedEntry) 0;
- ftpResultExpr = re_compile("[1-5][0-9][0-9] ",0);
- unreachableNetworkExpr = re_compile("Network.*unreachable",1);
- transferSizeExpr = re_compile("[0-9]* bytes",1);
- unkownHostExpr = re_compile("Unknown.*Host",1);
- transferCompleteExpr = re_compile("226.*Transfer",1);
-
- /* Here we go */
- [self debug: MEGA, "FTPM: Begging modal session\n"];
- [statusBoxID removeFromSuperview];
- [timerViewID startMinSecTimer: self];
- [hostnameFieldID setStringValue: hostName];
- [messageFieldID setStringValue: "Connecting to remote host..."];
- [ftpPanelID display];
- [NXApp beginModalSession: &theSession for: ftpPanelID];
- while(transferComplete == NO)
- {
- modalStatus = [NXApp runModalSession: &theSession];
- if(modalStatus != NX_RUNCONTINUES)
- break;
- switch(ftpMode)
- {
- case CONNECT_SUCCESS :
- ftpMode = USER_LOGIN;
- [self debug: MED, "FTPM: USER_LOGIN\n"];
- [messageFieldID setStringValue: "Connected to remote host..."];
- /* Initiate anonymous login */
- [self writeFtpCommand: "user anonymous\n"];
- break;
- case LOGIN_SUCCESS :
- ftpMode = USER_PASSWORD;
- [self debug: MED, "FTPM: USER_PASSWORD\n"];
- /* Send user@hostname as password */
- gethostname(localHostname,63);
- localHostname[63] = '\0';
- localUsername = (char *) NXUserName();
- [self writeFtpCommand: "%s@%s\n",localUsername,localHostname];
- break;
- case PASSWORD_SUCCESS :
- ftpMode = REMOTE_CHDIR;
- [self debug: MED, "FTPM: REMOTE_CHDIR\n"];
- [messageFieldID setStringValue: "Anonymous login successful..."];
- [self writeFtpCommand: "cd %s\n", remoteDirectory];
- if(mode == YES)
- [self writeFtpCommand: "binary\n"];
- else
- [self writeFtpCommand: "ascii\n"];
- break;
- case CHDIR_SUCCESS :
- if(dirVLINK == nil)
- {
- ftpMode = FILE_TRANSFER;
- [self debug: MED, "FTPM: FILE_TRANSFER\n"];
- [messageFieldID setStringValue: "Retrieving remote file..."];
- if( stat(localFilePath,&transferStatus) != -1)
- {
- if(overWrite == NO)
- {
- int alertRslt = NXRunAlertPanel(NULL,
- "The local file: %s exists. Overwrite?",
- "Yes","Save As...","Cancel",remoteFilename);
- switch(alertRslt)
- {
- case NX_ALERTDEFAULT :
- [self debug: HIGH, "FTPM: AlertPanel = NX_ALERTDEFAULT\n"];
- truncate(localFilePath,0);
- break;
- case NX_ALERTALTERNATE :
- [self debug: HIGH, "FTPM: AlertPanel = NX_ALERTALTERNATE\n"];
- if([self setLocalPath: ftpDirectory file: remoteFilename
- panel: YES] == nil)
- {
- [self abortTransfer: self];
- break;
- }
- /* In case the user changed their mind */
- if( stat(localFilePath,&transferStatus) != -1)
- truncate(localFilePath,0);
- break;
- case NX_ALERTOTHER :
- [self debug: HIGH, "FTPM: AlertPanel = NX_ALERTOTHER\n"];
- case NX_ALERTERROR :
- [self abortTransfer: self];
- break;
- }
- }
- }
- else
- truncate(localFilePath,0);
- [self writeFtpCommand: "get %s %s\n", remoteFilename, localFilePath];
- [sizeFieldID setStringValue: "0"];
- [statusViewID displayPercent: 0.0];
- [[[hostnameFieldID window] contentView] addSubview: statusBoxID];
- [ftpPanelID display];
- }
- /* Else get the output from the dir command */
- else
- {
- [messageFieldID setStringValue: "Retrieving remote directory listing..."];
- listingSize = 0;
- listingBufferSize = 1024;
- NX_MALLOC(listingBuffer,char,listingBufferSize);
- if(listingBuffer == NULL)
- return [self error: ALERT key: DIRLISTING_ALLOC_FAILED];
- listingBuffer[0] = '\0';
- ftpMode = FILE_LISTING;
- [self writeFtpCommand: "dir\n"];
- }
- break;
- case TRANSFER_SUCCESS :
- case LISTING_SUCCESS :
- ftpMode = DISCONNECT;
- [self debug: MED, "FTPM: DISCONNECT\n"];
- if(dirVLINK == nil)
- {
- [messageFieldID setStringValue: "Transfer complete."];
- [statusViewID displayPercent: 1.0];
- }
- else
- {
- DPSRemoveTimedEntry(dirListingTE);
- dirListingTE = (DPSTimedEntry) 0;
- [messageFieldID setStringValue: "Listing complete."];
- }
- [self writeFtpCommand: "bye\n"];
- break;
- case DISCONNECT_SUCCESS:
- case FTP_ERROR :
- transferComplete = YES;
- break;
- default:
- break;
- }
-
- /* Compute the local files size and update the percent completion */
- if(ftpMode >= FILE_TRANSFER && dirVLINK == nil)
- {
- if( stat(localFilePath,&transferStatus) != -1)
- {
- BOOL update;
- currentSize = transferStatus.st_size;
- sprintf(sizeBuffer,"%d bytes",(int)currentSize);
- [sizeFieldID setStringValue: sizeBuffer];
- if(targetFileSize > 0)
- {
- percent = currentSize / (float)targetFileSize;
- [self debug: MEGA,"FTPM: File transfer percent = %f\n",percent];
- if(currentSize > lastSize)
- {
- lastSize = currentSize;
- update = NO;
- if(percent <= 1.0)
- { // Just in case the size was parsed incorrectly
- if([statusViewID displayPercent: percent] != nil)
- update = YES;
- }
- [self conditionalDebug: update : HIGH, "size = %d; percent = %f\n", lastSize, percent];
- }
- }
- }
- }
- }
- /* Remove the timed entry */
- DPSRemoveTimedEntry(ftpDaemonTE);
- if(dirListingTE)
- DPSRemoveTimedEntry(dirListingTE);
- [timerViewID endTimer: self];
-
- /* Set the indicators to their final state */
- if(ftpMode != FTP_ERROR && dirVLINK == nil)
- {
- if(modalStatus != ABORT_TRANSFER)
- {
- if( targetFileSize > 0)
- {
- sprintf(sizeBuffer,"%d bytes", targetFileSize);
- [sizeFieldID setStringValue: sizeBuffer];
- }
- }
- else if(modalStatus == ABORT_TRANSFER)
- {
- [statusBoxID removeFromSuperview];
- [messageFieldID setStringValue: "Transfer Aborted."];
- [ftpPanelID display];
- /* Remove the trasfer file */
- unlink(localFilePath);
- }
- }
-
- /* End the modal session */
- [NXApp endModalSession: &theSession];
- /* Order out the panel after a slight delay to make sure the final
- state was indicated */
- if(dirVLINK == nil)
- [ftpPanelID perform: @selector(orderOut:) with: self
- afterDelay: 2000 cancelPrevious: NO];
- else
- [ftpPanelID orderOut: self];
-
- /* Display an alert if the FTP_ERROR mode is set */
- if(ftpMode == FTP_ERROR)
- {
- int alertRslt;
- [self writeFtpCommand: "bye\n"];
- alertRslt = NXRunAlertPanel(NULL,"An ftp error occurred, the error was:\n\t[%s]",
- "Ok","Info...",NULL,ftpErrMessage);
- if(alertRslt == NX_ALERTALTERNATE)
- [self displayErrOutput: self];
- /* Remove the trasfer file. Maybe I should prompt the user? */
- unlink(localFilePath);
- }
- else if(dirVLINK != nil && modalStatus != ABORT_TRANSFER)
- {
- /* Parse the listingBuffer to generate the DirEntry linked list */
- if( [dirVLINK setListing: ParseBuffer(listingBuffer)] == nil)
- [self error: ALERT key: SETLISTING_FAILED];
- }
-
- [self debug: MEGA,"FTPM: Done.\n"];
-
- NX_FREE(tmpBuffer);
- NX_FREE(listingBuffer);
- NX_FREE(ftpFullErrMessage);
- NX_FREE(localFilePath);
- localFilePath = NULL;
- ftpFullErrMessage = NULL;
- listingBuffer = NULL;
-
- return self;
- }
-
- void FtpDaemon(DPSTimedEntry te, double timeNow, FTPManager *self)
- {
- int n;
- char ftpRsltBuffer[1024],*bufferPtr,*tmpPtr,_150_RsltBuffer[128];
- BOOL condition;
- BOOL ValidFTPCode(int code);
- int GetSize(char *buffer,FTPManager *self);
- char *FTPResultCode(char *buffer,int *result);
- static double byeTimeOut;
-
- /* Don't care about anything after the ``221 Goodbye..'' code */
- if(self->transferComplete == YES)
- return;
-
- /* Read the ftp channel and display it in status TextField */
- n = read(0, ftpRsltBuffer,1023);
- if(n == -1) n ++;
- ftpRsltBuffer[n] = '\0';
- _150_RsltBuffer[0] = '\0';
- condition = (n > 0);
- [self conditionalDebug: condition : FTP_RESULT, "FTPD: %s\n", ftpRsltBuffer];
-
- /* Check the output for the ``ftp: connect: Network is unreachable'' message,
- and the ``... unkown host'' message since ftp does not return an
- error code(or any) for this */
- if(re_match(ftpRsltBuffer, unreachableNetworkExpr) == 1)
- {
- strcpy(ftpErrMessage,"Ftp claims this network is unreachable");
- if(ftpFullErrMessage != NULL)
- NX_FREE(ftpFullErrMessage);
- NX_MALLOC(ftpFullErrMessage,char,strlen(ftpRsltBuffer)+1);
- if(ftpFullErrMessage == NULL)
- {
- [self error: ALERT key: ERRBUFFER_ALLOC_FAILED];
- // User chose not to abort
- self->ftpMode = FTP_SELF_ERROR;
- return;
- }
- strcpy(ftpFullErrMessage, ftpRsltBuffer);
- self->ftpMode = FTP_ERROR;
- return;
- }
- else if(re_match(ftpRsltBuffer, unkownHostExpr) == 1)
- {
- sprintf(ftpErrMessage,"Ftp claims the host:%s is unkown",
- [self->hostnameFieldID stringValue]);
- if(ftpFullErrMessage != NULL)
- NX_FREE(ftpFullErrMessage);
- NX_MALLOC(ftpFullErrMessage,char,strlen(ftpRsltBuffer)+1);
- if(ftpFullErrMessage == NULL)
- {
- [self error: ALERT key: ERRBUFFER_ALLOC_FAILED];
- // User chose not to abort
- self->ftpMode = FTP_SELF_ERROR;
- return;
- }
- strcpy(ftpFullErrMessage, ftpRsltBuffer);
- self->ftpMode = FTP_ERROR;
- return;
- }
-
- /* See if we are looking for a directory listing, and if so,
- copy the output to the listing buffer. Also, the only ftp result
- code we are looking for is the ``226 Transfer complete'' message.
- The simple approach used in the following while loop will match too many
- things, so we do the regex matching here. One problem with this is that if
- the transfer does not complete, we can get stuck here so we start
- another timed entry to make sure that the size of the listingBuffer continues
- to grow. All of these special case situations are beginning to make my parsing
- approach rather inelegant. I need to figure a more robust, yet simple method
- of parsing the ftp output. */
- if(self->ftpMode == FILE_LISTING && n > 0)
- {
- /* Start the listing TE if not already running */
- if(self->listingTEStarted == NO)
- {
- self->dirListingTE = DPSAddTimedEntry(5.0, ListingDaemon,self,
- NX_MODALRESPTHRESHOLD);
- self->listingTEStarted = YES;
- }
-
- /* Check to see that the listing buffer size is large enough */
- listingSize += n;
- if(listingSize >= listingBufferSize)
- {
- listingBufferSize = listingSize + 512;
- NX_REALLOC(listingBuffer,char, listingBufferSize);
- if(listingBuffer == NULL)
- {
- [self error: ALERT key: DIRLISTING_REALLOC_FAILED];
- // User chose not to abort
- self->ftpMode = FTP_SELF_ERROR;
- return;
- }
- }
- strcat(listingBuffer,ftpRsltBuffer);
-
- /* Check for the transfer complete code */
- if(re_match(ftpRsltBuffer,transferCompleteExpr) == 1)
- lastFtpResult = 226;
-
- /* Bypass the normal ftp code checking */
- ftpRsltBuffer[0] = '\0';
- }
-
- /* Scan the ftpRsltBuffer for the ftp result numbers */
- bufferPtr = ftpRsltBuffer;
- while( (tmpPtr = FTPResultCode(bufferPtr,&lastFtpResult)) != NULL)
- {
- bufferPtr = NULL;
- /* Check for an error code and save the ftp output if there was one */
- if(lastFtpResult >= 500)
- {
- sscanf((tmpPtr-4),"%[^\n]",ftpErrMessage);
- if(ftpFullErrMessage != NULL)
- NX_FREE(ftpFullErrMessage);
- NX_MALLOC(ftpFullErrMessage,char,strlen(ftpRsltBuffer)+1);
- if(ftpFullErrMessage == NULL)
- {
- [self error: ALERT key: ERRBUFFER_ALLOC_FAILED];
- // User chose not to abort
- self->ftpMode = FTP_SELF_ERROR;
- return;
- }
- strcpy(ftpFullErrMessage, ftpRsltBuffer);
- self->ftpMode = FTP_ERROR;
- return;
- }
-
- /* See if this is the ``150 Opening..'' command that has the file size */
- if(self->ftpMode == FILE_TRANSFER && lastFtpResult == DATA_CONNECTION)
- {
- if(_150_RsltBuffer[0] == '\0') // True if not already filled
- sscanf(tmpPtr,"%[^\n]\n",_150_RsltBuffer);
- [self debug: MED,"FTPD: _150_RsltBuffer: %s\n", _150_RsltBuffer];
- }
-
- [self debug: HIGH, "FTPD_loop: lastFtpResult = %d\n", lastFtpResult];
- condition = (lastFtpResult != 0);
- [self conditionalDebug: condition : MED, "FTPD_loop: lastFtpResult = %d\n", lastFtpResult];
- }
-
- [self debug: MEGA, "FTPD_last: lastFtpResult = %d\n", lastFtpResult];
-
- [self debug: MEGA,"FTPD: ftpMode = %d\n", self->ftpMode];
- switch(self->ftpMode)
- {
- case BEGIN_SESSION :
- if(lastFtpResult == CONNECTED)
- {
- lastFtpResult = 0;
- self->ftpMode = CONNECT_SUCCESS;
- [self debug: MED, "FTPD: CONNECT_SUCCESS \n"];
- }
- break;
- case USER_LOGIN :
- if(lastFtpResult == LOGIN_OK)
- {
- lastFtpResult = 0;
- self->ftpMode = LOGIN_SUCCESS;
- [self debug: MED, "FTPD: LOGIN_SUCCESS \n"];
- }
- break;
- case USER_PASSWORD :
- if(lastFtpResult == PASSWORD_OK)
- {
- lastFtpResult = 0;
- self->ftpMode = PASSWORD_SUCCESS;
- [self debug: MED, "FTPD: PASSWORD_SUCCESS \n"];
- }
- break;
- case REMOTE_CHDIR :
- if(lastFtpResult == CWD_OK || lastFtpResult == CWD_OK2)
- {
- lastFtpResult = 0;
- self->ftpMode = CHDIR_SUCCESS;
- [self debug: MED, "FTPD: CHDIR_SUCCESS \n"];
- }
- break;
- case FILE_TRANSFER :
- if(lastFtpResult == DATA_CONNECTION && _150_RsltBuffer[0] != '\0')
- {
- /* Parse the dir to get the file size */
- self->targetFileSize = GetSize(_150_RsltBuffer,self);
- [self debug: MED, "FTPD: targetSize = %d \n",self->targetFileSize];
- }
- else if(lastFtpResult == TRANSFER_COMPLETE)
- {
- lastFtpResult = 0;
- self->ftpMode = TRANSFER_SUCCESS;
- [self debug: MED, "FTPD: TRANSFER_SUCCESS \n"];
- }
- break;
- case FILE_LISTING :
- if(lastFtpResult == TRANSFER_COMPLETE)
- {
- lastFtpResult = 0;
- self->ftpMode = LISTING_SUCCESS;
- /* Prepare the disconnect time out variable */
- byeTimeOut = timeNow;
- }
- break;
- case DISCONNECT :
- if(lastFtpResult == GOODBYE || (timeNow - byeTimeOut) > 5.0 )
- {
- lastFtpResult = 0;
- self->ftpMode = DISCONNECT_SUCCESS;
- [self debug: MED, "FTPD: DISCONNECT_SUCCESS \n"];
- self->transferComplete = YES;
- [self debug: HIGH, "FTPD: Set transferComplete\n"];
- }
- break;
- }
- }
-
- char *FTPResultCode(char *ftpRsltBuffer,int *result)
- {
- int ftpCode;
- char codeString[5];
- static char *searchBuffer;
- BOOL ValidFTPCode(int code);
-
- /* Go through the buffer looking for patterns "[1-5][0-9][0-9] " */
- if(ftpRsltBuffer != NULL && ftpRsltBuffer[0] != '\0')
- searchBuffer = ftpRsltBuffer;
- else if(ftpRsltBuffer != NULL)
- return NULL;
-
- if(re_match(searchBuffer, ftpResultExpr) == 1)
- {
- /* Move the search pointer past the code string */
- searchBuffer = ftpResultExpr->start + 4;
- strncpy(codeString, ftpResultExpr->start,4);
- codeString[4] = '\0';
- ftpCode = atoi(codeString);
- /* Check the code number */
- if(ValidFTPCode(ftpCode) == YES)
- {
- *result = ftpCode;
- return searchBuffer;
- }
- else
- return FTPResultCode(NULL,result);
- }
- searchBuffer = NULL;
- return searchBuffer;
- }
-
- BOOL ValidFTPCode(int code)
- {
- /* Check this code against those I am interested in */
- switch(code)
- {
- case CONNECTED:
- case LOGIN_OK:
- case PASSWORD_OK:
- case CWD_OK:
- case TRANSFER_COMPLETE:
- case GOODBYE:
- case DATA_CONNECTION:
- case PORT_OK:
- return YES;
- break;
- case FTP_ERR0:
- case FTP_ERR1:
- case FTP_ERR2:
- case FTP_ERR3:
- case FTP_ERR4:
- case FTP_ERR5:
- case FTP_ERR6:
- case FTP_ERR7:
- return YES;
- default:
- break;
- }
- return NO;
- }
-
- /* Parse the ``150 ...'' output for the size */
- int GetSize(char *buffer,FTPManager *self)
- {
- int size = 0;
- char tmpBuffer[32];
-
- [self debug: HIGH,"Begin size parsing\n"];
- /* Find the end of the line */
- if( re_match(buffer, transferSizeExpr) != 1)
- return 0;
- sscanf(transferSizeExpr->start,"%[0-9] ",tmpBuffer);
- [self debug: MED,"(%s bytes)\n",tmpBuffer];
- size = atoi(tmpBuffer);
- return size;
- }
-
- /* The proc which tries to determine if a dir command fails to complete */
- void ListingDaemon(DPSTimedEntry te, double timeNow, FTPManager *self)
- {
- static int lastListingSize = 0;
- if(listingBufferSize != lastListingSize)
- lastListingSize = listingBufferSize;
- else
- self->listingTECalls ++;
-
- if(self->listingTECalls >= LISTING_TIMEOUT_COUNT)
- {
- /* Set the ftpFullErrMessage ptr to the incomplete dir listing
- and set the ftpErrMessage */
- strcpy(ftpErrMessage,"ftp ``dir'' command timed out");
- if(ftpFullErrMessage != NULL)
- NX_FREE(ftpFullErrMessage);
- ftpFullErrMessage = listingBuffer;
- listingBuffer = NULL;
- self->ftpMode = FTP_ERROR;
- }
- }
-
- /* Report the error associated with the keyString and depending on
- the continue depending on the severity */
- - error:(int) severity key:(const char *) keyString
- {
- const char *errMessage;
- const char *replyRequest;
-
- int alertResult;
-
- errMessage = [errStringTable valueForStringKey: keyString];
- switch(severity)
- {
- case SORRY :
- replyRequest = [errStringTable valueForStringKey: REPLY_REQUEST2];
- break;
- case ALERT :
- replyRequest = [errStringTable valueForStringKey: REPLY_REQUEST1];
- break;
- case SEVERE :
- default:
- replyRequest = [errStringTable valueForStringKey: REPLY_REQUEST0];
- break;
- }
-
- if(severity >= SEVERE)
- alertResult = NXRunAlertPanel("Error","%s\n\t%s","Quit",NULL,NULL,
- errMessage,replyRequest);
- else
- alertResult = NXRunAlertPanel("Error","%s\n\t%s","Ok","Quit",NULL,
- errMessage,replyRequest);
- if(severity >= SEVERE || alertResult == NX_ALERTALTERNATE)
- [NXApp terminate: self];
-
- return self;
- }
-
- /* My debugging methods */
- #import <stdarg.h>
-
- - debug: (int) debugLevel, ...
- {
- va_list ap;
- char *format;
-
- // Only those calls with levels less than the current debugging level are active
- if(debugLevel > ftpDebugLevel)
- return self;
-
- va_start(ap, debugLevel);
- format = va_arg(ap,char *);
- vfprintf(errFile,format,ap);
- va_end(ap);
- fflush(errFile);
-
- return self;
- }
-
- - conditionalDebug: (BOOL) condition : (int) debugLevel, ...
- {
- va_list ap;
- char *format;
-
- if(condition == NO)
- return self;
- if(debugLevel > ftpDebugLevel)
- return self;
-
- va_start(ap, debugLevel);
- format = va_arg(ap,char *);
- vfprintf(errFile,format,ap);
- va_end(ap);
- fflush(errFile);
-
- return self;
- }
-
- /* The sender must implement the [[sender selectedCell] tag] */
- - setDebugLevel: sender
- {
- if([sender respondsTo: @selector(selectedCell)])
- ftpDebugLevel = [[sender selectedCell] tag];
- else
- ftpDebugLevel = 0; // Disable debugging
- return self;
- }
-
- - setErrFile: (FILE *) err
- {
- errFile = err;
- return self;
- }
-
- /* Unimplemented routines */
- - runBatch: (const char *) hostName get: (const char *) targetPathName
- {
- [self notImplemented: _cmd];
- return self;
- }
- - runBatch: (const char *) hostName get: (const char *) targetPathName priority: (int) priority
- {
- [self notImplemented: _cmd];
- return self;
- }
-
- @end
-