/* File Transfer Protocol (FTP) Client ** for a WorldWideWeb browser ** =================================== ** ** A cache of one control connection is kept ** ** Note: Port allocation ** ** It is essential that the port is allocated by the system, rather ** than chosen in rotation by us (POLL_PORTS), or the following ** problem occurs. ** ** It seems that an attempt by the server to connect to a port which has ** been used recently by a listen on the same socket, or by another ** socket this or another process causes a hangup of (almost exactly) ** one minute. Therefore, we have to use a rotating port number. ** The problem remains that if the application is run twice in quick ** succession, it will hang for what remains of a minute. ** ** Authors ** TBL Tim Berners-lee ** DD Denis DeLaRoca 310 825-4580 ** History: ** 2 May 91 Written TBL, as a part of the WorldWideWeb project. ** 15 Jan 92 Bug fix: close() was used for NETCLOSE for control soc ** 10 Feb 92 Retry if cached connection times out or breaks ** 8 Dec 92 Bug fix 921208 TBL after DD ** 17 Dec 92 Anon FTP password now just WWWuser@ suggested by DD ** fails on princeton.edu! ** 30 Jun 95 Re-added cached connection so it works; Added support ** to display informational messages that the FTP site ** shows. ** */ /* SOCKS mods by: * Ying-Da Lee, * NEC Systems Laboratory * C&C Software Technology Center */ #include "../config.h" #include #include #include #include #include "HTFTP.h" /* Implemented here */ #include "../libnut/str-tools.h" #define LINE_LENGTH 1024 #include "HTParse.h" #include "HTUtils.h" #include "tcp.h" #include "HTTCP.h" #include "HTAnchor.h" #include "HTFile.h" #include "HTChunk.h" #include "HTSort.h" #include "HText.h" #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 /* Arbitrary limit */ #endif /*For HTAA_LOGIN */ #include "HTAAUtil.h" #ifndef IPPORT_FTP #define IPPORT_FTP 21 #endif #ifdef __STDC__ #include #endif #ifndef NIL #define NIL 0 #endif #ifndef DISABLE_TRACE extern int www2Trace; #endif #define SWP_HACK /* Hypertext object building machinery */ #include "HTML.h" #define PUTC(c) (*targetClass.put_character)(target, c) #define PUTS(s) (*targetClass.put_string)(target, s) #define START(e) (*targetClass.start_element)(target, e, 0, 0) #define END(e) (*targetClass.end_element)(target, e) #define END_TARGET (*targetClass.end_document)(target) #define FREE_TARGET (*targetClass.free)(target) struct _HTStructured { WWW_CONST HTStructuredClass * isa; /* ... */ }; struct _HTStream { WWW_CONST HTStreamClass* isa; /* ... */ }; /* ** Info for cached connection; right now we only keep one around for a while */ extern XtAppContext app_context; extern int ftp_timeout_val; extern int securityType; void close_it_up(); extern int noLength; char *prompt_for_password(char *msg); /*SWP -- Yukky Cool Kludge*/ int ftpKludge=0; extern int loading_length; BOOL usingNLST = 0; /*SWP -- FTP "redial" using ftpRedial resource for number of times*/ extern int ftpRedial; extern int ftpRedialSleep; extern int ftpFilenameLength; extern int ftpEllipsisLength; extern int ftpEllipsisMode; /*SWP -- 9.27.95 -- Directory parsing*/ #define NEW_PARSE #ifdef NEW_PARSE int ParseDate(char *szBuffer, char *szFileInfo, char *szMonth, char *szDay, char *szYear, char *szTime); int ParseFileSizeAndName(char *szBuffer, char *szFileName, char *szSize); #endif HText *HT; int fTimerStarted = 0; XtIntervalId timer; static struct ftpcache { int control; char p1[256]; char host[256]; char username[BUFSIZ]; char password[BUFSIZ]; } ftpcache = { -1, "", "", "", "" }; #ifdef SOCKS extern struct in_addr SOCKS_ftpsrv; /* in HTFTP.c */ #endif /* HTFTPClearCache () Expects: Nothing Returns: Nothing */ void HTFTPClearCache (void) { ftpcache.password[0] = '\0'; ftpcache.control = -1; ftpcache.p1[0] = 0; ftpcache.host[0] = 0; ftpcache.username[0] = 0; } /* Module-Wide Variables ** --------------------- */ PRIVATE char response_text[LINE_LENGTH+1];/* Last response from NewsHost */ PRIVATE int control = -1; /* Current connection */ PRIVATE int data_soc = -1; /* Socket for data transfer =invalid */ PRIVATE int master_socket = -1; /* Listening socket = invalid */ PRIVATE char port_command[255]; /* Command for setting the port */ #define DATA_BUFFER_SIZE 2048 PRIVATE char data_buffer[DATA_BUFFER_SIZE]; /* Input data buffer */ PRIVATE char * data_read_pointer; PRIVATE char * data_write_pointer; /* Procedure: Read a character from the data connection ** ---------------------------------------------------- */ PRIVATE int interrupted_in_next_data_char = 0; PRIVATE char next_data_char NOARGS { int status; interrupted_in_next_data_char = 0; if (data_read_pointer >= data_write_pointer) { status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE); if (status == HT_INTERRUPTED) interrupted_in_next_data_char = 1; if (status <= 0) return (char)-1; data_write_pointer = data_buffer + status; data_read_pointer = data_buffer; } return *data_read_pointer++; } /* Execute Command and get Response ** -------------------------------- ** ** See the state machine illustrated in RFC959, p57. This implements ** one command/reply sequence. It also interprets lines which are to ** be continued, which are marked with a "-" immediately after the ** status code. ** ** Continuation then goes on until a line with a matching reply code ** an a space after it. ** ** On entry, ** con points to the connection which is established. ** cmd points to a command, or is NIL to just get the response. ** ** The command is terminated with the CRLF pair. ** ** On exit, ** returns: The first digit of the reply type, ** or negative for communication failure. */ #ifdef __STDC__ PRIVATE int response (char * cmd) #else PRIVATE int response (cmd) char * cmd; #endif { int result; /* Three-digit decimal code */ #ifdef OLD int continuation_response = -1; #endif int status; char continuation; int multiline_response = 0; int messageStarted = 0; char *ptr; char bytestr[256],*byteptr; int bytes; if (!control || control == -1) { #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr, "FTP: No control connection set up!!\n"); #endif return -99; } if (cmd) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, " Tx: %s", cmd); #endif status = NETWRITE(control, cmd, (int)strlen(cmd)); if (status<0) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP: Error %d sending command: closing socket %d\n", status, control); #endif CLOSE_CONTROL(control); control = -1; return status; } } /* Patch to be generally compatible with RFC 959 servers -spok@cs.cmu.edu */ /* Multiline responses start with a number and a hyphen; end with same number and a space. When it ends, the number must be flush left. */ do { char *p = response_text; /* If nonzero, it's set to initial code of multiline response */ for (;;) { int foo; /* This is set to 0 at the start of HTGetCharacter. */ extern int interrupted_in_htgetcharacter; foo = (*p++ = HTGetCharacter ()); if (interrupted_in_htgetcharacter) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted in HTGetCharacter, apparently.\n"); #endif CLOSE_CONTROL (control); control = -1; return HT_INTERRUPTED; } if (foo == LF || /* if (((*p++=NEXT_CHAR) == '\n') || */ (p == &response_text[LINE_LENGTH])) { *p++=0; /* Terminate the string */ #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, " Rx: %s", response_text); #endif if (!strncmp(response_text,"150",3)) { if((ptr=strrchr(response_text,'(')) && *ptr){ bytes = atoi((ptr+1)); } else { bytes=0; } if (bytes==0) { loading_length=(-1); } else { noLength=0; loading_length=bytes; } } else { noLength=0; HTMeter(100,NULL); noLength=1; } sscanf(response_text, "%d%c", &result, &continuation); if ((response_text[0] == '2') || (response_text[0] == '5')) { if (continuation == '-') { char *p; if (messageStarted == 0) { HText_appendText(HT, "
\n");
        				HTProgress ("Receiving directory message");
					messageStarted = 1;
				}

				p = strchr(response_text, '-');
				p++;
				if (p != NULL)
					HText_appendText (HT, p);
			}
		}

				
              if (continuation == '-' && !multiline_response) 
                {
                  multiline_response = result;
                }
              else if (multiline_response && continuation == ' ' &&
                       multiline_response == result &&
                       isdigit(response_text[0])) 
                {
                  /* End of response (number must be flush on left) */
                  multiline_response = 0;
                }
              break;
            } /* if end of line */
          
          if (*(p-1) < 0) 
            {
#ifndef DISABLE_TRACE
              if (www2Trace) 
                fprintf(stderr, "Error on rx: closing socket %d\n",
                        control);
#endif
#ifdef SWP_HACK
	      loading_length=(-1);
#endif
              strcpy (response_text, "000 *** TCP read error on response\n");
              CLOSE_CONTROL(control);
              control = -1;
              return -1;	/* End of file on response */
            }
        } /* Loop over characters */
    } 
  while (multiline_response);

  if (messageStarted)
	  HText_appendText(HT, "

\n"); #ifdef OLD do { char *p = response_text; for(;;) { int foo; /* This is set to 0 at the start of HTGetCharacter. */ extern int interrupted_in_htgetcharacter; foo = (*p++ = HTGetCharacter ()); if (interrupted_in_htgetcharacter) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted in HTGetCharacter, apparently.\n"); #endif CLOSE_CONTROL (control); control = -1; return HT_INTERRUPTED; } if (foo == LF || p == &response_text[LINE_LENGTH]) { char continuation; int rv; *p++=0; /* Terminate the string */ #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, " Rx: %s", response_text); #endif /* Clear out result ahead of time to see if we couldn't read a real value. */ result = -1; rv = sscanf(response_text, "%d%c", &result, &continuation); /* Try just continuing if we couldn't pull out a value for result and the response_text starts with whitespace. */ if (rv < 2 && result == -1 && (*response_text == ' ' || *response_text == '\t')) { /* Dunno what to do here -- the code isn't really set up to deal with continuation lines starting with whitespace. Testcase is reports.adm.cs.cmu.edu. */ } else if (continuation_response == -1) { if (continuation == '-') /* start continuation */ continuation_response = result; } else { /* continuing */ if (continuation_response == result && continuation == ' ') continuation_response = -1; /* ended */ } break; } /* if end of line */ if (*(p-1) == EOF) { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "Error on rx: closing socket %d\n", control); #endif strcpy (response_text, "000 *** TCP read error on response\n"); CLOSE_CONTROL(control); control = -1; return -1; /* End of file on response */ } } /* Loop over characters */ } while (continuation_response != -1); #endif if (result == 421) { #ifndef DISABLE_TRACE if(www2Trace) fprintf(stderr, "FTP: They close so we close socket %d\n", control); #endif #ifdef SWP_HACK loading_length=(-1); #endif CLOSE_CONTROL(control); return -1; } if (result==550) { HTProgress(response_text); } return result/100; } /* Get a valid connection to the host ** ---------------------------------- ** ** On entry, ** arg points to the name of the host in a hypertext address ** On exit, ** returns <0 if error ** socket number if success ** ** This routine takes care of managing timed-out connections, and ** limiting the number of connections in use at any one time. ** ** It ensures that all connections are logged in if they exist. ** It ensures they have the port number transferred. */ PRIVATE int get_connection ARGS1 (char *,arg) { int status, con; static char host[BUFSIZ]; static char username[BUFSIZ]; static char password[BUFSIZ]; char dummy[MAXHOSTNAMELEN+32]; int redial=0; if (!arg) return -1; /* Bad if no name sepcified */ if (!*arg) return -1; /* Bad if name had zero length */ #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP: Looking for %s\n", arg); #endif { char *p1 = HTParse(arg, "", PARSE_HOST); char *p2 = strrchr(p1, '@'); /* user? */ char * pw; char * un; /* Save the actual host */ { char *tmpptr; strcpy(host,p1); tmpptr=strchr(host,'/'); if (tmpptr) { *tmpptr='\0'; } } if (p2) { un = p1; *p2=0; /* terminate */ p1 = p2+1; /* point to host */ pw = strchr(un, ':'); if (pw) { *pw++ = 0; /* password = pw; */ } if (strcmp(un,username)) { /*new username*/ strcpy(username,un); if (pw) { strcpy(password,pw); } else { *password='\0'; } } else { /*same username*/ if (!*(ftpcache.host) || (*(ftpcache.host) && strcmp(host,ftpcache.host))) { /*new host*/ *password='\0'; } } } /*no username*/ else { if (strcmp(username,"anonymous")) { /*last one was not anon*/ *username='\0'; *password='\0'; } } /* copy hostname into dummy URL, since username:password@ might have been part of original */ sprintf(dummy, "ftp://%s", p1); #ifndef DISABLE_TRACE if(www2Trace) fprintf (stderr, "FTP: set dummy to %s\n", dummy); #endif /*Is the cache connection still good?*/ if (ftpcache.control!=(-1) && *(ftpcache.host) && !strcmp(ftpcache.host,host) && *username) { /*Did we use a username before?*/ if (!*username) { return(ftpcache.control); } /*Is the username and password used the same?*/ if (*username && !strcmp(ftpcache.username,username) && *password && !strcmp(ftpcache.password,password)) { /* For security Icon */ if (*username && strcmp(username,"anonymous") && strcmp(username,"ftp")) { /*not anon login...assuming a real login*/ securityType=HTAA_LOGIN; } else { securityType=HTAA_NONE; } return(ftpcache.control); } /*Something has changed...reopen connection*/ else { close(ftpcache.control); } } /*Connection is not good. Reopen*/ strcpy(ftpcache.p1,p1); strcpy(ftpcache.host,host); if (*username) { strcpy(ftpcache.username,username); } else { ftpcache.username[0]='\0'; } if (*password) { strcpy(ftpcache.password,password); } else { ftpcache.password[0]='\0'; } /* if ((ftpcache.p1[0] != NULL) && (strcmp(ftpcache.p1,p1) == 0) && (ftpcache.control != -1)) { return ftpcache.control; } else { close(ftpcache.control); strcpy(ftpcache.p1,p1); } */ if (!*username) { free(p1); } } /*default the redial values if out of range*/ if (ftpRedial<0) { ftpRedial=0; } if (ftpRedialSleep<1) { ftpRedialSleep=1; } redialFTP: con = -1; #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr,"dummy = %s\n",dummy); #endif status = HTDoConnect (dummy, "FTP", IPPORT_FTP, &con); if (status < 0) { #ifndef DISABLE_TRACE if (www2Trace) { if (status == HT_INTERRUPTED) fprintf (stderr, "FTP: Interrupted on connect\n"); else fprintf(stderr, "FTP: Unable to connect to remote host for `%s'.\n", arg); } #endif if (status == HT_INTERRUPTED) HTProgress ("Connection interrupted."); if (con != -1) { CLOSE_CONTROL(con); con = -1; } /* if (username) free(username); */ HTProgress ("Unable to connect to remote host."); return status; /* Bad return */ } #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP connected, assigning control socket %d\n", con); #endif control = con; /* Current control connection */ ftpcache.control = control; /* Initialise buffering for contron connection */ HTInitInput (con); /* Now we log in; Look up username, prompt for pw. */ { int status = response (NIL); /* Get greeting */ if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted at beginning of login.\n"); #endif #ifdef SWP_HACK loading_length=(-1); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL(control); control = -1; return HT_INTERRUPTED; } if (status == 2) { /* Send username */ char * command; if (*username) { command = (char*)malloc(10+strlen(username)+2+1); sprintf(command, "USER %s%c%c", username, CR, LF); if (!*password) { char *pw=NULL; pw=prompt_for_password("Please Enter Your FTP Password:"); if (pw && *pw) { strcpy(password,pw); strcpy(ftpcache.password,password); free(pw); } else { *password='\0'; *(ftpcache.password)='\0'; HTProgress ("Connection aborted."); CLOSE_CONTROL(control); control = -1; return HT_INTERRUPTED; } } } else { command = (char*)malloc(25); sprintf(command, "USER anonymous%c%c", CR, LF); strcpy(username,"anonymous"); strcpy(ftpcache.username,username); } status = response (command); free(command); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted while sending username.\n"); #endif #ifdef SWP_HACK loading_length=(-1); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL(control); control = -1; return HT_INTERRUPTED; } } if (status == 3) { /* Send password */ char * command; if (*password) { command = (char*)malloc(10+strlen(password)+2+1); sprintf(command, "PASS %s%c%c", password, CR, LF); } else { char * user = getenv("USER"); extern char *machine_with_domain; char *host = machine_with_domain; if (!user) user = "WWWuser"; /* If not fully qualified, suppress it as ftp.uu.net prefers a blank to a bad name */ if (!strchr(host, '.')) host = ""; command = (char*)malloc(20+strlen(host)+2+1); sprintf(command, "PASS %s@%s%c%c", user ? user : "WWWuser", host, CR, LF); /*@@*/ sprintf(password,"%s@%s",(user?user:"WWWuser"),host); strcpy(ftpcache.password,password); } status = response (command); free(command); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted while sending password.\n"); #endif #ifdef SWP_HACK loading_length=(-1); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL(control); control = -1; return HT_INTERRUPTED; } } if (status == 3) { char temp[80]; /* if (username) free(username); */ sprintf (temp, "ACCT noaccount%c%c", CR, LF); status = response (temp); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted while sending ACCT.\n"); #endif #ifdef SWP_HACK loading_length=(-1); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL(control); control = -1; return HT_INTERRUPTED; } } if (status != 2) { if (status==HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted in redial attempt.\n"); #endif #ifdef SWP_HACK loading_length=(-1); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL(control); control = -1; return HT_INTERRUPTED; } if (*username && strcmp(username,"anonymous")) { HText_appendText(HT, "

