Index: squid/errors/English/ERR_FTP_FAILURE diff -u squid/errors/English/ERR_FTP_FAILURE:1.1.1.1 squid/errors/English/ERR_FTP_FAILURE:1.1.1.1.6.2 --- squid/errors/English/ERR_FTP_FAILURE:1.1.1.1 Fri Nov 21 12:29:37 1997 +++ squid/errors/English/ERR_FTP_FAILURE Sat Nov 22 00:03:41 1997 @@ -17,3 +17,7 @@
%F+ +
+This could be caused by a non RFC-1738 compliant FTP URL with a absolute path. +If this is the cause, then the file can be found at %u. Index: squid/src/errorpage.c diff -u squid/src/errorpage.c:1.1.1.2 squid/src/errorpage.c:1.1.1.2.4.1 --- squid/src/errorpage.c:1.1.1.2 Fri Nov 21 12:36:35 1997 +++ squid/src/errorpage.c Fri Nov 21 20:04:41 1997 @@ -215,9 +215,10 @@ * d - seconds elapsed since request received * e - errno x * E - strerror() x - * F - FTP reply line x * f - FTP request line x + * F - FTP reply line x * h - cache hostname x + * H - server host name x * i - client IP address x * I - server IP address x * L - HREF link for more info/contact x @@ -226,6 +227,8 @@ * P - Protocol x * t - local time x * T - UTC x + * U - URL without password x + * u - URL without password, %2f added to path x * w - cachemgr email address x * z - dns server error message x */ @@ -299,6 +302,9 @@ break; case 'U': p = r ? urlCanonicalClean(r) : err->url; + break; + case 'u': + p = r ? urlCanonicalClean2f(r) : err->url; break; case 'w': if (Config.adminEmail) { Index: squid/src/ftp.c diff -u squid/src/ftp.c:1.1.1.3 squid/src/ftp.c:1.1.1.3.2.4 --- squid/src/ftp.c:1.1.1.3 Fri Nov 21 13:03:02 1997 +++ squid/src/ftp.c Sat Nov 22 00:09:41 1997 @@ -30,6 +30,11 @@ #include "squid.h" +/* Define FTP_SLASH_HACK to 0 to disable */ +#ifndef FTP_SLASH_HACK +#define FTP_SLASH_HACK 1 +#endif + enum { FTP_ISDIR, FTP_PASV_SUPPORTED, @@ -43,7 +48,10 @@ FTP_ROOT_DIR, FTP_NO_DOTDOT, FTP_HTML_HEADER_SENT, - FTP_MAYBE_TRY_SLASH_HACK + FTP_BINARY, +#if FTP_SLASH_HACK + FTP_TRY_SLASH_HACK +#endif }; static const char *const crlf = "\r\n"; @@ -78,7 +86,6 @@ int conn_att; int login_att; ftp_state_t state; - char *errmsg; time_t mdtm; int size; int flags; @@ -89,6 +96,8 @@ char *proxy_host; size_t list_width; wordlist *cwd_message; + char *old_request; + char *old_reply; struct { int fd; char *buf; @@ -132,31 +141,45 @@ static CWCB ftpWriteCommandCallback; static char *ftpGetBasicAuth(const char *); static void ftpLoginParser(const char *, FtpStateData *); -static void ftpFail(FtpStateData * ftpState); static wordlist *ftpParseControlReply(char *buf, size_t len, int *code); -static void ftpSendPasv(FtpStateData * ftpState); -static void ftpSendCwd(FtpStateData * ftpState); -static void ftpSendPort(FtpStateData * ftpState); static void ftpRestOrList(FtpStateData * ftpState); -static void ftpReadQuit(FtpStateData * ftpState); static void ftpDataTransferDone(FtpStateData * ftpState); static void ftpAppendSuccessHeader(FtpStateData * ftpState); static char *ftpAuthRequired(const request_t *, const char *); static STABH ftpAbort; +/* State machine functions + * send == state transition + * read == wait for response, and select next state transition + */ static FTPSM ftpReadWelcome; +static FTPSM ftpSendUser; static FTPSM ftpReadUser; +static FTPSM ftpSendPass; static FTPSM ftpReadPass; +static FTPSM ftpSendType; static FTPSM ftpReadType; +static FTPSM ftpSendMdtm; static FTPSM ftpReadMdtm; +static FTPSM ftpSendSize; static FTPSM ftpReadSize; +static FTPSM ftpSendPort; static FTPSM ftpReadPort; +static FTPSM ftpSendPasv; static FTPSM ftpReadPasv; +static FTPSM ftpSendCwd; static FTPSM ftpReadCwd; +static FTPSM ftpSendList; +static FTPSM ftpSendNlst; static FTPSM ftpReadList; +static FTPSM ftpSendRest; static FTPSM ftpReadRest; +static FTPSM ftpSendRetr; static FTPSM ftpReadRetr; static FTPSM ftpReadTransferDone; +static FTPSM ftpSendQuit; +static FTPSM ftpReadQuit; +static FTPSM ftpFail; FTPSM *FTP_SM_FUNCS[] = { @@ -203,6 +226,8 @@ wordlistDestroy(&ftpState->cwd_message); safe_free(ftpState->ctrl.last_reply); safe_free(ftpState->ctrl.last_command); + safe_free(ftpState->old_request); + safe_free(ftpState->old_reply); safe_free(ftpState->title_url); safe_free(ftpState->filepath); safe_free(ftpState->data.host); @@ -1102,20 +1127,26 @@ if (ftpState->ctrl.message) if (strstr(ftpState->ctrl.message->key, "NetWare")) EBIT_SET(ftpState->flags, FTP_SKIP_WHITESPACE); - if (ftpState->proxy_host != NULL) - snprintf(cbuf, 1024, "USER %s@%s\r\n", - ftpState->user, - ftpState->request->host); - else - snprintf(cbuf, 1024, "USER %s\r\n", ftpState->user); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_USER; + ftpSendUser(ftpState); } else { ftpFail(ftpState); } } static void +ftpSendUser(FtpStateData *ftpState) +{ + if (ftpState->proxy_host != NULL) + snprintf(cbuf, 1024, "USER %s@%s\r\n", + ftpState->user, + ftpState->request->host); + else + snprintf(cbuf, 1024, "USER %s\r\n", ftpState->user); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_USER; +} + +static void ftpReadUser(FtpStateData * ftpState) { int code = ftpState->ctrl.replycode; @@ -1123,35 +1154,50 @@ if (code == 230) { ftpReadPass(ftpState); } else if (code == 331) { - snprintf(cbuf, 1024, "PASS %s\r\n", ftpState->password); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_PASS; + ftpSendPass(ftpState); } else { ftpFail(ftpState); } } static void +ftpSendPass(FtpStateData * ftpState) +{ + snprintf(cbuf, 1024, "PASS %s\r\n", ftpState->password); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_PASS; +} + +static void ftpReadPass(FtpStateData * ftpState) { int code = ftpState->ctrl.replycode; - char *t; - char *filename; - char mode; debug(9, 3) ("ftpReadPass\n"); if (code == 230) { - t = strrchr(ftpState->request->urlpath, '/'); - filename = t ? t + 1 : ftpState->request->urlpath; - mode = mimeGetTransferMode(filename); - snprintf(cbuf, 1024, "TYPE %c\r\n", mode); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_TYPE; + ftpSendType(ftpState); } else { ftpFail(ftpState); } } static void +ftpSendType(FtpStateData *ftpState) +{ + char *t; + char *filename; + char mode; + + t = strrchr(ftpState->request->urlpath, '/'); + filename = t ? t + 1 : ftpState->request->urlpath; + mode = mimeGetTransferMode(filename); + if ( mode == 'I' ) + EBIT_SET(ftpState->flags, FTP_BINARY); + snprintf(cbuf, 1024, "TYPE %c\r\n", mode); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_TYPE; +} + +static void ftpReadType(FtpStateData * ftpState) { int code = ftpState->ctrl.replycode; @@ -1171,11 +1217,9 @@ T = &w->next; } xfree(path); - if (ftpState->pathcomps) { - if (*ftpState->pathcomps->key != '/') - EBIT_SET(ftpState->flags, FTP_MAYBE_TRY_SLASH_HACK); + if (ftpState->pathcomps) ftpSendCwd(ftpState); - } else + else ftpSendPasv(ftpState); } else { ftpFail(ftpState); @@ -1214,7 +1258,6 @@ int code = ftpState->ctrl.replycode; size_t len = 0; wordlist *w; - char *t; debug(9, 3) ("This is ftpReadCwd\n"); w = ftpState->pathcomps; assert(w != NULL); @@ -1224,43 +1267,33 @@ ftpState->cwd_message = ftpState->ctrl.message; ftpState->ctrl.message = NULL; /* CWD OK */ - EBIT_CLR(ftpState->flags, FTP_MAYBE_TRY_SLASH_HACK); ftpState->pathcomps = w->next; xfree(w->key); xfree(w); ftpSendCwd(ftpState); - } else if (EBIT_TEST(ftpState->flags, FTP_MAYBE_TRY_SLASH_HACK)) { - EBIT_CLR(ftpState->flags, FTP_MAYBE_TRY_SLASH_HACK); - len = strlen(w->key) + 2; - t = xmalloc(len); - snprintf(t, len, "/%s", w->key); - xfree(w->key); - w->key = t; - ftpSendCwd(ftpState); - } else if (EBIT_TEST(ftpState->flags, FTP_ISDIR)) { + } else if (EBIT_TEST(ftpState->flags, FTP_ISDIR) || w->next) { /* CWD FAILED */ ftpFail(ftpState); } else { - /* CWD FAILED */ - while (w) { - len += (strlen(w->key) + 1); - w = w->next; - } + /* Final CWD FAILED */ + len = (strlen(w->key) + 1); ftpState->filepath = xcalloc(len, 1); - for (w = ftpState->pathcomps; w; w = w->next) { - strcat(ftpState->filepath, w->key); - if (w->next) - strcat(ftpState->filepath, "/"); - } + strcat(ftpState->filepath, w->key); wordlistDestroy(&ftpState->pathcomps); - assert(*ftpState->filepath != '\0'); - snprintf(cbuf, 1024, "MDTM %s\r\n", ftpState->filepath); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_MDTM; + ftpSendMdtm(ftpState); } } static void +ftpSendMdtm(FtpStateData *ftpState) +{ + assert(*ftpState->filepath != '\0'); + snprintf(cbuf, 1024, "MDTM %s\r\n", ftpState->filepath); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_MDTM; +} + +static void ftpReadMdtm(FtpStateData * ftpState) { int code = ftpState->ctrl.replycode; @@ -1270,11 +1303,23 @@ } else if (code < 0) { ftpFail(ftpState); } - assert(ftpState->filepath != NULL); - assert(*ftpState->filepath != '\0'); - snprintf(cbuf, 1024, "SIZE %s\r\n", ftpState->filepath); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_SIZE; + ftpSendSize(ftpState); +} + +static void +ftpSendSize(FtpStateData *ftpState) +{ + /* Only send SIZE for binary transfers. The returned size + * is useless on ASCII transfers */ + if (!EBIT_TEST(ftpState->flags, FTP_BINARY)) { + assert(ftpState->filepath != NULL); + assert(*ftpState->filepath != '\0'); + snprintf(cbuf, 1024, "SIZE %s\r\n", ftpState->filepath); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_SIZE; + } else + /* Skip to next state no non-binary transfers */ + ftpSendPasv(ftpState); } static void @@ -1399,20 +1444,20 @@ ftpRestOrList(FtpStateData * ftpState) { debug(9, 3) ("This is ftpRestOrList\n"); - if (EBIT_TEST(ftpState->flags, FTP_ISDIR)) { - snprintf(cbuf, 1024, "LIST\r\n"); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_LIST; - } else if (ftpState->restart_offset > 0) { - snprintf(cbuf, 1024, "REST %d\r\n", ftpState->restart_offset); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_REST; - } else { - assert(ftpState->filepath != NULL); - snprintf(cbuf, 1024, "RETR %s\r\n", ftpState->filepath); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_RETR; - } + if (EBIT_TEST(ftpState->flags, FTP_ISDIR)) + ftpSendList(ftpState); + else if (ftpState->restart_offset > 0) + ftpSendRest(ftpState); + else + ftpSendRetr(ftpState); +} + +static void +ftpSendRest(FtpStateData * ftpState) +{ + snprintf(cbuf, 1024, "REST %d\r\n", ftpState->restart_offset); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_REST; } static void @@ -1422,10 +1467,7 @@ debug(9, 3) ("This is ftpReadRest\n"); assert(ftpState->restart_offset > 0); if (code == 350) { - assert(ftpState->filepath != NULL); - snprintf(cbuf, 1024, "RETR %s\r\n", ftpState->filepath); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_RETR; + ftpSendRetr(ftpState); } else if (code > 0) { debug(9, 3) ("ftpReadRest: REST not supported\n"); EBIT_CLR(ftpState->flags, FTP_REST_SUPPORTED); @@ -1435,6 +1477,14 @@ } static void +ftpSendList(FtpStateData * ftpState) +{ + snprintf(cbuf, 1024, "LIST\r\n"); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_LIST; +} + +static void ftpReadList(FtpStateData * ftpState) { int code = ftpState->ctrl.replycode; @@ -1454,10 +1504,7 @@ commSetTimeout(ftpState->data.fd, Config.Timeout.read, ftpTimeout, ftpState); return; } else if (!EBIT_TEST(ftpState->flags, FTP_TRIED_NLST)) { - EBIT_SET(ftpState->flags, FTP_TRIED_NLST); - snprintf(cbuf, 1024, "NLST\r\n"); - ftpWriteCommand(cbuf, ftpState); - ftpState->state = SENT_NLST; + ftpSendNlst(ftpState); } else { ftpFail(ftpState); return; @@ -1465,6 +1512,24 @@ } static void +ftpSendNlst(FtpStateData * ftpState) +{ + EBIT_SET(ftpState->flags, FTP_TRIED_NLST); + snprintf(cbuf, 1024, "NLST\r\n"); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_NLST; +} + +static void +ftpSendRetr(FtpStateData * ftpState) +{ + assert(ftpState->filepath != NULL); + snprintf(cbuf, 1024, "RETR %s\r\n", ftpState->filepath); + ftpWriteCommand(cbuf, ftpState); + ftpState->state = SENT_RETR; +} + +static void ftpReadRetr(FtpStateData * ftpState) { int code = ftpState->ctrl.replycode; @@ -1509,6 +1574,12 @@ comm_close(ftpState->data.fd); ftpState->data.fd = -1; } + ftpSendQuit(ftpState); +} + +static void +ftpSendQuit(FtpStateData *ftpState) +{ assert(ftpState->ctrl.fd > -1); snprintf(cbuf, 1024, "QUIT\r\n"); ftpWriteCommand(cbuf, ftpState); @@ -1521,15 +1592,64 @@ comm_close(ftpState->ctrl.fd); } +#if FTP_SLASH_HACK +static void +ftpTrySlashHack(FtpStateData * ftpState) +{ + char *path; + EBIT_SET(ftpState->flags, FTP_TRY_SLASH_HACK); + /* Free old paths */ + if (ftpState->pathcomps) + wordlistDestroy(&ftpState->pathcomps); + safe_free(ftpState->filepath); + /* Build the new path (urlpath begins with /)*/ + path=xstrdup(ftpState->request->urlpath); + rfc1738_unescape(path); + ftpState->filepath=path; + /* And off we go */ + ftpSendMdtm(ftpState); +} +#endif + static void ftpFail(FtpStateData * ftpState) { ErrorState *err; debug(9, 3) ("ftpFail\n"); +#if FTP_SLASH_HACK + /* Try the / hack to support "Netscape" FTP URL's + * only if we failed on CWD, RETR, MDTM or SIZE and !IS_DIR */ + if ( !EBIT_TEST(ftpState->flags, FTP_ISDIR) && + !EBIT_TEST(ftpState->flags, FTP_TRY_SLASH_HACK) ) { + switch(ftpState->state) { + case SENT_CWD: + case SENT_RETR: + case SENT_MDTM: + case SENT_SIZE: + /* Try the / hack */ + /* 1. Save old error message */ + ftpState->old_request=ftpState->ctrl.last_command; + ftpState->ctrl.last_command=NULL; + ftpState->old_reply=ftpState->ctrl.last_reply; + ftpState->ctrl.last_reply=NULL; + /* 2. Start hack */ + ftpTrySlashHack(ftpState); + return; + default: + break; + } + } +#endif err = errorCon(ERR_FTP_FAILURE, HTTP_INTERNAL_SERVER_ERROR); err->request = requestLink(ftpState->request); - err->ftp.request = ftpState->ctrl.last_command; - err->ftp.reply = ftpState->ctrl.last_reply; + if ( ftpState->old_request) + err->ftp.request = ftpState->old_request; + else + err->ftp.request = ftpState->ctrl.last_command; + if ( ftpState->old_reply) + err->ftp.reply = ftpState->old_reply; + else + err->ftp.reply = ftpState->ctrl.last_reply; errorAppendEntry(ftpState->entry, err); storeAbort(ftpState->entry, 0); comm_close(ftpState->ctrl.fd); Index: squid/src/protos.h diff -u squid/src/protos.h:1.1.1.3 squid/src/protos.h:1.1.1.3.2.1 --- squid/src/protos.h:1.1.1.3 Fri Nov 21 13:03:06 1997 +++ squid/src/protos.h Fri Nov 21 20:04:41 1997 @@ -529,6 +529,7 @@ extern int urlDefaultPort(protocol_t p); extern char *urlClean(char *); extern char *urlCanonicalClean(const request_t *); +extern char *urlCanonicalClean2f(const request_t *); extern void useragentOpenLog(void); extern void useragentRotateLog(void); Index: squid/src/url.c diff -u squid/src/url.c:1.1.1.1 squid/src/url.c:1.1.1.1.6.1 --- squid/src/url.c:1.1.1.1 Fri Nov 21 12:29:38 1997 +++ squid/src/url.c Fri Nov 21 20:04:41 1997 @@ -303,6 +303,7 @@ { LOCAL_ARRAY(char, buf, MAX_URL); LOCAL_ARRAY(char, portbuf, 32); + LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ+1); char *t; switch (request->method) { case METHOD_CONNECT: @@ -312,10 +313,54 @@ portbuf[0] = '\0'; if (request->port != urlDefaultPort(request->protocol)) snprintf(portbuf, 32, ":%d", request->port); - snprintf(buf, MAX_URL, "%s://%s%s%s", + loginbuf[0] = '\0'; + if (strlen(request->login) > 0 ) { + strcpy(loginbuf,request->login); + if ((t = strchr(loginbuf, ':'))) + *t = '\0'; + strcat(loginbuf,"@"); + } + snprintf(buf, MAX_URL, "%s://%s%s%s%s", ProtocolStr[request->protocol], + loginbuf, request->host, portbuf, + request->urlpath); + if ((t = strchr(buf, '?'))) + *t = '\0'; + break; + } + return buf; +} + +char * +urlCanonicalClean2f(const request_t * request) +{ + LOCAL_ARRAY(char, buf, MAX_URL); + LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ+1); + LOCAL_ARRAY(char, portbuf, 32); + char *t; + switch (request->method) { + case METHOD_CONNECT: + snprintf(buf, MAX_URL, "%s:%d", request->host, request->port); + break; + default: + portbuf[0] = '\0'; + if (request->port != urlDefaultPort(request->protocol)) + snprintf(portbuf, 32, ":%d", request->port); + loginbuf[0] = '\0'; + if (strlen(request->login) > 0 ) { + strcpy(loginbuf,request->login); + if ((t = strchr(loginbuf, ':'))) + *t = '\0'; + strcat(loginbuf,"@"); + } + snprintf(buf, MAX_URL, "%s://%s%s%s%s%s", + ProtocolStr[request->protocol], + loginbuf, + request->host, + portbuf, + "/%2f", request->urlpath); if ((t = strchr(buf, '?'))) *t = '\0';