=== modified file 'errors/errorpage.css' --- errors/errorpage.css 2009-09-19 09:15:45 +0000 +++ errors/errorpage.css 2009-09-20 10:34:23 +0000 @@ -67,6 +67,15 @@ } /* special event: FTP / Gopher directory listing */ +#dirmsg { + font-family: courier; + color: black; + font-size: 10pt; +} +#dirlisting { + margin-left: 2%; + margin-right: 2%; +} #dirlisting tr.entry td.icon,td.filename,td.size,td.date { border-bottom: groove; } === modified file 'errors/templates/ERR_DIR_LISTING' --- errors/templates/ERR_DIR_LISTING 2009-09-19 09:15:45 +0000 +++ errors/templates/ERR_DIR_LISTING 2009-09-20 10:23:40 +0000 @@ -2,7 +2,7 @@ Directory: %U -
-

Directory: %U/

+

Directory: %U


-

Directory Content:

+

Directory Content:

-
%z
+
%z
=== modified file 'src/MemBuf.h' --- src/MemBuf.h 2009-07-02 16:36:36 +0000 +++ src/MemBuf.h 2009-09-19 12:08:52 +0000 @@ -72,8 +72,8 @@ /** * Whether the buffer contains any data space available. - \retval true if data can be added to teh buffer - \retval false if teh buffer is full + \retval true if data can be added to the buffer + \retval false if the buffer is full */ bool hasSpace() const { return size+1 < capacity; } === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2009-09-19 00:12:39 +0000 +++ src/cache_cf.cc 2009-09-19 12:08:52 +0000 @@ -2529,11 +2529,13 @@ *var = 0; } +#if UNUSED_CODE static void dump_size_t(StoreEntry * entry, const char *name, size_t var) { storeAppendPrintf(entry, "%s %d\n", name, (int) var); } +#endif static void dump_b_size_t(StoreEntry * entry, const char *name, size_t var) @@ -2561,6 +2563,7 @@ storeAppendPrintf(entry, "%s %"PRId64" %s\n", name, var, B_KBYTES_STR); } +#if UNUSED_CODE static void parse_size_t(size_t * var) { @@ -2568,6 +2571,7 @@ i = GetInteger(); *var = (size_t) i; } +#endif static void parse_b_size_t(size_t * var) === modified file 'src/cf.data.pre' --- src/cf.data.pre 2009-09-18 23:46:33 +0000 +++ src/cf.data.pre 2009-09-19 12:08:52 +0000 @@ -2944,16 +2944,6 @@ (for example perl.com). DOC_END -NAME: ftp_list_width -TYPE: size_t -DEFAULT: 32 -LOC: Config.Ftp.list_width -DOC_START - Sets the width of ftp listings. This should be set to fit in - the width of a standard browser. Setting this too small - can cut off long filenames when browsing ftp sites. -DOC_END - NAME: ftp_passive TYPE: onoff DEFAULT: on === modified file 'src/err_type.h' --- src/err_type.h 2009-08-23 09:30:49 +0000 +++ src/err_type.h 2009-09-19 12:08:52 +0000 @@ -52,6 +52,7 @@ ERR_ICAP_FAILURE, /* Special Cases */ + ERR_DIR_LISTING, /* Display of remote directory (FTP, Gopher) */ ERR_SQUID_SIGNATURE, /* not really an error */ ERR_SHUTTING_DOWN, TCP_RESET, === modified file 'src/errorpage.cc' --- src/errorpage.cc 2009-08-23 09:30:49 +0000 +++ src/errorpage.cc 2009-09-20 08:04:44 +0000 @@ -660,7 +660,12 @@ case 'g': /* FTP SERVER MESSAGE */ - wordlistCat(ftp.server_msg, &mb); + if(ftp.server_msg) + wordlistCat(ftp.server_msg, &mb); + else if(ftp.listing) { + mb.append(ftp.listing->content(), ftp.listing->contentSize()); + do_quote = 0; + } break; @@ -819,9 +824,10 @@ case 'z': if (dnsError.size() > 0) p = dnsError.termedBuf(); + else if (ftp.cwd_msg) + p = ftp.cwd_msg; else p = "[unknown]"; - break; case 'Z': === modified file 'src/errorpage.h' --- src/errorpage.h 2009-07-12 22:56:47 +0000 +++ src/errorpage.h 2009-09-19 12:08:52 +0000 @@ -136,6 +136,8 @@ wordlist *server_msg; char *request; char *reply; + char *cwd_msg; + MemBuf *listing; } ftp; char *request_hdrs; === modified file 'src/ftp.cc' --- src/ftp.cc 2009-08-23 09:30:49 +0000 +++ src/ftp.cc 2009-09-20 10:43:12 +0000 @@ -53,6 +53,7 @@ #include "wordlist.h" #include "SquidTime.h" #include "URLScheme.h" +#include "SquidString.h" /** \defgroup ServerProtocolFTPInternal Server-Side FTP Internals @@ -114,13 +115,12 @@ bool dir_slash; bool root_dir; bool no_dotdot; - bool html_header_sent; bool binary; bool try_slash_hack; bool put; bool put_mkdir; bool listformat_unknown; - bool listing_started; + bool listing; bool completed_forwarding; }; @@ -178,11 +178,12 @@ int64_t restart_offset; char *proxy_host; size_t list_width; - wordlist *cwd_message; + String cwd_message; char *old_request; char *old_reply; char *old_filepath; char typecode; + MemBuf listing; ///< FTP directory listing in HTML format. // \todo: optimize ctrl and data structs member order, to minimize size /// FTP control channel info; the channel is opened once per transaction @@ -219,13 +220,12 @@ void failed(err_type, int xerrno); void failedErrorMessage(err_type, int xerrno); void unhack(); - void listingStart(); - void listingFinish(); void scheduleReadControlReply(int); void handleControlReply(); void readStor(); - char *htmlifyListEntry(const char *line); void parseListing(); + MemBuf *htmlifyListEntry(const char *line); + void completedListing(void); void dataComplete(); void dataRead(const CommIoCbParams &io); int checkAuth(const HttpHeader * req_hdr); @@ -501,8 +501,7 @@ if (ctrl.message) wordlistDestroy(&ctrl.message); - if (cwd_message) - wordlistDestroy(&cwd_message); + cwd_message.clean(); safe_free(ctrl.last_reply); @@ -600,115 +599,21 @@ /* failed() closes ctrl.fd and frees ftpState */ } -void -FtpStateData::listingStart() -{ - debugs(9,3, HERE); - wordlist *w; - char *dirup; - int i, j, k; - const char *title = title_url.termedBuf(); - flags.listing_started = true; - printfReplyBody("\n"); - printfReplyBody("\n", - version_string); - printfReplyBody("\n", mkrfc1123(squid_curtime)); - printfReplyBody("\n"); - { - char *t = xstrdup(title); - rfc1738_unescape(t); - printfReplyBody("FTP Directory: %s\n", html_quote(t)); - xfree(t); - } - - printfReplyBody("\n"); - printfReplyBody("\n"); - - if (flags.need_base_href) - printfReplyBody("\n", - html_quote(base_href.termedBuf())); - - printfReplyBody("\n"); - - if (cwd_message) { - printfReplyBody("
\n");
-
-        for (w = cwd_message; w; w = w->next)
-            printfReplyBody("%s\n", html_quote(w->key));
-
-        printfReplyBody("
\n"); - - printfReplyBody("
\n"); - - wordlistDestroy(&cwd_message); - } - - printfReplyBody("

\n"); - printfReplyBody("FTP Directory: "); - /* "ftp://" == 6 characters */ - assert(title_url.size() >= 6); - k = 6 + strcspn(&title[6], "/"); - - for (i = 6, j = 0; title[i]; j = i) { - printfReplyBody(" j) { - char *url = xstrdup(title); - url[i] = '\0'; - printfReplyBody("%s", html_quote(url + k)); - printfReplyBody("/"); - printfReplyBody("\">"); - rfc1738_unescape(url + j); - printfReplyBody("%s", html_quote(url + j)); - safe_free(url); - printfReplyBody(""); - } - - printfReplyBody("/"); - - if (title[i] == '/') - i++; - - if (i == j) { - /* Error guard, or "assert" */ - printfReplyBody("ERROR: Failed to parse URL: %s\n", - html_quote(title)); - debugs(9, DBG_CRITICAL, "Failed to parse URL: " << title); - break; - } - } - - printfReplyBody("

\n"); - printfReplyBody("
\n");
-    dirup = htmlifyListEntry("");
-    writeReplyBody(dirup, strlen(dirup));
-    flags.html_header_sent = 1;
-}
-
+#if DEAD_CODE // obsoleted by ERR_DIR_LISTING
 void
 FtpStateData::listingFinish()
 {
-    debugs(9,3,HERE);
-    entry->buffer();
-    printfReplyBody("
\n"); + // TODO: figure out what this means and how to show it ... if (flags.listformat_unknown && !flags.tried_nlst) { - printfReplyBody("[As plain directory]\n", + printfReplyBody("[As plain directory]\n", flags.dir_slash ? rfc1738_escape_part(old_filepath) : "."); } else if (typecode == 'D') { const char *path = flags.dir_slash ? filepath : "."; - printfReplyBody("[As extended directory]\n", rfc1738_escape_part(path)); + printfReplyBody("[As extended directory]\n", rfc1738_escape_part(path)); } - - printfReplyBody("
\n"); - printfReplyBody("
\n"); - printfReplyBody("Generated %s by %s (%s)\n", - mkrfc1123(squid_curtime), - getMyHostname(), - visible_appname_string); - printfReplyBody("
\n"); } +#endif /* DEAD_CODE */ /// \ingroup ServerProtocolFTPInternal static const char *Month[] = { @@ -972,47 +877,28 @@ return p; } -/// \ingroup ServerProtocolFTPInternal -static const char * -dots_fill(size_t len) -{ - static char buf[256]; - size_t i = 0; - - if (len > Config.Ftp.list_width) { - memset(buf, ' ', 256); - buf[0] = '\n'; - buf[Config.Ftp.list_width + 4] = '\0'; - return buf; - } - - for (i = len; i < Config.Ftp.list_width; i++) - buf[i - len] = (i % 2) ? '.' : ' '; - - buf[i - len] = '\0'; - - return buf; -} - -char * +MemBuf * FtpStateData::htmlifyListEntry(const char *line) { - LOCAL_ARRAY(char, icon, 2048); - LOCAL_ARRAY(char, href, 2048 + 40); - LOCAL_ARRAY(char, text, 2048); - LOCAL_ARRAY(char, size, 2048); - LOCAL_ARRAY(char, chdir, 2048 + 40); - LOCAL_ARRAY(char, view, 2048 + 40); - LOCAL_ARRAY(char, download, 2048 + 40); - LOCAL_ARRAY(char, link, 2048 + 40); - LOCAL_ARRAY(char, html, 8192); - LOCAL_ARRAY(char, prefix, 2048); - size_t width = Config.Ftp.list_width; + char icon[2048]; + char href[2048 + 40]; + char text[ 2048]; + char size[ 2048]; + char chdir[ 2048 + 40]; + char view[ 2048 + 40]; + char download[ 2048 + 40]; + char link[ 2048 + 40]; + MemBuf *html; + char prefix[2048]; ftpListParts *parts; - *icon = *href = *text = *size = *chdir = *view = *download = *link = *html = '\0'; - - if ((int) strlen(line) > 1024) { - snprintf(html, 8192, "%s\n", line); + *icon = *href = *text = *size = *chdir = *view = *download = *link = '\0'; + + debugs(9, 7, HERE << " line ={" << line << "}"); + + if (strlen(line) > 1024) { + html = new MemBuf(); + html->init(); + html->Printf("\n", line); return html; } @@ -1021,64 +907,12 @@ else prefix[0] = '\0'; - /* Handle builtin */ - if (strcmp(line, "") == 0) { - /* {icon} {text} {link} */ - snprintf(icon, 2048, "\"%-6s\"", - mimeGetIconURL("internal-dirup"), - "[DIRUP]"); - - if (!flags.no_dotdot && !flags.root_dir) { - /* Normal directory */ - - if (!flags.dir_slash) - strcpy(href, "../"); - else - strcpy(href, "./"); - - strcpy(text, "Parent Directory"); - } else if (!flags.no_dotdot && flags.root_dir) { - /* "Top level" directory */ - strcpy(href, "%2e%2e/"); - strcpy(text, "Parent Directory"); - snprintf(link, 2048, "(%s)", - "%2f/", - "Root Directory"); - } else if (flags.no_dotdot && !flags.root_dir) { - char *url; - /* Normal directory where last component is / or .. */ - strcpy(href, "%2e%2e/"); - strcpy(text, "Parent Directory"); - - if (flags.dir_slash) { - url = xstrdup("./"); - } else { - const char *title = title_url.termedBuf(); - int k = 6 + strcspn(&title[6], "/"); - char *t; - url = xstrdup(title + k); - t = url + strlen(url) - 2; - - while (t > url && *t != '/') - *t-- = '\0'; - } - - snprintf(link, 2048, "(%s)", url, "Back"); - safe_free(url); - } else { /* NO_DOTDOT && ROOT_DIR */ - /* "UNIX Root" directory */ - strcpy(href, "/"); - strcpy(text, "Home Directory"); - } - - snprintf(html, 8192, "%s %s %s\n", - href, icon, href, text, link); - return html; - } - if ((parts = ftpListParseParts(line, flags)) == NULL) { const char *p; - snprintf(html, 8192, "%s\n", line); + + html = new MemBuf(); + html->init(); + html->Printf("\n", line); for (p = line; *p && xisspace(*p); p++); if (*p && !xisspace(*p)) @@ -1088,22 +922,14 @@ } if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) { - *html = '\0'; ftpListPartsFree(&parts); - return html; + return NULL; } parts->size += 1023; parts->size >>= 10; parts->showname = xstrdup(parts->name); - if (!Config.Ftp.list_wrap) { - if (strlen(parts->showname) > width - 1) { - *(parts->showname + width - 1) = '>'; - *(parts->showname + width - 0) = '\0'; - } - } - /* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}\n */ xstrncpy(href, rfc1738_escape_part(parts->name), 2048); @@ -1112,21 +938,21 @@ switch (parts->type) { case 'd': - snprintf(icon, 2048, "\"%-6s\"", + snprintf(icon, 2048, "\"%-6s\"", mimeGetIconURL("internal-dir"), "[DIR]"); strcat(href, "/"); /* margin is allocated above */ break; case 'l': - snprintf(icon, 2048, "\"%-6s\"", + snprintf(icon, 2048, "\"%-6s\"", mimeGetIconURL("internal-link"), "[LINK]"); /* sometimes there is an 'l' flag, but no "->" link */ if (parts->link) { char *link2 = xstrdup(html_quote(rfc1738_escape(parts->link))); - snprintf(link, 2048, " -> %s", + snprintf(link, 2048, " -> %s", *link2 != '/' ? prefix : "", link2, html_quote(parts->link)); safe_free(link2); @@ -1135,11 +961,11 @@ break; case '\0': - snprintf(icon, 2048, "\"%-6s\"", + snprintf(icon, 2048, "\"%-6s\"", mimeGetIconURL(parts->name), "[UNKNOWN]"); - snprintf(chdir, 2048, " ", + snprintf(chdir, 2048, "", rfc1738_escape_part(parts->name), mimeGetIconURL("internal-dir")); break; @@ -1147,7 +973,7 @@ case '-': default: - snprintf(icon, 2048, "\"%-6s\"", + snprintf(icon, 2048, "\"%-6s\"", mimeGetIconURL(parts->name), "[FILE]"); snprintf(size, 2048, " %6"PRId64"k", parts->size); @@ -1156,31 +982,33 @@ if (parts->type != 'd') { if (mimeGetViewOption(parts->name)) { - snprintf(view, 2048, " ", + snprintf(view, 2048, "", prefix, href, mimeGetIconURL("internal-view")); } if (mimeGetDownloadOption(parts->name)) { - snprintf(download, 2048, " ", + snprintf(download, 2048, "", prefix, href, mimeGetIconURL("internal-download")); } } - /* {icon}{text} . . . {date}{size}{chdir}{view}{download}{link}\n */ - if (parts->type != '\0') { - snprintf(html, 8192, "%s%s%s " - "%s%8s%s%s%s%s\n", - prefix, href, icon, prefix, href, html_quote(text), dots_fill(strlen(text)), - parts->date, size, chdir, view, download, link); - } else { - /* Plain listing. {icon} {text} ... {chdir}{view}{download} */ - snprintf(html, 8192, "%s%s%s " - "%s%s%s%s\n", - prefix, href, icon, prefix, href, html_quote(text), dots_fill(strlen(text)), - chdir, view, download, link); - } + /* construct the table row from parts. */ + html = new MemBuf(); + html->init(); + html->Printf("" + "" + "" + "" + "" + "" + "\n", + prefix, href, icon, + prefix, href, html_quote(text), + parts->date, + size, + chdir, view, download, link); ftpListPartsFree(&parts); return html; @@ -1194,14 +1022,13 @@ char *end; char *line; char *s; - char *t; + MemBuf *t; size_t linelen; size_t usable; - StoreEntry *e = entry; size_t len = data.readBuf->contentSize(); if (!len) { - debugs(9, 3, HERE << "no content to parse for " << e->url() ); + debugs(9, 3, HERE << "no content to parse for " << entry->url() ); return; } @@ -1220,7 +1047,7 @@ debugs(9, 3, HERE << "usable = " << usable); if (usable == 0) { - debugs(9, 3, HERE << "didn't find end for " << e->url() ); + debugs(9, 3, HERE << "didn't find end for " << entry->url() ); xfree(sbuf); return; } @@ -1229,7 +1056,6 @@ line = (char *)memAllocate(MEM_4K_BUF); end++; - e->buffer(); /* released when done processing current data payload */ s = sbuf; s += strspn(s, crlf); @@ -1252,11 +1078,14 @@ t = htmlifyListEntry(line); - assert(t != NULL); - - writeReplyBody(t, strlen(t)); + if( t != NULL) { + debugs(9, 7, HERE << "listing append: t = {" << t->contentSize() << ", '" << t->content() << "'}"); + listing.append(t->content(), t->contentSize()); +//leak? delete t; + } } + debugs(9, 7, HERE << "Done."); data.readBuf->consume(usable); memFree(line, MEM_4K_BUF); xfree(sbuf); @@ -1373,7 +1202,7 @@ maybeReadVirginBody(); } else { - if (!flags.http_header_sent && !fwd->ftpPasvFailed() && flags.pasv_supported) { + if (!flags.http_header_sent && !fwd->ftpPasvFailed() && flags.pasv_supported && !flags.listing) { fwd->dontRetry(false); /* this is a retryable error */ fwd->ftpPasvFailed(true); } @@ -1410,7 +1239,8 @@ return; } - if (!flags.http_header_sent && data.readBuf->contentSize() >= 0) + /* Directory listings are special. They write ther own headers via the error objects */ + if (!flags.http_header_sent && data.readBuf->contentSize() >= 0 && !flags.isdir) appendSuccessHeader(); if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { @@ -1431,11 +1261,14 @@ #endif - if (flags.isdir && !flags.listing_started) - listingStart(); - if (flags.isdir) { + if (!flags.listing) { + flags.listing = 1; + listing.reset(); + } parseListing(); + maybeReadVirginBody(); + return; } else if (const int csize = data.readBuf->contentSize()) { writeReplyBody(data.readBuf->content(), csize); debugs(9, 5, HERE << "consuming " << csize << " bytes of readBuf"); @@ -1925,7 +1758,12 @@ /* Copy the rest of the message to cwd_message to be printed in * error messages */ - wordlistAddWl(&cwd_message, ctrl.message); + if (ctrl.message) { + for (wordlist *w = ctrl.message; w; w = w->next) { + cwd_message.append('\n'); + cwd_message.append(w->key); + } + } debugs(9, 3, HERE << "state=" << state << ", code=" << ctrl.replycode); @@ -2269,13 +2107,13 @@ if (code >= 200 && code < 300) { /* CWD OK */ ftpState->unhack(); + /* Reset cwd_message to only include the last message */ - - if (ftpState->cwd_message) - wordlistDestroy(&ftpState->cwd_message); - - ftpState->cwd_message = ftpState->ctrl.message; - + ftpState->cwd_message.reset(""); + for (wordlist *w = ftpState->ctrl.message; w; w = w->next) { + ftpState->cwd_message.append(' '); + ftpState->cwd_message.append(w->key); + } ftpState->ctrl.message = NULL; /* Continue to traverse the path */ @@ -3441,6 +3279,28 @@ } } +/** + * Generate the HTTP headers and template fluff around an FTP + * directory listing display. + */ +void +FtpStateData::completedListing() +{ + assert(entry); + entry->lock(); + ErrorState *ferr = errorCon(ERR_DIR_LISTING, HTTP_OK, request); + ferr->ftp.listing = &listing; + ferr->ftp.cwd_msg = xstrdup(cwd_message.termedBuf()); + ferr->ftp.server_msg = ctrl.message; + ctrl.message = NULL; + entry->replaceHttpReply( ferr->BuildHttpReply() ); + errorStateFree(ferr); + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + entry->flush(); + entry->unlock(); +} + + /// \ingroup ServerProtocolFTPInternal static void ftpReadTransferDone(FtpStateData * ftpState) @@ -3450,10 +3310,10 @@ if (code == 226 || code == 250) { /* Connection closed; retrieval done. */ - - if (ftpState->flags.html_header_sent) - ftpState->listingFinish(); - + if (ftpState->flags.listing) { + ftpState->completedListing(); + /* QUIT operation handles sending the reply to client */ + } ftpSendQuit(ftpState); } else { /* != 226 */ debugs(9, DBG_IMPORTANT, HERE << "Got code " << code << " after reading data"); @@ -3513,11 +3373,15 @@ ftpState->state = SENT_QUIT; } -/// \ingroup ServerProtocolFTPInternal +/** + * \ingroup ServerProtocolFTPInternal + * + * This completes a client FTP operation with success or other page + * generated and stored in the entry field by the code issuing QUIT. + */ static void ftpReadQuit(FtpStateData * ftpState) { - /** \todo XXX should this just be a case of abortTransaction? */ ftpState->serverComplete(); } @@ -3687,7 +3551,6 @@ err->xerrno = xerrno; err->ftp.server_msg = ctrl.message; - ctrl.message = NULL; if (old_request) @@ -3764,7 +3627,6 @@ String urlpath = request->urlpath; const char *filename = NULL; const char *t = NULL; - StoreEntry *e = entry; debugs(9, 3, HERE); @@ -3775,11 +3637,11 @@ flags.http_header_sent = 1; - assert(e->isEmpty()); - - EBIT_CLR(e->flags, ENTRY_FWD_HDR_WAIT); - - e->buffer(); /* released when done processing current data payload */ + assert(entry->isEmpty()); + + EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); + + entry->buffer(); /* released when done processing current data payload */ filename = (t = urlpath.rpos('/')) ? t + 1 : urlpath.termedBuf();
%s
%s
%s%s%s%s%s%s%s%s