FTP login using username \""); HText_appendText(HT, username); HText_appendText(HT, "\" failed.


"); if (*password) { HText_appendText(HT, "If you have a login "); HText_appendText(HT, "on this machine please check "); HText_appendText(HT, "to make sure the password you "); HText_appendText(HT, "are specifying is correct."); } else { HText_appendText(HT, "This is probably because you "); HText_appendText(HT, "didn't specify a password "); HText_appendText(HT, "along with your username.
"); HText_appendText(HT, "To do this you have to specify "); HText_appendText(HT, "the FTP line like this:
"); HText_appendText(HT, "

"); HText_appendText(HT, "ftp://username:password@ftp_site/"); HText_appendText(HT, "

"); HText_appendText(HT, "OR"); HText_appendText(HT, "

"); HText_appendText(HT, "You can now just specify a username "); HText_appendText(HT, "and you will be prompted for your "); HText_appendText(HT, "password."); HText_appendText(HT, "

"); HText_appendText(HT, "e.g. ftp://username@ftp_site/"); } } else { char buf[BUFSIZ]; if (redialAnonymous FTP login failed.

There were %d redial attempts made.",redial); HText_appendText(HT, buf); } HTProgress("Login failed"); #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP: Login fail: %s", response_text); #endif HText_appendText(HT,"\n\n


