]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Bug 843: translation should cover FTP Directory Listings as well
authorAmos Jeffries <squid3@treenet.co.nz>
Thu, 6 Nov 2008 13:26:08 +0000 (02:26 +1300)
committerAmos Jeffries <squid3@treenet.co.nz>
Thu, 6 Nov 2008 13:26:08 +0000 (02:26 +1300)
Converts the FTP directory listing to using error page templates.
Which are translated, HTML strict standards compliant, and CSS styled.

errors/errorpage.css
src/MemBuf.h
src/errorpage.cc
src/errorpage.h
src/ftp.cc

index 449a8ca34e5d7e784c5571388282e6b70f54d1d3..d7ba8bf0cc63f0ffece37d06f21a4b4c23c6a1f3 100644 (file)
@@ -66,6 +66,16 @@ pre {
     font-family:sans-serif;
 }
 
+/* special event: FTP directory listing */
+#ftplisting tr.entry td.icon,td.filename,td.size,td.date {
+    border-bottom: groove;
+}
+#ftplisting td.size {
+    width: 50px;
+    text-align: right;
+    padding-right: 5px;
+}
+
 /* horizontal lines */
 hr {
        margin: 0;
index 4da921b3a899bb8fdd9dce4ecfcd2878610dd45e..2fff10579ee65f7f91bb1869fbcb596c2a61ff13 100644 (file)
@@ -72,8 +72,8 @@ public:
 
     /**
      * 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; }
 
index 97b94105eb3afdac27e695ab0146df5e632a5bb7..9e333a7f2a275e8f40c702ad8279083da7955193 100644 (file)
@@ -657,7 +657,10 @@ ErrorState::Convert(char token)
 
     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);
 
         break;
 
@@ -808,6 +811,8 @@ ErrorState::Convert(char token)
     case 'z':
         if (dnsserver_msg)
             p = dnsserver_msg;
+        else if (ftp.cwd_msg)
+            p = ftp.cwd_msg;
         else
             p = "[unknown]";
 
index 35e556e6dd997f3a520d29c21a09e9238a0540d6..02b15ad91cf61e4492b3d462cd58d5a991ddbc3c 100644 (file)
@@ -138,6 +138,8 @@ public:
         wordlist *server_msg;
         char *request;
         char *reply;
+        char *cwd_msg;
+        MemBuf *listing;
     } ftp;
 
     char *request_hdrs;
index e9463d734b4d7e2075f4abe514938b81dd49ea1c..a452b881eec16dafff3060c2b563972548401d7d 100644 (file)
@@ -176,6 +176,7 @@ public:
     char *old_reply;
     char *old_filepath;
     char typecode;
+    MemBuf listing;
 
     // \todo: optimize ctrl and data structs member order, to minimize size
     /// FTP control channel info; the channel is opened once per transaction
@@ -214,8 +215,6 @@ public:
     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();
@@ -567,95 +566,17 @@ FtpStateData::ftpTimeout(const CommTimeoutCbParams &io)
 void
 FtpStateData::listingStart()
 {
-    debugs(9,3, HERE);
-    wordlist *w;
-    char *dirup;
-    int i, j, k;
-    const char *title = title_url.buf();
-    flags.listing_started = true;
-    printfReplyBody("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
-    printfReplyBody("<!-- HTML listing generated by Squid %s -->\n",
-                    version_string);
-    printfReplyBody("<!-- %s -->\n", mkrfc1123(squid_curtime));
-    printfReplyBody("<HTML><HEAD><TITLE>\n");
-    {
-        char *t = xstrdup(title);
-        rfc1738_unescape(t);
-        printfReplyBody("FTP Directory: %s\n", html_quote(t));
-        xfree(t);
-    }
-
-    printfReplyBody("</TITLE>\n");
-    printfReplyBody("<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
-
-    if (flags.need_base_href)
-        printfReplyBody("<BASE HREF=\"%s\">\n",
-                        html_quote(base_href.buf()));
-
-    printfReplyBody("</HEAD><BODY>\n");
-
-    if (cwd_message) {
-        printfReplyBody("<PRE>\n");
-
-        for (w = cwd_message; w; w = w->next)
-            printfReplyBody("%s\n", html_quote(w->key));
-
-        printfReplyBody("</PRE>\n");
-
-        printfReplyBody("<HR noshade size=\"1px\">\n");
-
-        wordlistDestroy(&cwd_message);
-    }
-
-    printfReplyBody("<H2>\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("<A HREF=\"");
-        i += strcspn(&title[i], "/");
-
-        if (i > 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("</A>");
-        }
-
-        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("</H2>\n");
-    printfReplyBody("<PRE>\n");
-    dirup = htmlifyListEntry("<internal-dirup>");
-    writeReplyBody(dirup, strlen(dirup));
-    flags.html_header_sent = 1;
 }
 
+#if DEAD_CODE // obsoleted by ERR_FTP_LISTING template.
 void
 FtpStateData::listingFinish()
 {
     debugs(9,3,HERE);
     entry->buffer();
-    printfReplyBody("</PRE>\n");
+/*
+
+// TODO: figure out what this means and how to show it ...
 
     if (flags.listformat_unknown && !flags.tried_nlst) {
         printfReplyBody("<A HREF=\"%s/;type=d\">[As plain directory]</A>\n",
@@ -664,15 +585,9 @@ FtpStateData::listingFinish()
         const char *path = flags.dir_slash ? filepath : ".";
         printfReplyBody("<A HREF=\"%s/\">[As extended directory]</A>\n", rfc1738_escape_part(path));
     }
-
-    printfReplyBody("<HR noshade size=\"1px\">\n");
-    printfReplyBody("<ADDRESS>\n");
-    printfReplyBody("Generated %s by %s (%s)\n",
-                    mkrfc1123(squid_curtime),
-                    getMyHostname(),
-                    visible_appname_string);
-    printfReplyBody("</ADDRESS></BODY></HTML>\n");
+*/
 }
