2827 lines
69 KiB
C
2827 lines
69 KiB
C
/* 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 <timbl@info.cern.ch>
|
|
** DD Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
|
|
** 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, <ylee@syl.dl.nec.com>
|
|
* NEC Systems Laboratory
|
|
* C&C Software Technology Center
|
|
*/
|
|
#include "../config.h"
|
|
#include <X11/Intrinsic.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "HTFTP.h" /* Implemented here */
|
|
#include "../libnut/str-tools.h"
|
|
#define LINE_LENGTH 1024
|
|
|
|
#include "HTAlert.h"
|
|
#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"
|
|
|
|
#include "../src/mosaic.h"
|
|
#include "../src/gui.h"
|
|
#include "../src/gui-dialogs.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 <stdlib.h>
|
|
#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;
|
|
|
|
void CLOSE_CONTROL(int);
|
|
|
|
/*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;
|
|
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, "<PRE>\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, "</PRE><HR>\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, "<H2>FTP login using username \"");
|
|
HText_appendText(HT, username);
|
|
HText_appendText(HT, "\" failed.</H2><BR>");
|
|
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.<BR>");
|
|
HText_appendText(HT, "To do this you have to specify ");
|
|
HText_appendText(HT, "the FTP line like this:<BR>");
|
|
HText_appendText(HT, "<P>");
|
|
HText_appendText(HT, "ftp://username:password@ftp_site/");
|
|
HText_appendText(HT, "<P>");
|
|
HText_appendText(HT, "<strong>OR</strong>");
|
|
HText_appendText(HT, "<P>");
|
|
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, "<P>");
|
|
HText_appendText(HT, "e.g. ftp://username@ftp_site/");
|
|
}
|
|
} else {
|
|
|
|
char buf[BUFSIZ];
|
|
|
|
if (redial<ftpRedial) {
|
|
/*close down current connection*/
|
|
ftpcache.control = -1;
|
|
CLOSE_CONTROL(control);
|
|
control = -1;
|
|
|
|
/*tell them in the progress string*/
|
|
sprintf(buf,"Login failed. Redial Attempt %d/%d. Sleeping %d seconds.",redial,ftpRedial,ftpRedialSleep);
|
|
HTProgress(buf);
|
|
|
|
/*if we're tracing, explain it all*/
|
|
#ifndef DISABLE_TRACE
|
|
if (www2Trace)
|
|
fprintf(stderr, "FTP: Login fail: %s", response_text);
|
|
#endif
|
|
/*
|
|
sleep(ftpRedialSleep);
|
|
*/
|
|
|
|
/* Commented out until we get a new "sleep" routine...SWP
|
|
|
|
if (my_sleep(ftpRedialSleep,1)) {
|
|
if (www2Trace)
|
|
#ifndef DISABLE_TRACE
|
|
fprintf (stderr,
|
|
"FTP: Interrupted in sleep during redial attempt.\n");
|
|
#endif
|
|
#ifdef SWP_HACK
|
|
loading_length=(-1);
|
|
#endif
|
|
HTProgress ("Connection interrupted.");
|
|
CLOSE_CONTROL(control);
|
|
control = -1;
|
|
return HT_INTERRUPTED;
|
|
}
|
|
|
|
*/
|
|
|
|
/*index redial and try again*/
|
|
redial++;
|
|
goto redialFTP;
|
|
}
|
|
|
|
/*Printout message and stop retrying*/
|
|
sprintf(buf,"<H2>Anonymous FTP login failed.<br><br>There were %d redial attempts made.</h2>",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<hr><p>Reason for Failure:<br><br><plaintext>\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;
|
|
socklen_t 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 itemtype;
|
|
char itemname[BUFSIZ];
|
|
char itemsize[BUFSIZ];
|
|
char *full_ftp_name, *ptr;
|
|
int count, ret, cmpr, c='\0', 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;
|
|
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) 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[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 (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);
|
|
}
|
|
|
|
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;
|
|
|
|
socklen_t 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 = 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;
|
|
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;
|
|
socklen_t 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 */
|
|
|
|
|
|
void 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
|
|
|