Reason for Failure:

\n"); HText_appendText(HT,response_text); #ifdef SWP_HACK loading_length=(-1); #endif ftpcache.control = -1; CLOSE_CONTROL(control); control = -1; return -1; /* Bad return */ } #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP: Logged in.\n"); #endif /* For security Icon */ if (*username && strcmp(username,"anonymous") && strcmp(username,"ftp")) { /*not anon login...assuming a real login*/ securityType=HTAA_LOGIN; } else { securityType=HTAA_NONE; } } return con; /* Good return */ } /* Scope of con */ /* Close Master (listening) socket ** ------------------------------- ** ** */ #ifdef __STDC__ PRIVATE void close_master_socket(void) #else PRIVATE void close_master_socket() #endif { #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP: Closing master socket %d\n", master_socket); #endif NETCLOSE(master_socket); master_socket = -1; return; } /* Open a master socket for listening on ** ------------------------------------- ** ** When data is transferred, we open a port, and wait for the server to ** connect with the data. ** ** On entry, ** master_socket Must be negative if not set up already. ** On exit, ** Returns socket number if good ** less than zero if error. ** master_socket is socket number if good, else negative. ** port_number is valid if good. */ #ifdef __STDC__ PRIVATE int get_listen_socket(void) #else PRIVATE int get_listen_socket() #endif { struct sockaddr_in soc_address; /* Binary network address */ struct sockaddr_in *sin = &soc_address; int new_socket; /* Will be master_socket */ /* Create internet socket */ new_socket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); if (new_socket < 0) return -1; #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP: Opened master socket number %d\n", new_socket); #endif /* Search for a free port. */ sin->sin_family = AF_INET; /* Family = internet, host order */ sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */ { int status; int address_length = sizeof(soc_address); #ifdef SOCKS status = Rgetsockname(control, #else status = getsockname(control, #endif (struct sockaddr *)&soc_address, &address_length); if (status<0) return -1; #ifndef DISABLE_TRACE if(www2Trace) { fprintf(stderr, "FTP: This host is %s\n", HTInetString(sin)); } #endif soc_address.sin_port = 0; /* Unspecified: please allocate */ #ifdef SOCKS status=Rbind(new_socket, #else status=bind(new_socket, #endif (struct sockaddr*)&soc_address, /* Cast to generic sockaddr */ #ifdef SOCKS sizeof(soc_address), SOCKS_ftpsrv.s_addr); #else sizeof(soc_address)); #endif if (status<0) return -1; address_length = sizeof(soc_address); #ifdef SOCKS status = Rgetsockname(new_socket, #else status = getsockname(new_socket, #endif (struct sockaddr*)&soc_address, &address_length); if (status<0) return -1; } #ifndef DISABLE_TRACE if(www2Trace) { fprintf(stderr, "FTP: bound to port %d on %s\n", (int)ntohs(sin->sin_port), HTInetString(sin)); } #endif if (master_socket >= 0) close_master_socket (); master_socket = new_socket; /* Now we must find out who we are to tell the other guy */ (void)HTHostName(); /* Make address valid - doesn't work*/ sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c", (int)*((unsigned char *)(&sin->sin_addr)+0), (int)*((unsigned char *)(&sin->sin_addr)+1), (int)*((unsigned char *)(&sin->sin_addr)+2), (int)*((unsigned char *)(&sin->sin_addr)+3), (int)*((unsigned char *)(&sin->sin_port)+0), (int)*((unsigned char *)(&sin->sin_port)+1), CR, LF); /* Inform TCP that we will accept connections */ #ifdef SOCKS if (Rlisten (master_socket, 1) < 0) #else if (listen (master_socket, 1) < 0) #endif { close_master_socket (); return -1; } #ifndef DISABLE_TRACE if(www2Trace) { fprintf(stderr, "FTP: Master socket(), bind() and listen() all OK\n"); } #endif return master_socket; /* Good */ } /* get_listen_socket */ /* Read a directory into an hypertext object from the data socket ** -------------------------------------------------------------- ** ** On entry, ** anchor Parent anchor to link the this node to ** address Address of the directory ** On exit, ** returns HT_LOADED if OK ** <0 if error. ** ** Author: Charles Henrich (henrich@crh.cl.msu.edu) October 2, 1993 ** ** Completely re-wrote this chunk of code to present FTP directory information ** in a much more useful manner. Also included the use of icons. -Crh */ PRIVATE int read_directory ARGS4 ( HTParentAnchor *, parent, WWW_CONST char *, address, HTFormat, format_out, HTStream *, sink ) { HTFormat format; HTAtom *pencoding; char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION); char buffer[BUFSIZ]; char buf[BUFSIZ]; char itemtype; char itemname[BUFSIZ]; char itemsize[BUFSIZ]; char *full_ftp_name, *ptr; int count, ret, cmpr, c, rv; extern char *HTgeticonname(HTFormat, char *); char *ellipsis_string=(char *)calloc(1024,sizeof(char)); #ifdef NEW_PARSE int nTime; char szDate[256]; int nStringLen; int nSpaces; int nOldSpaces; char szFileInfo[32]; char szMonth[32]; char szDay[16]; char szYear[32]; char szTime[32]; #endif HTProgress("Reading FTP directory"); HText_appendText (HT, "<H1>FTP Directory "); HText_appendText (HT, filename); HText_appendText (HT, "</H1>\n"); #ifdef NEW_PARSE HText_appendText (HT, "<PRE>"); #endif HText_appendText (HT, "<DL>\n"); data_read_pointer = data_write_pointer = data_buffer; /* If this isnt the root level, spit out a parent directory entry */ if(strcmp(filename,"/") != 0) { HText_appendText(HT,"<DD>"); HText_appendText(HT,"<A HREF=\""); strcpy(buffer,filename); ptr = strrchr(buffer,'/'); if(ptr != NULL) *ptr='\0'; if(buffer[0] == '\0') HText_appendText(HT,"/"); else HText_appendText(HT, buffer); HText_appendText(HT,"\"><IMG SRC=\""); HText_appendText(HT, HTgeticonname(NULL, "directory")); HText_appendText(HT,"\"> Parent Directory</a>"); } /* Loop until we hit EOF */ while(1) { /* Read in a line of data */ for(count = 0; count < BUFSIZ; count++) { c = next_data_char (); if (interrupted_in_next_data_char) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Picked up interrupted_in_next_data_char\n"); #endif return HT_INTERRUPTED; } if (c == '\r') { c = next_data_char (); if (interrupted_in_next_data_char) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Picked up interrupted_in_next_data_char\n"); #endif return HT_INTERRUPTED; } if (c != '\n') break; } if (c == '\n' || c == (char)EOF) break; buffer[count] = c; } if(c == (char)EOF) break; buffer[count] = 0; /* Parse the input buffer, extract the item type, and the item size */ #if 0 ret=sscanf(buffer,"%c%*9s%*d %*s %*s %s", &itemtype, itemsize); if (ret != 2) continue; #endif /* Retain whole string -- we don't use it at the moment, but we will. */ full_ftp_name = strdup (buffer); /* Read but disregard itemsize -- this effectively guarantees we will know what we should display and what we shouldn't -- don't ask. */ if (usingNLST==1) { ret=sscanf(buffer,"%c%*9s %*d %*s %*s %s", &itemtype, itemsize); if (ret != 2) { free (full_ftp_name); continue; } } else if (usingNLST==2) { /*only name*/ if (!strcmp(buffer,".") || !strcmp(buffer,"..")) { free(full_ftp_name); continue; } ptr=strrchr(buffer,'.'); itemtype='-'; if (ptr && *ptr) { if (!my_strncasecmp(ptr,".dir",4)) { *ptr='\0'; itemtype='d'; } } } else { /*using LIST command*/ ret=sscanf(buffer,"%c%*9s %*d %*s %*s %s", &itemtype, itemsize); if (ret != 2) { free (full_ftp_name); continue; } } if (!buffer || !*buffer) { continue; } if (usingNLST==2) { /*only name*/ strcpy(itemname,buffer); nTime=(-1); } else { /* Due to the various time stamp formats, its "safer" to retrieve the */ /* filename by taking it from the right side of the string, we do that here. */ ptr = strrchr(buffer,' '); if(ptr == NULL) continue; strcpy(itemname,ptr+1); if (!strcmp(itemname,".") || !strcmp(itemname,"..")) { free(full_ftp_name); continue; } #ifdef NEW_PARSE nTime = ParseDate(buffer, szFileInfo, szMonth, szDay, szYear, szTime); if (usingNLST==1) { ParseFileSizeAndName(buffer, itemname, itemsize); } if (nTime == 3) { /* ie a dos or NT server possibly */ if (!ParseFileSizeAndName(buffer, itemname, itemsize)) { itemtype = 'd'; } else { itemtype = '-'; } } #endif } HText_appendText (HT, "<DD>"); /* Spit out the anchor refrence, and continue on... */ HText_appendText (HT, "<A HREF=\""); /* Assuming it's a relative reference... */ if (itemname && itemname[0] != '/') { HText_appendText (HT, filename); if (filename[strlen(filename)-1] != '/') HText_appendText (HT, "/"); } HText_appendText (HT, itemname); HText_appendText (HT, "\">"); /* There are 3 "types", directory, link and file. If its a directory we */ /* just spit out the name with a directory icon. If its a link, we go */ /* retrieve the proper name (i.e. the input looks like bob -> ../../../bob */ /* so we want to hop past the -> and just grab bob. The link case falls */ /* through to the filetype case. The filetype shows name and filesize, and */ /* then attempts to select the correct icon based on file extension. */ switch(itemtype) { case 'd': { if (compact_string(itemname,ellipsis_string,ftpFilenameLength,ftpEllipsisMode,ftpEllipsisLength)) { strcpy(itemname,ellipsis_string); } sprintf(buffer,"%s",itemname); HText_appendText(HT, "<IMG SRC=\""); HText_appendText(HT, HTgeticonname(NULL, "directory")); HText_appendText(HT, "\"> "); break; } case 'l': { ptr = strrchr(buffer,' '); if(ptr != NULL) { *ptr = '\0'; ptr = strrchr(buffer,' '); } if(ptr != NULL) { *ptr = '\0'; ptr = strrchr(buffer,' '); } if(ptr != NULL) strcpy(itemname,ptr+1); if (compact_string(itemname,ellipsis_string,ftpFilenameLength,ftpEllipsisMode,ftpEllipsisLength)) { strcpy(itemname,ellipsis_string); } } case '-': { /* If this is a link type, and the bytes are small, its probably a directory so lets not show the byte count */ #if 0 if(itemtype == 'l' && atoi(itemsize) < 128) { sprintf(buffer,"%s",itemname); } else { sprintf(buffer,"%s (%s bytes)",itemname,itemsize); } #endif #if 0 if(itemtype == 'l') { #endif if (compact_string(itemname,ellipsis_string,ftpFilenameLength,ftpEllipsisMode,ftpEllipsisLength)) { strcpy(itemname,ellipsis_string); } sprintf(buffer,"%s",itemname); #if 0 } else { /* code doesn't work for this, and neither does pre. */ sprintf(buffer,"<code>%s</code>",full_ftp_name); } #endif format = HTFileFormat(itemname, &pencoding, WWW_SOURCE, &cmpr); if (1) { HText_appendText(HT, "<IMG SRC=\""); /* If this is a link, and we can't figure out what kind of file it is by extension, throw up the unknown icon; however, if it isn't a link and we can't figure out what it is, throw up the text icon... Unless it's compressed. */ if(itemtype == 'l' && cmpr == COMPRESSED_NOT) { /* If it's unknown, let's call it a menu (since symlinks are most commonly used on FTP servers to point to directories, IMHO... -marc */ HText_appendText(HT, HTgeticonname(format, "directory") ); } else { HText_appendText(HT, HTgeticonname(format, "text")); } HText_appendText(HT, "\"> "); } else { HText_appendText(HT, "<IMG SRC=\""); HText_appendText(HT, HTgeticonname(format, "application")); HText_appendText(HT, "\"> "); } break; } default: { HText_appendText(HT, "<IMG SRC=\""); HText_appendText(HT, HTgeticonname(NULL, "unknown")); HText_appendText(HT, "\"> "); break; } } HText_appendText (HT, buffer); #ifndef NEW_PARSE HText_appendText (HT, "</A>\n"); #endif #ifdef NEW_PARSE HText_appendText (HT, "</A>"); nStringLen = strlen(buffer); nSpaces = ftpFilenameLength - nStringLen; /* if (itemtype != 'd') { */ if (nTime == 1) { struct tm *ptr; time_t t; t=time(0); ptr=localtime(&t); sprintf(szYear,"%d",1900+ptr->tm_year); sprintf(szDate, "%*s%9s %s %s %s %2.2s, %s", nSpaces, " ", itemsize, szFileInfo, szTime, szMonth, szDay, szYear); } else if (nTime == 0) { sprintf(szDate, "%*s%9s %s %s %s %2.2s, %s", nSpaces, " ", itemsize, szFileInfo, " ", szMonth, szDay, szYear); } else { /*nSpaces += strlen(itemsize); */ sprintf(szDate, "%*s %9.9s %s %s", nSpaces, " ", itemsize, szMonth, szTime); } /* } else { nOldSpaces = nSpaces; nSpaces += 22; if (nTime == 1) { sprintf(szDate, "%*s %s %s %2.2s", nSpaces, szFileInfo, szTime, szMonth, szDay); } else if (nTime == 0) { sprintf(szDate, "%*s %s %s %2.2s, %s", nSpaces, szFileInfo, "00:00", szMonth, szDay, szYear); } else { sprintf(szDate, "%*s %s %s", nOldSpaces, " ", szMonth, szTime); } } */ if (usingNLST!=2) { HText_appendText (HT, szDate); } HText_appendText (HT, "\n"); #endif free (full_ftp_name); } HText_appendText (HT, "</DL>\n"); #ifdef NEW_PARSE HText_appendText (HT, "</PRE>\n"); #endif HText_endAppend (HT); rv = response (NIL); if (rv == HT_INTERRUPTED) return rv; return rv == 2 ? HT_LOADED : -1; } /* Retrieve File from Server ** ------------------------- ** ** On entry, ** name WWW address of a file: document, including hostname ** On exit, ** returns Socket number for file if good. ** <0 if bad. */ PUBLIC int HTFTPLoad ARGS4 ( char *, name, HTParentAnchor *, anchor, HTFormat, format_out, HTStream *, sink ) { BOOL isDirectory = NO; int status; int retry; /* How many times tried? */ HTFormat format; int compressed = 0; int try; HTProgress("Retrieval in progress"); if (fTimerStarted) { XtRemoveTimeOut(timer); fTimerStarted = 0; } HT = HText_new (); HText_beginAppend (HT); for (retry = 0; retry < 2; retry++) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: TRYING in HTFTPLoad, attempt %d\n", retry); #endif status = get_connection(name); if (status < 0) { CLOSE_CONTROL (control); control = -1; /* HT_INTERRUPTED will fall through. */ #ifdef SWP_HACK loading_length=(-1); #endif return status; } status = get_listen_socket(); if (status < 0) { HText_appendText(HT, "FTP terminated because login failed"); HTProgress("Login failed"); CLOSE_CONTROL (control); control = -1; close_master_socket (); #ifdef SWP_HACK loading_length=(-1); #endif /* HT_INTERRUPTED would fall through, if we could interrupt somehow in the middle of it, which we currently can't. */ return status; } /* Inform the server of the port number we will listen on */ { status = response (port_command); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted in response (port_command)\n"); #endif #ifdef SWP_HACK loading_length=(-1); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); return HT_INTERRUPTED; } if (status !=2) { /* Could have timed out */ if (status < 0) { CLOSE_CONTROL (control); control = -1; close_master_socket (); continue; /* try again - net error*/ } CLOSE_CONTROL (control); control = -1; close_master_socket (); #ifdef SWP_HACK loading_length=(-1); #endif return HT_NOT_LOADED; /* bad reply */ } #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP: Port defined.\n"); #endif } status = 0; break; /* No more retries */ } /* for retries */ if (status < 0) { close_master_socket (); CLOSE_CONTROL (control); control = -1; #ifdef SWP_HACK loading_length=(-1); #endif return status; /* Failed with this code */ } /* Ask for the file: */ { char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION); char *fname, *ptr; char command[LINE_LENGTH+1]; HTAtom *encoding; if (!(*filename)) StrAllocCopy(filename, "/"); format = HTFileFormat (filename, &encoding, WWW_PLAINTEXT, &compressed); sprintf(command, "TYPE %s%c%c", "I", CR, LF); status = response (command); if (status != 2) { if (status == HT_INTERRUPTED) HTProgress ("Connection interrupted."); close_master_socket (); CLOSE_CONTROL (control); control = -1; free (filename); #ifdef SWP_HACK loading_length=(-1); #endif return (status == HT_INTERRUPTED) ? HT_INTERRUPTED : -1; } fname=strdup(filename); try=(-1); tryAgain: try++; sprintf(command, "RETR %s%c%c", fname, CR, LF); status = response (command); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted while sending RETR\n"); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); free (filename); #ifdef SWP_HACK loading_length=(-1); #endif return HT_INTERRUPTED; } if (status != 1) { /* Failed : try to CWD to it */ sprintf(command, "CWD %s%c%c", fname, CR, LF); status = response (command); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted while sending CWD\n"); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); free (filename); #ifdef SWP_HACK loading_length=(-1); #endif return HT_INTERRUPTED; } /* If we failed CWD and not at root and on type 2 NLST, try VMS style*/ if (status==5 && strcmp(filename,"/") && usingNLST==2) { switch(try) { case 0: /* First, put filename in fname*/ strcpy(fname,filename+1); goto tryAgain; case 1: /* First, put filename in fname*/ strcpy(fname,filename+1); /* Back up to last character*/ for (ptr=fname+strlen(fname)-1; ptr && *ptr && isspace(*ptr); ptr--) { *ptr='\0'; } /* If the last char is a /, get rid of it*/ if (fname[strlen(fname)-1]=='/') { fname[strlen(fname)-1]='\0'; } goto tryAgain; case 2: /* First, put filename in fname*/ strcpy(fname,filename+1); /* Back up to last character*/ for (ptr=fname+strlen(fname)-1; ptr && *ptr && isspace(*ptr); ptr--) { *ptr='\0'; } /* If the last char is a /, get rid of it*/ if (fname[strlen(fname)-1]=='/') { fname[strlen(fname)-1]='\0'; } /* Now change all / to .*/ ptr=fname; while (ptr && *ptr) { if (*ptr=='/') { *ptr='.'; } ptr++; } goto tryAgain; default: /*Failure...*/ goto skipDir; } } /*if status is 2, we successfully did a CWD*/ /*if status is 5 and we are at the root, assume we are on a vms machine and try to print out a directory*/ if (status == 2 || (status == 5 && !strcmp(filename,"/"))) { /* Successed : let's NLST it */ isDirectory = YES; usingNLST=1; sprintf(command, "NLST %s %c%c", NLST_PARAMS, CR, LF); status = response (command); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted while sending LIST\n"); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); free (filename); #ifdef SWP_HACK loading_length=(-1); #endif return HT_INTERRUPTED; } if (status==5) { /*unrecognized command or failed*/ isDirectory = YES; usingNLST=2; sprintf(command, "NLST%c%c", CR, LF); status = response (command); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted while sending NLST\n"); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); free (filename); #ifdef SWP_HACK loading_length=(-1); #endif return HT_INTERRUPTED; } } if (status==5) { /*unrecognized command or failed*/ isDirectory = YES; usingNLST=0; sprintf(command, "LIST%c%c", CR, LF); status = response (command); if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted while sending LIST\n"); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); free (filename); #ifdef SWP_HACK loading_length=(-1); #endif return HT_INTERRUPTED; } } } } skipDir: free(filename); free(fname); if (status != 1) { CLOSE_CONTROL (control); control = -1; close_master_socket (); #ifdef SWP_HACK loading_length=(-1); #endif return HT_NOT_LOADED; /* Action not started */ } } /* Wait for the connection */ { struct sockaddr_in soc_address; int soc_addrlen = sizeof(soc_address); #ifdef SOCKS status = Raccept(master_socket, #else status = accept(master_socket, #endif (struct sockaddr *)&soc_address, &soc_addrlen); if (status < 0) { CLOSE_CONTROL (control); control = -1; close_master_socket (); /* We can't interrupt out of an accept. */ #ifdef SWP_HACK loading_length=(-1); #endif return HT_NOT_LOADED; } #ifndef DISABLE_TRACE if(www2Trace) { fprintf(stderr, "FTP: Accepted new socket %d\n", status); } #endif data_soc = status; } if (isDirectory) { int s = read_directory (anchor, name, format_out, sink); #ifdef SWP_HACK loading_length=(-1); #endif close_master_socket (); NETCLOSE (data_soc); data_soc = -1; #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Returning %d after doing read_directory\n", s); #endif /* HT_INTERRUPTED should fall right through. */ return s; } else { /* We reproduce ParseSocket below because of socket/child process problem. */ HTStream * stream; HTStreamClass targetClass; int rv; stream = HTStreamStack(format, format_out, compressed, sink, anchor); if (!stream) { char buffer[1024]; /* @@@@@@@@ */ sprintf(buffer, "Sorry, can't convert from %s to %s.", HTAtom_name(format), HTAtom_name(format_out)); HTProgress (buffer); #ifndef DISABLE_TRACE if (www2Trace) fprintf(stderr, "FTP: %s\n", buffer); #endif #ifdef SWP_HACK loading_length=(-1); #endif return HT_NOT_LOADED; } targetClass = *(stream->isa); /* Copy pointers to procedures */ ftpKludge=1; rv = HTCopy(data_soc, stream, 0); ftpKludge=0; loading_length=(-1); if (rv == -1) { rv = HT_INTERRUPTED; } else { (*targetClass.end_document)(stream); /* Do NOT call *targetClass.free yet -- sockets aren't closed. */ rv = HT_LOADED; } #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Got back %d from our local equivalent of ParseSocket\n", rv); #endif /* Reset buffering to control connection -- probably no longer necessary, since we don't use a connection more than once. */ HTInitInput(control); #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Closing data socket %d\n", data_soc); #endif NETCLOSE (data_soc); data_soc = -1; /* Unfortunately, picking up the final reply sometimes causes serious problems. It *probably* isn't safe not to do this, as there is the possibility that FTP servers could die if they try to send it and we're not listening. Testcase for problems (10/30/93): uxc.cso.uiuc.edu, AnswerGarden COPYRIGHT in X11R5 contrib clients. Of course, we may already be triggering hostile actions by allowing client-side interrupts as follows... */ if (rv != HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Picking up final reply...\n"); #endif status = response (NIL); /* Pick up final reply */ if (status == HT_INTERRUPTED) { #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Interrupted picking up final reply.\n"); #endif HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); (*targetClass.handle_interrupt)(stream); return HT_INTERRUPTED; } if (status != 2) { CLOSE_CONTROL (control); control = -1; close_master_socket (); return HT_NOT_LOADED; } } close_master_socket (); if (rv != HT_INTERRUPTED) { /* WAIT until all sockets have been closed. */ #ifndef DISABLE_TRACE if (www2Trace) fprintf (stderr, "FTP: Calling free method, finally.\n"); #endif (*targetClass.free)(stream); } timer = XtAppAddTimeOut(app_context, ftp_timeout_val*1000, close_it_up, NULL); fTimerStarted = 1; return rv == HT_INTERRUPTED ? HT_INTERRUPTED : HT_LOADED; } } /* End HTFTPLoad */ /* HTFTPMkDir Request that a directory be created on the FTP site. ** Expects: *name is a pointer to a string that consists of the FTP URL with ** the remote directory name. ** Returns 0 if successful, nonzero on error */ PUBLIC int HTFTPMkDir ARGS1 ( char *, name ) { char *curpath, *path; char command[ LINE_LENGTH+1]; int status, method = 0; HTProgress ("FTP mkdir in progress"); if(fTimerStarted) { XtRemoveTimeOut (timer); fTimerStarted = 0; } /* Open a connection (or get a cached connection) to the FTP site */ status = get_connection (name); if (status < 0) { CLOSE_CONTROL (control); control = -1; return status; } /* The remote directory name is in the url, so pull it out i.e. ftp://warez.yomama.com/pub/31337&warez_boy means to make the directory warez_boy at ftp://warez.yomama.com/pub/31337 */ if ((path = strchr(name, '&')) == NULL) { /* No dirname in this URL */ close_master_socket (); CLOSE_CONTROL (control); control = -1; return -1; } *path = '\0'; /* Make the url normal */ path++; /* Move to the dirname */ /* *path is the directory name to create */ curpath = HTParse (name, "", PARSE_PATH+PARSE_PUNCTUATION); if (!curpath || !(*curpath)) curpath = strdup ("/"); /* *curpath is the remote directory in which to create *path */ /* First change to current directory on the server */ sprintf (command, "CWD %s%c%c", curpath, CR, LF); status = response (command); if (status != 2) { close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status = HT_INTERRUPTED) HTProgress ("Connection interrupted"); return (status==HT_INTERRUPTED)?-2:-1; } /* Now create the directory on the server */ sprintf (command, "MKD %s%c%c", path, CR, LF); status = response (command); if (status != 2) { close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status = HT_INTERRUPTED) HTProgress ("Connection interrupted"); return (status==HT_INTERRUPTED)?-2:-1; } /* Clean up */ close_master_socket (); CLOSE_CONTROL (control); control = -1; HTProgress ("Created remote directory."); return 0; } /* end HTFTPMkDir */ /* HTFTRemove Request that a file (or directory) be removed from the FTP site ** Expects: *name is a pointer to a string that consists of the FTP URL with the remote filename ** included. ** Returns 0 if successful, nonzero on error */ PUBLIC int HTFTPRemove ARGS1 ( char *, name ) { char *fname, *filename, *path; char command[ LINE_LENGTH+1]; int status, method = 0, didIt = 0; HTProgress ("FTP remove in progress"); if(fTimerStarted) { XtRemoveTimeOut (timer); fTimerStarted = 0; } /* Open a connection (or get a cached connection) to the FTP site */ status = get_connection (name); if (status < 0) { CLOSE_CONTROL (control); control = -1; return status; } /* Pull out the filename (and path) */ fname = HTParse (name, "", PARSE_PATH+PARSE_PUNCTUATION); if(!(*fname)) StrAllocCopy (filename, "/"); /* Pull out just the filename */ filename = strrchr (fname, '/'); filename++; if (!(*filename)) { /* No filename in the URL */ close_master_socket (); CLOSE_CONTROL (control); control = -1; return -1; } /* *fname is the full path to the file, *filename is just the filename */ for (method =0; method < 2; method++ ) { switch (method) { /* First, attempt to CWD to fname, if successful, fname is a directory. So, CDUP to get to the parent and call RMD on filename */ case 0: sprintf (command, "CWD %s%c%c", fname, CR, LF); status = response (command); if (status != 2) { if (status == 5) { /* Not a directory */ continue; } close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status == HT_INTERRUPTED) HTProgress ("Connection interrupted."); return (status == HT_INTERRUPTED)?-2:-1; } /* Must be a directory, move up and RMD it*/ *(filename-1) = 0; /* Make fname -> just the path of the parent directory */ sprintf (command, "CWD %s%c%c", fname, CR, LF); status = response (command); if (status != 2) { close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status == HT_INTERRUPTED) HTProgress ("Connection interrupted."); return (status == HT_INTERRUPTED)?-2:-1; } sprintf (command, "RMD %s%c%c", filename, CR, LF); status = response (command); if (status != 2) { close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status == HT_INTERRUPTED) HTProgress ("Connection interrupted."); return (status == HT_INTERRUPTED)?-2:status; } didIt = 1; break; /* If the first attempt failed, CWD to fname and DELE filename */ case 1: *(filename-1) = 0; /* Make fname -> just the path of the file */ sprintf (command, "CWD %s%c%c", fname, CR, LF); status = response (command); if (status != 2) { close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status == HT_INTERRUPTED) { HTProgress ("Connection interrupted."); } return (status == HT_INTERRUPTED)?HT_INTERRUPTED:status; } sprintf (command, "DELE %s%c%c", filename, CR, LF); status = response (command); if (status != 2) { close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status == HT_INTERRUPTED) { HTProgress ("Connection interrupted."); } return (status == HT_INTERRUPTED)?-2:status; } didIt = 1; break; } /* end of switch (method) */ if(didIt) break; } /* end for(method.. */ /* Clean up */ close_master_socket (); CLOSE_CONTROL (control); control = -1; return 0; } /* end HTFTPRemove */ /* HTFTPSend Send File to the FTP site ** Expects: *name is a pointer to a string that consists of the FTP URL with the local filename ** appended to the URL (delimited by an &. i.e. ftp://warez.mama.com/pub&/tmp/bubba.tgz ** would send /tmp/bubba.tgz to warez.mama.com:/pub ** Returns 0 if successful, nonzero on error */ #define OUTBUFSIZE 4096 /* Size of the chunk of the file read */ PUBLIC int HTFTPSend ARGS1 ( char *, name ) { int status; FILE *f; char *fname, *filename, *path; char command[ LINE_LENGTH+1], outBuf[ OUTBUFSIZE+1]; long bLeft, bDone, bTotal, chunk; extern int twirl_increment; int next_twirl = twirl_increment, intr = 0; struct sockaddr_in soc_address; int soc_addrlen = sizeof (soc_address); struct stat sbuf; HTProgress ("FTP send in progress."); if (fTimerStarted) { XtRemoveTimeOut (timer); fTimerStarted = 0; } /* The local filename is in the url, so pull it out i.e. ftp://warez.yomama.com/pub/31337&/u/warezboy/Mosaic0.1a.tar.gz means to send /u/warezboy/Mosaic0.1a.tar.gz to warez.yomama.com/pub/31337 */ if ((fname = strchr(name, '&')) == NULL) { /* No local filename in this URL */ close_master_socket (); CLOSE_CONTROL (control); control = -1; return -1; } *fname = '\0'; /* Make the url normal */ fname++; /* Move to the filename */ filename = strrchr (fname, '/'); filename++; if (!(*filename)) { /* no filename */ close_master_socket (); CLOSE_CONTROL (control); control = -1; return -1; } /* *fname is the full path and filename, *filename is just the filename */ /* get size information */ if( stat (fname, &sbuf) < 0) { CLOSE_CONTROL (control); control = -1; close_master_socket (); return -1; } bTotal = sbuf.st_size; #ifndef DISABLE_TRACE if(www2Trace) fprintf (stderr, "HTFTPSend: Attempting to send %s (%s) (%lu)\n", fname, filename, bTotal); #endif status = get_connection (name); if (status<0) { CLOSE_CONTROL (control); control = -1; close_master_socket (); return status; } status = get_listen_socket (); if (status<0) { CLOSE_CONTROL (control); control = -1; close_master_socket (); return status; } status = response (port_command); if (status == HT_INTERRUPTED) { HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); return -2; } if (status != 2) { /* If the port command was not successful */ CLOSE_CONTROL (control); control = -1; close_master_socket (); if (status < 0) { /*If we were going to try again, we would do it here.... */ return -3; } return -3; } /* Logged in, set up the port, now let's send the sucka */ /* Set the type to image */ sprintf (command, "TYPE %s%c%c", "I", CR, LF); status = response (command); if (status != 2) { close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status == HT_INTERRUPTED) HTProgress ("Connection interrupted."); return (status == HT_INTERRUPTED)?-2:-1; } /* Move to correct directory */ path = HTParse (name, "", PARSE_PATH+PARSE_PUNCTUATION); if (!(*path)) StrAllocCopy (path, "/"); sprintf (command, "CWD %s%c%c", path, CR, LF); status = response (command); if (status != 2) { close_master_socket (); CLOSE_CONTROL (control); control = -1; if (status == HT_INTERRUPTED) HTProgress ("Connection interrupted."); return (status == HT_INTERRUPTED)?-2:-1; } /* Send it */ sprintf (command, "STOR %s%c%c", filename, CR, LF); status = response (command); if (status == HT_INTERRUPTED) { HTProgress ("Connection interrupted."); CLOSE_CONTROL (control); control = -1; close_master_socket (); return -2; } if (status != 1) { /* Does not seem to understand the STOR command */ HTProgress ("FTP host does not understand STOR command."); CLOSE_CONTROL (control); control = -1; close_master_socket (); return -3; } /* Ready to send the data now, server is primed and ready... here we go, go go */ #ifdef SOCKS status = Raccept (master_socket, (struct sockaddr *)&soc_address, &soc_addrlen); #else status = accept (master_socket, (struct sockaddr *)&soc_address, &soc_addrlen); #endif if (status < 0) { CLOSE_CONTROL (control); control = -1; close_master_socket (); return -2; } data_soc = status; /* Server has contacted us... send them data */ /* Send the data! */ if( (f = fopen( fname, "r")) == NULL) { CLOSE_CONTROL (control); control = -1; close_master_socket (); return -1; } HTMeter (0,NULL); bDone = 0; bLeft = bTotal; mo_busy (); for (;;) { if (bDone > next_twirl) { intr = HTCheckActiveIcon(1); next_twirl += twirl_increment; } else { intr = HTCheckActiveIcon(0); } if (intr) { HTProgress ("Data transfer interrupted"); HTMeter (100,NULL); break; } if (bLeft < OUTBUFSIZE) { /* Handle last block */ if ((chunk = fread (outBuf, 1, bLeft, f)) == 0) break; NETWRITE (data_soc, outBuf, chunk); bLeft -= chunk; bDone += chunk; } else if (bLeft <= 0) { /* Exit */ break; } else { /* Handle a block of the data */ if ( (chunk = fread (outBuf, 1, OUTBUFSIZE, f)) == 0) break; NETWRITE (data_soc, outBuf, chunk); bLeft -= chunk; bDone += chunk; } HTMeter ((bDone*100)/bTotal, NULL); } mo_not_busy (); /* Done, now clean up */ fclose (f); HTMeter (100, NULL); CLOSE_CONTROL (control); control = -1; close_master_socket(); #ifndef DISABLE_TRACE if(www2Trace) fprintf (stderr, "HTFTPSend: Closing data socket\n"); #endif NETCLOSE (data_soc); data_soc = -1; if (bLeft != 0) { #ifndef DISABLE_TRACE if(www2Trace) fprintf (stderr, "HTFTPSend: Error sending file %lu bytes left\n", bLeft); #endif return intr?-2:-1; } timer = XtAppAddTimeOut(app_context, ftp_timeout_val*1000, close_it_up, NULL); fTimerStarted = 1; #ifndef DISABLE_TRACE if(www2Trace) fprintf (stderr, "HTFTPSend: File sent, returning OK\n"); #endif return 0; } /* End of HTFTPSend */ CLOSE_CONTROL(s) int s; { NETCLOSE(s); ftpcache.control = -1; } void close_it_up() { NETCLOSE(ftpcache.control); ftpcache.control = -1; } #ifdef NEW_PARSE /* * This code based off of Rick Vestal's FTP parse code for the NCSA Windows * Mosaic client. * * Modified for X by Scott Powers * 9.27.95 */ int ParseFileSizeAndName(char *szBuffer, char *szFileName, char *szSize) { char *szPtr,*szName,*szEndPtr,*szLength; static char *tmpbuf=NULL; if (!szBuffer) { return(0); } if (!tmpbuf) { tmpbuf=(char *)calloc(BUFSIZ,sizeof(char)); } if (usingNLST==1) { strcpy(tmpbuf,szBuffer); /*filename*/ szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } *(szPtr+1)='\0'; if (szPtr && *szPtr=='>') { /*deal with a link*/ if (szPtr) { szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } *(szPtr+1)='\0'; } if (szPtr) { szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } *(szPtr+1)='\0'; } } if (szPtr) { /*year/time*/ szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } } *(szPtr+1)='\0'; if (szPtr) { /*date*/ szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } } *(szPtr+1)='\0'; if (szPtr) { /*month*/ szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } } *(szPtr+1)='\0'; if (szPtr) { /*filesize*/ szPtr=strrchr(tmpbuf,' '); } /*beginning of filesize*/ szPtr++; /* szSize=szBuffer+(szPtr-tmpbuf); */ strcpy(szSize,szPtr); } else { szPtr = strrchr(szBuffer, ' '); szName = szPtr + 1; if (szPtr) { strcpy(szFileName, szName); } /* go to end of file length */ while (szPtr && *szPtr == ' ') { szPtr--; } szEndPtr = szPtr+1; if (*szPtr != '>') { while (szPtr && *szPtr != ' ') { szPtr--; } if (szPtr) { szLength = szPtr+1; strncpy(szSize, szLength, szEndPtr - szLength); szSize[szEndPtr - szLength] = '\0'; } } else { return(0); /* a directory */ } } return(1); /* not a directory */ } int ParseDate(char *szBuffer, char *szFileInfo, char *szMonth, char *szDay, char *szYear, char *szTime) { char *szPtr,*szEndPtr; int nCount; char *tmpbuf=(char *)calloc(BUFSIZ,sizeof(char)); if (!szBuffer) { free(tmpbuf); return(0); } if ( (*szBuffer != 'd') && (*szBuffer != 'l') && (*szBuffer != '-')) { /* Hopefully this is the dos format */ szPtr = szBuffer; strncpy(szMonth, szBuffer, 8); szMonth[8] = '\0'; szPtr = szPtr + 10; if (szPtr) { strncpy(szTime, szPtr, 7); szTime[7] = '\0'; } szPtr = szPtr + 15; if (szPtr) { if (*szPtr == 'D') { *szDay = 'd'; szDay[1] = '\0'; } else { *szDay = 'f'; szDay[1] = '\0'; } } free(tmpbuf); return(3); /* ie the info is this dos way */ } else { szPtr = NULL; nCount = 0; /* alright, use this ugly loop to go to each of the month, day, year, whatever parts */ while (szPtr || ((nCount == 0) && szBuffer)) { switch (nCount) { case 0: /* file info */ strncpy(szFileInfo, szBuffer, 10); szFileInfo[10] = '\0'; if (usingNLST==1) { strcpy(tmpbuf,szBuffer); /*filename*/ szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } *(szPtr+1)='\0'; if (szPtr && *szPtr=='>') { /*deal with a link*/ if (szPtr) { szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } *(szPtr+1)='\0'; } if (szPtr) { szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } *(szPtr+1)='\0'; } } if (szPtr) { /*year/time*/ szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } } *(szPtr+1)='\0'; if (szPtr) { /*date*/ szPtr=strrchr(tmpbuf,' '); while (szPtr && (*szPtr == ' ')) { szPtr--; } } *(szPtr+1)='\0'; if (szPtr) { /*month*/ szPtr=strrchr(tmpbuf,' '); } /*beginning of month*/ szPtr++; szPtr=szBuffer+(szPtr-tmpbuf); } else { szPtr = strchr(szBuffer, ' '); while (szPtr && (*szPtr == ' ')) { szPtr++; } if (szPtr) { szPtr = strchr(szPtr, ' '); while (szPtr && (*szPtr == ' ')) { szPtr++; } } if (szPtr) { szPtr = strchr(szPtr, ' '); while (szPtr && (*szPtr == ' ')) { szPtr++; } } if (szPtr) { szPtr = strchr(szPtr, ' '); while (szPtr && (*szPtr == ' ')) { szPtr++; } } if (szPtr) { szPtr = strchr(szPtr, ' '); while (szPtr && (*szPtr == ' ')) { szPtr++; } } /* now we are at the month entry */ } break; case 1: szEndPtr = strchr(szPtr, ' '); if (szEndPtr) { strncpy(szMonth, szPtr, szEndPtr - szPtr); szMonth[szEndPtr - szPtr] = '\0'; szPtr = szEndPtr+1; /* go to next entry (day) */ while (szPtr && (*szPtr == ' ')) { szPtr++; } } else { strcpy(szMonth, " "); } break; case 2: szEndPtr = strchr(szPtr, ' '); if (szEndPtr) { strncpy(szDay, szPtr, szEndPtr - szPtr); szDay[szEndPtr - szPtr] = '\0'; szPtr = szEndPtr+1; while (szPtr && (*szPtr == ' ')) { szPtr++; } } else { strcpy(szDay, " "); } break; case 3: szEndPtr = strchr(szPtr, ' '); if (szEndPtr) { strncpy(szYear, szPtr, szEndPtr - szPtr); szYear[szEndPtr - szPtr] = '\0'; szPtr = szEndPtr+1; } else if (szEndPtr) { strcpy(szYear, " "); } break; case 4: szPtr = NULL; } nCount++; } szPtr = strchr(szYear, ':'); if (!szPtr) { free(tmpbuf); return(0); /* ie the info is month, day, year */ } szPtr -= 2; /* beginning of time; */ strncpy(szTime, szPtr, 5); szTime[5] = '\0'; free(tmpbuf); return(1); /* ie the info is month, day, time */ } } #endif