+#endif /* DEAD_CODE */
 
 /// \ingroup ServerProtocolFTPInternal
 static const char *Month[] =
@@ -964,25 +879,24 @@ dots_fill(size_t len)
     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);
+    if (strlen(line) > 1024) {
+        html->Printf("<tr><td colspan="5">%s</td></tr>\n", line);
         return html;
     }
 
@@ -991,64 +905,10 @@ FtpStateData::htmlifyListEntry(const char *line)
     else
         prefix[0] = '\0';
 
-    /* Handle builtin <dirup> */
-    if (strcmp(line, "<internal-dirup>") == 0) {
-        /* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> {link} */
-        snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-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, "(<A HREF=\"%s\">%s</A>)",
-                     "%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.buf();
-                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, "(<A HREF=\"%s\">%s</A>)", url, "Back");
-            safe_free(url);
-        } else {               /* NO_DOTDOT && ROOT_DIR */
-            /* "UNIX Root" directory */
-            strcpy(href, "/");
-            strcpy(text, "Home Directory");
-        }
-
-        snprintf(html, 8192, "<A HREF=\"%s\">%s</A> <A HREF=\"%s\">%s</A> %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->Printf("<tr class=\"entry\"<td colspan=\"5\">%s</td></tr>\n", line);
 
         for (p = line; *p && xisspace(*p); p++);
         if (*p && !xisspace(*p))
@@ -1058,9 +918,8 @@ FtpStateData::htmlifyListEntry(const char *line)
     }
 
     if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) {
-        *html = '\0';
         ftpListPartsFree(&parts);
-        return html;
+        return NULL;
     }
 
     parts->size += 1023;
@@ -1082,21 +941,21 @@ FtpStateData::htmlifyListEntry(const char *line)
     switch (parts->type) {
 
     case 'd':
-        snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+        snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
                  mimeGetIconURL("internal-dir"),
                  "[DIR]");
         strcat(href, "/");     /* margin is allocated above */
         break;
 
     case 'l':
-        snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+        snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-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, " -> <A HREF=\"%s%s\">%s</A>",
+            snprintf(link, 2048, " -&gt; <a href=\"%s%s\">%s</a>",
                      *link2 != '/' ? prefix : "", link2,
                      html_quote(parts->link));
             safe_free(link2);
@@ -1105,11 +964,11 @@ FtpStateData::htmlifyListEntry(const char *line)
         break;
 
     case '\0':
-        snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+        snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
                  mimeGetIconURL(parts->name),
                  "[UNKNOWN]");
-        snprintf(chdir, 2048, " <A HREF=\"%s/;type=d\"><IMG border=\"0\" SRC=\"%s\" "
-                 "ALT=\"[DIR]\"></A>",
+        snprintf(chdir, 2048, "<a href=\"%s/;type=d\"><img border=\"0\" src=\"%s\" "
+                 "alt=\"[DIR]\"></a>",
                  rfc1738_escape_part(parts->name),
                  mimeGetIconURL("internal-dir"));
         break;
@@ -1117,7 +976,7 @@ FtpStateData::htmlifyListEntry(const char *line)
     case '-':
 
     default:
-        snprintf(icon, 2048, "<IMG border=\"0\" SRC=\"%s\" ALT=\"%-6s\">",
+        snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
                  mimeGetIconURL(parts->name),
                  "[FILE]");
         snprintf(size, 2048, " %6"PRId64"k", parts->size);
@@ -1126,31 +985,32 @@ FtpStateData::htmlifyListEntry(const char *line)
 
     if (parts->type != 'd') {
         if (mimeGetViewOption(parts->name)) {
-            snprintf(view, 2048, " <A HREF=\"%s%s;type=a\"><IMG border=\"0\" SRC=\"%s\" "
-                     "ALT=\"[VIEW]\"></A>",
+            snprintf(view, 2048, "<a href=\"%s%s;type=a\"><img border=\"0\" src=\"%s\" "
+                     "alt=\"[VIEW]\"></a>",
                      prefix, href, mimeGetIconURL("internal-view"));
         }
 
         if (mimeGetDownloadOption(parts->name)) {
-            snprintf(download, 2048, " <A HREF=\"%s%s;type=i\"><IMG border=\"0\" SRC=\"%s\" "
-                     "ALT=\"[DOWNLOAD]\"></A>",
+            snprintf(download, 2048, "<a href=\"%s%s;type=i\"><img border=\"0\" src=\"%s\" "
+                     "alt=\"[DOWNLOAD]\"></a>",
                      prefix, href, mimeGetIconURL("internal-download"));
         }
     }
 
-    /* <A HREF="{href}">{icon}</A> <A HREF="{href}">{text}</A> . . . {date}{size}{chdir}{view}{download}{link}\n  */
-    if (parts->type != '\0') {
-        snprintf(html, 8192, "<A HREF=\"%s%s\">%s</A> <A HREF=\"%s%s\">%s</A>%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, "<A HREF=\"%s%s\">%s</A> <A HREF=\"%s%s\">%s</A>%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->Printf("<tr class=\"entry\">"
+             "<td class=\"icon\"><a href=\"%s%s\">%s</a></td>"
+             "<td class=\"filename\"><a href=\"%s%s\">%s</a></td>"
+             "<td class="date">%s</td>"
+             "<td class="size\">%s</td>"
+             "<td class=\"actions\">%s%s%s%s</td>"
+             "</tr>\n",
+             prefix, href, icon,
+             prefix, href, html_quote(text),
+             parts->date,
+             size,
+             chdir, view, download, link);
 
     ftpListPartsFree(&parts);
     return html;
@@ -1222,9 +1082,9 @@ FtpStateData::parseListing()
 
         t = htmlifyListEntry(line);
 
-        assert(t != NULL);
-
-        writeReplyBody(t, strlen(t));
+        if( t != NULL) {
+            listing.append(t->content(), t->contentSize());
+        }
     }
 
     data.readBuf->consume(usable);
@@ -1416,11 +1276,28 @@ FtpStateData::processReplyBody()
 
 #endif
 
-    if (flags.isdir && !flags.listing_started)
-        listingStart();
-
     if (flags.isdir) {
+
+/*
+// TODO: do we still need this in header?
+    if (flags.need_base_href)
+        printfReplyBody("<BASE HREF=\"%s\">\n", html_quote(base_href.buf()));
+*/
+
+        if (cwd_message) {
+            String tmp;
+            for (wordlist *w = cwd_message; w; w = w->next) {
+                tmp.append(html_quote(w->key));
+                tmp.append("\n");
+            }
+           err_message = xstrdup(tmp.buf());
+        }
+
+        flags.html_header_sent = 1;
+
         parseListing();
+        return;
+
     } else 
     if (const int csize = data.readBuf->contentSize()) {
         writeReplyBody(data.readBuf->content(), csize);
@@ -3325,8 +3202,7 @@ ftpReadTransferDone(FtpStateData * ftpState)
         /* Connection closed; retrieval done. */
 
         if (ftpState->flags.html_header_sent)
-            ftpState->listingFinish();
-
+            ftpState->failed(ERR_FTP_LISTING, 0);
         ftpSendQuit(ftpState);
     } else {                   /* != 226 */
         debugs(9, DBG_IMPORTANT, HERE << "Got code " << code << " after reading data");
@@ -3554,9 +3430,14 @@ FtpStateData::failedErrorMessage(err_type error, int xerrno)
 
     err->xerrno = xerrno;
 
-    err->ftp.server_msg = ctrl.message;
+    if(error == ERR_FTP_LISTING) {
+        err->ftp.listing = &listing;
+    }
+    else {
+        err->ftp.server_msg = ctrl.message;
+        ctrl.message = NULL;
+    }
 
-    ctrl.message = NULL;
 
     if (old_request)
         command = old_request;