/*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "squid.h"
#include "acl/FilledChecklist.h"
+#include "base/PackableStream.h"
#include "clients/forward.h"
#include "clients/FtpClient.h"
#include "comm.h"
#include "HttpHeader.h"
#include "HttpHeaderRange.h"
#include "HttpReply.h"
-#include "HttpRequest.h"
#include "ip/tools.h"
#include "MemBuf.h"
#include "mime.h"
#include "StatCounters.h"
#include "Store.h"
#include "tools.h"
-#include "URL.h"
#include "util.h"
#include "wordlist.h"
void unhack();
void readStor();
void parseListing();
- MemBuf *htmlifyListEntry(const char *line);
+ bool htmlifyListEntry(const char *line, PackableStream &);
void completedListing(void);
/// create a data channel acceptor and start listening.
virtual void timeout(const CommTimeoutCbParams &io);
void ftpAcceptDataConnection(const CommAcceptCbParams &io);
- static HttpReply *ftpAuthRequired(HttpRequest * request, const char *realm);
- const char *ftpRealm(void);
+ static HttpReply *ftpAuthRequired(HttpRequest * request, SBuf &realm, AccessLogEntry::Pointer &);
+ SBuf ftpRealm();
void loginFailed(void);
virtual void haveParsedReplyHeaders();
char *link;
} ftpListParts;
-#define CTRL_BUFLEN 1024
+#define CTRL_BUFLEN 16*1024
static char cbuf[CTRL_BUFLEN];
/*
static FTPSM ftpReadMdtm;
static FTPSM ftpSendSize;
static FTPSM ftpReadSize;
+#if 0
static FTPSM ftpSendEPRT;
+#endif
static FTPSM ftpReadEPRT;
static FTPSM ftpSendPORT;
static FTPSM ftpReadPORT;
void
Ftp::Gateway::listenForDataChannel(const Comm::ConnectionPointer &conn)
{
+ if (!Comm::IsConnOpen(ctrl.conn)) {
+ debugs(9, 5, "The control connection to the remote end is closed");
+ return;
+ }
+
assert(!Comm::IsConnOpen(data.conn));
typedef CommCbMemFunT<Gateway, CommAcceptCbParams> AcceptDialer;
{
ftpListParts *p = NULL;
char *t = NULL;
- const char *ct = NULL;
- char *tokens[MAX_TOKENS];
+ struct FtpLineToken {
+ char *token = nullptr; ///< token image copied from the received line
+ size_t pos = 0; ///< token offset on the received line
+ } tokens[MAX_TOKENS];
int i;
int n_tokens;
static char tbuf[128];
}
for (t = strtok(xbuf, w_space); t && n_tokens < MAX_TOKENS; t = strtok(NULL, w_space)) {
- tokens[n_tokens] = xstrdup(t);
+ tokens[n_tokens].token = xstrdup(t);
+ tokens[n_tokens].pos = t - xbuf;
++n_tokens;
}
/* locate the Month field */
for (i = 3; i < n_tokens - 2; ++i) {
- char *size = tokens[i - 1];
- char *month = tokens[i];
- char *day = tokens[i + 1];
- char *year = tokens[i + 2];
+ const auto size = tokens[i - 1].token;
+ char *month = tokens[i].token;
+ char *day = tokens[i + 1].token;
+ char *year = tokens[i + 2].token;
if (!is_month(month))
continue;
if (regexec(&scan_ftp_time, year, 0, NULL, 0) != 0) /* Yr | hh:mm */
continue;
- snprintf(tbuf, 128, "%s %2s %5s",
- month, day, year);
+ const auto *copyFrom = buf + tokens[i].pos;
- if (!strstr(buf, tbuf))
- snprintf(tbuf, 128, "%s %2s %-5s",
- month, day, year);
+ // "MMM DD [ YYYY|hh:mm]" with at most two spaces between DD and YYYY
+ auto dateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %5s", month, day, year);
+ bool isTypeA = (dateSize == 12) && (strncmp(copyFrom, tbuf, dateSize) == 0);
- char const *copyFrom = NULL;
+ // "MMM DD [YYYY|hh:mm]" with one space between DD and YYYY
+ dateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %-5s", month, day, year);
+ bool isTypeB = (dateSize == 12 || dateSize == 11) && (strncmp(copyFrom, tbuf, dateSize) == 0);
- if ((copyFrom = strstr(buf, tbuf))) {
- p->type = *tokens[0];
+ // TODO: replace isTypeA and isTypeB with a regex.
+ if (isTypeA || isTypeB) {
+ p->type = *tokens[0].token;
p->size = strtoll(size, NULL, 10);
+ const auto finalDateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %5s", month, day, year);
+ assert(finalDateSize >= 0);
p->date = xstrdup(tbuf);
+ // point after tokens[i+2] :
+ copyFrom = buf + tokens[i + 2].pos + strlen(tokens[i + 2].token);
if (flags.skip_whitespace) {
- copyFrom += strlen(tbuf);
-
while (strchr(w_space, *copyFrom))
++copyFrom;
} else {
- /* XXX assumes a single space between date and filename
+ /* Handle the following four formats:
+ * "MMM DD YYYY Name"
+ * "MMM DD YYYYName"
+ * "MMM DD YYYY Name"
+ * "MMM DD YYYY Name"
+ * Assuming a single space between date and filename
* suggested by: Nathan.Bailey@cc.monash.edu.au and
* Mike Battersby <mike@starbug.bofh.asn.au> */
- copyFrom += strlen(tbuf) + 1;
+ if (strchr(w_space, *copyFrom))
+ ++copyFrom;
}
p->name = xstrdup(copyFrom);
/* try it as a DOS listing, 04-05-70 09:33PM ... */
if (n_tokens > 3 &&
- regexec(&scan_ftp_dosdate, tokens[0], 0, NULL, 0) == 0 &&
- regexec(&scan_ftp_dostime, tokens[1], 0, NULL, 0) == 0) {
- if (!strcasecmp(tokens[2], "<dir>")) {
+ regexec(&scan_ftp_dosdate, tokens[0].token, 0, NULL, 0) == 0 &&
+ regexec(&scan_ftp_dostime, tokens[1].token, 0, NULL, 0) == 0) {
+ if (!strcasecmp(tokens[2].token, "<dir>")) {
p->type = 'd';
} else {
p->type = '-';
- p->size = strtoll(tokens[2], NULL, 10);
+ p->size = strtoll(tokens[2].token, NULL, 10);
}
- snprintf(tbuf, 128, "%s %s", tokens[0], tokens[1]);
+ snprintf(tbuf, sizeof(tbuf), "%s %s", tokens[0].token, tokens[1].token);
p->date = xstrdup(tbuf);
if (p->type == 'd') {
- /* Directory.. name begins with first printable after <dir> */
- ct = strstr(buf, tokens[2]);
- ct += strlen(tokens[2]);
-
- while (xisspace(*ct))
- ++ct;
-
- if (!*ct)
- ct = NULL;
+ // Directory.. name begins with first printable after <dir>
+ // Because of the "n_tokens > 3", the next printable after <dir>
+ // is stored at token[3]. No need for more checks here.
} else {
- /* A file. Name begins after size, with a space in between */
- snprintf(tbuf, 128, " %s %s", tokens[2], tokens[3]);
- ct = strstr(buf, tbuf);
-
- if (ct) {
- ct += strlen(tokens[2]) + 2;
- }
+ // A file. Name begins after size, with a space in between.
+ // Also a space should exist before size.
+ // But there is not needed to be very strict with spaces.
+ // The name is stored at token[3], take it from here.
}
- p->name = xstrdup(ct ? ct : tokens[3]);
+ p->name = xstrdup(tokens[3].token);
goto found;
}
/* Try EPLF format; carson@lehman.com */
if (buf[0] == '+') {
- ct = buf + 1;
+ const char *ct = buf + 1;
p->type = 0;
while (ct && *ct) {
found:
for (i = 0; i < n_tokens; ++i)
- xfree(tokens[i]);
+ xfree(tokens[i].token);
if (!p->name)
ftpListPartsFree(&p); /* cleanup */
return p;
}
-MemBuf *
-Ftp::Gateway::htmlifyListEntry(const char *line)
+bool
+Ftp::Gateway::htmlifyListEntry(const char *line, PackableStream &html)
{
- 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 = '\0';
-
- debugs(9, 7, HERE << " line ={" << line << "}");
+ debugs(9, 7, "line={" << line << "}");
if (strlen(line) > 1024) {
- html = new MemBuf();
- html->init();
- html->appendf("<tr><td colspan=\"5\">%s</td></tr>\n", line);
- return html;
+ html << "<tr><td colspan=\"5\">" << line << "</td></tr>\n";
+ return true;
}
- if (flags.dir_slash && dirpath && typecode != 'D')
- snprintf(prefix, 2048, "%s/", rfc1738_escape_part(dirpath));
- else
- prefix[0] = '\0';
-
- if ((parts = ftpListParseParts(line, flags)) == NULL) {
- const char *p;
+ SBuf prefix;
+ if (flags.dir_slash && dirpath && typecode != 'D') {
+ prefix.append(rfc1738_escape_part(dirpath));
+ prefix.append("/", 1);
+ }
- html = new MemBuf();
- html->init();
- html->appendf("<tr class=\"entry\"><td colspan=\"5\">%s</td></tr>\n", line);
+ ftpListParts *parts = ftpListParseParts(line, flags);
+ if (!parts) {
+ html << "<tr class=\"entry\"><td colspan=\"5\">" << line << "</td></tr>\n";
+ const char *p;
for (p = line; *p && xisspace(*p); ++p);
if (*p && !xisspace(*p))
flags.listformat_unknown = 1;
- return html;
+ return true;
}
if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) {
ftpListPartsFree(&parts);
- return NULL;
+ return false;
}
parts->size += 1023;
parts->showname = xstrdup(parts->name);
/* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}\n */
- xstrncpy(href, rfc1738_escape_part(parts->name), 2048);
+ SBuf href(prefix);
+ href.append(rfc1738_escape_part(parts->name));
- xstrncpy(text, parts->showname, 2048);
+ SBuf text(parts->showname);
+ SBuf icon, size, chdir, link;
switch (parts->type) {
case 'd':
- snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
- mimeGetIconURL("internal-dir"),
- "[DIR]");
- strcat(href, "/"); /* margin is allocated above */
+ icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
+ mimeGetIconURL("internal-dir"),
+ "[DIR]");
+ href.append("/", 1); /* margin is allocated above */
break;
case 'l':
- snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
- mimeGetIconURL("internal-link"),
- "[LINK]");
+ icon.appendf("<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>",
- *link2 != '/' ? prefix : "", link2,
- html_quote(parts->link));
- safe_free(link2);
+ SBuf link2(html_quote(rfc1738_escape(parts->link)));
+ link.appendf(" -> <a href=\"%s" SQUIDSBUFPH "\">%s</a>",
+ link2[0] != '/' ? prefix.c_str() : "", SQUIDSBUFPRINT(link2),
+ html_quote(parts->link));
}
break;
case '\0':
- 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>",
- rfc1738_escape_part(parts->name),
- mimeGetIconURL("internal-dir"));
+ icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
+ mimeGetIconURL(parts->name),
+ "[UNKNOWN]");
+ chdir.appendf("<a href=\"%s/;type=d\"><img border=\"0\" src=\"%s\" "
+ "alt=\"[DIR]\"></a>",
+ rfc1738_escape_part(parts->name),
+ mimeGetIconURL("internal-dir"));
break;
case '-':
default:
- snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
- mimeGetIconURL(parts->name),
- "[FILE]");
- snprintf(size, 2048, " %6" PRId64 "k", parts->size);
+ icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
+ mimeGetIconURL(parts->name),
+ "[FILE]");
+ size.appendf(" %6" PRId64 "k", parts->size);
break;
}
+ SBuf view, download;
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>",
- prefix, href, mimeGetIconURL("internal-view"));
+ view.appendf("<a href=\"" SQUIDSBUFPH ";type=a\"><img border=\"0\" src=\"%s\" "
+ "alt=\"[VIEW]\"></a>",
+ SQUIDSBUFPRINT(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>",
- prefix, href, mimeGetIconURL("internal-download"));
+ download.appendf("<a href=\"" SQUIDSBUFPH ";type=i\"><img border=\"0\" src=\"%s\" "
+ "alt=\"[DOWNLOAD]\"></a>",
+ SQUIDSBUFPRINT(href), mimeGetIconURL("internal-download"));
}
}
/* construct the table row from parts. */
- html = new MemBuf();
- html->init();
- html->appendf("<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);
+ html << "<tr class=\"entry\">"
+ "<td class=\"icon\"><a href=\"" << href << "\">" << icon << "</a></td>"
+ "<td class=\"filename\"><a href=\"" << href << "\">" << html_quote(text.c_str()) << "</a></td>"
+ "<td class=\"date\">" << parts->date << "</td>"
+ "<td class=\"size\">" << size << "</td>"
+ "<td class=\"actions\">" << chdir << view << download << link << "</td>"
+ "</tr>\n";
ftpListPartsFree(&parts);
- return html;
+ return true;
}
void
char *end;
char *line;
char *s;
- MemBuf *t;
size_t linelen;
size_t usable;
size_t len = data.readBuf->contentSize();
if (!strncmp(line, "total", 5))
continue;
- t = htmlifyListEntry(line);
+ MemBuf htmlPage;
+ htmlPage.init();
+ PackableStream html(htmlPage);
- if ( t != NULL) {
- debugs(9, 7, HERE << "listing append: t = {" << t->contentSize() << ", '" << t->content() << "'}");
- listing.append(t->content(), t->contentSize());
- delete t;
+ if (htmlifyListEntry(line, html)) {
+ html.flush();
+ debugs(9, 7, "listing append: t = {" << htmlPage.contentSize() << ", '" << htmlPage.content() << "'}");
+ listing.append(htmlPage.content(), htmlPage.contentSize());
}
}
#if HAVE_AUTH_MODULE_BASIC
/* Check HTTP Authorization: headers (better than defaults, but less than URL) */
- const SBuf auth(req_hdr->getAuth(Http::HdrType::AUTHORIZATION, "Basic"));
+ const auto auth(req_hdr->getAuthToken(Http::HdrType::AUTHORIZATION, "Basic"));
if (!auth.isEmpty()) {
flags.authenticated = 1;
loginParser(auth, false);
{
if (!checkAuth(&request->header)) {
/* create appropriate reply */
- HttpReply *reply = ftpAuthRequired(request, ftpRealm());
+ SBuf realm(ftpRealm()); // local copy so SBuf will not disappear too early
+ const auto reply = ftpAuthRequired(request.getRaw(), realm, fwd->al);
entry->replaceHttpReply(reply);
serverComplete();
return;
checkUrlpath();
buildTitleUrl();
- debugs(9, 5, "FD " << ctrl.conn->fd << " : host=" << request->url.host() <<
+ debugs(9, 5, "FD " << (ctrl.conn ? ctrl.conn->fd : -1) << " : host=" << request->url.host() <<
", path=" << request->url.path() << ", user=" << user << ", passwd=" << password);
state = BEGIN;
Ftp::Client::start();
if ((state == SENT_USER || state == SENT_PASS) && ctrl.replycode >= 400) {
if (ctrl.replycode == 421 || ctrl.replycode == 426) {
// 421/426 - Service Overload - retry permitted.
- err = new ErrorState(ERR_FTP_UNAVAILABLE, Http::scServiceUnavailable, fwd->request);
+ err = new ErrorState(ERR_FTP_UNAVAILABLE, Http::scServiceUnavailable, fwd->request, fwd->al);
} else if (ctrl.replycode >= 430 && ctrl.replycode <= 439) {
// 43x - Invalid or Credential Error - retry challenge required.
- err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request);
+ err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request, fwd->al);
} else if (ctrl.replycode >= 530 && ctrl.replycode <= 539) {
// 53x - Credentials Missing - retry challenge required
if (password_url) // but they were in the URI! major fail.
- err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scForbidden, fwd->request);
+ err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scForbidden, fwd->request, fwd->al);
else
- err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request);
+ err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request, fwd->al);
}
}
#if HAVE_AUTH_MODULE_BASIC
/* add Authenticate header */
- newrep->header.putAuth("Basic", ftpRealm());
+ // XXX: performance regression. c_str() may reallocate
+ SBuf realm(ftpRealm()); // local copy so SBuf will not disappear too early
+ newrep->header.putAuth("Basic", realm.c_str());
#endif
// add it to the store entry for response....
serverComplete();
}
-const char *
+SBuf
Ftp::Gateway::ftpRealm()
{
- static char realm[8192];
+ SBuf realm;
/* This request is not fully authenticated */
- if (!request) {
- snprintf(realm, 8192, "FTP %s unknown", user);
- } else if (request->url.port() == 21) {
- snprintf(realm, 8192, "FTP %s %s", user, request->url.host());
- } else {
- snprintf(realm, 8192, "FTP %s %s port %d", user, request->url.host(), request->url.port());
+ realm.appendf("FTP %s ", user);
+ if (!request)
+ realm.append("unknown", 7);
+ else {
+ realm.append(request->url.host());
+ if (request->url.port() != 21)
+ realm.appendf(" port %d", request->url.port());
}
return realm;
}
/* Reset cwd_message to only include the last message */
ftpState->cwd_message.reset("");
for (wordlist *w = ftpState->ctrl.message; w; w = w->next) {
- ftpState->cwd_message.append(' ');
+ ftpState->cwd_message.append('\n');
ftpState->cwd_message.append(w->key);
}
ftpState->ctrl.message = NULL;
if (ftpState->handlePasvReply(srvAddr))
ftpState->connectDataChannel();
else {
- ftpSendEPRT(ftpState);
+ ftpFail(ftpState);
+ // Currently disabled, does not work correctly:
+ // ftpSendEPRT(ftpState);
return;
}
}
// ABORT on timeouts. server may be waiting on a broken TCP link.
if (io.xerrno == Comm::TIMEOUT)
- writeCommand("ABOR");
+ writeCommand("ABOR\r\n");
// try another connection attempt with some other method
ftpSendPassive(this);
}
safe_free(ftpState->data.host);
+ if (!Comm::IsConnOpen(ftpState->ctrl.conn)) {
+ debugs(9, 5, "The control connection to the remote end is closed");
+ return;
+ }
+
/*
* Set up a listen socket on the same local address as the
* control connection.
errno = 0;
if (setsockopt(ftpState->ctrl.conn->fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof(on)) == -1) {
+ int xerrno = errno;
// SO_REUSEADDR is only an optimization, no need to be verbose about error
- debugs(9, 4, "setsockopt failed: " << xstrerror());
+ debugs(9, 4, "setsockopt failed: " << xstrerr(xerrno));
}
ftpState->ctrl.conn->flags |= COMM_REUSEADDR;
temp->flags |= COMM_REUSEADDR;
ftpRestOrList(ftpState);
}
+#if 0
static void
ftpSendEPRT(Ftp::Gateway * ftpState)
{
+ /* check the server control channel is still available */
+ if (!ftpState || !ftpState->haveControlChannel("ftpSendEPRT"))
+ return;
+
if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent) {
debugs(9, DBG_IMPORTANT, "FTP does not allow EPRT method after 'EPSV ALL' has been sent.");
return;
ftpState->writeCommand(cbuf);
ftpState->state = Ftp::Client::SENT_EPRT;
}
+#endif
static void
ftpReadEPRT(Ftp::Gateway * ftpState)
{
debugs(9, 3, HERE);
- if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
- abortAll("entry aborted when accepting data conn");
- data.listenConn->close();
- data.listenConn = NULL;
+ if (!Comm::IsConnOpen(ctrl.conn)) { /*Close handlers will cleanup*/
+ debugs(9, 5, "The control connection to the remote end is closed");
return;
}
return;
}
+ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
+ abortAll("entry aborted when accepting data conn");
+ data.listenConn->close();
+ data.listenConn = NULL;
+ io.conn->close();
+ return;
+ }
+
/* data listening conn is no longer even open. abort. */
if (!Comm::IsConnOpen(data.listenConn)) {
data.listenConn = NULL; // ensure that it's cleared and not just closed.
debugs(9, 3, HERE << "starting data transfer");
switchTimeoutToDataChannel();
sendMoreRequestBody();
- fwd->dontRetry(true); // dont permit re-trying if the body was sent.
+ fwd->dontRetry(true); // do not permit re-trying if the body was sent.
state = WRITING_DATA;
debugs(9, 3, HERE << "writing data channel");
} else if (code == 150) {
{
assert(entry);
entry->lock("Ftp::Gateway");
- ErrorState ferr(ERR_DIR_LISTING, Http::scOkay, request);
+ ErrorState ferr(ERR_DIR_LISTING, Http::scOkay, request.getRaw(), fwd->al);
ferr.ftp.listing = &listing;
ferr.ftp.cwd_msg = xstrdup(cwd_message.size()? cwd_message.termedBuf() : "");
ferr.ftp.server_msg = ctrl.message;
ctrl.message = NULL;
- entry->replaceHttpReply( ferr.BuildHttpReply() );
- EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
+ entry->replaceHttpReply(ferr.BuildHttpReply());
entry->flush();
entry->unlock("Ftp::Gateway");
}
}
Http::StatusCode sc = ftpState->failedHttpStatus(error_code);
- ErrorState *ftperr = new ErrorState(error_code, sc, ftpState->fwd->request);
+ const auto ftperr = new ErrorState(error_code, sc, ftpState->fwd->request, ftpState->fwd->al);
ftpState->failed(error_code, code, ftperr);
ftperr->detailError(code);
HttpReply *newrep = ftperr->BuildHttpReply();
http_code = Http::scInternalServerError;
}
- ErrorState err(err_code, http_code, ftpState->request);
+ ErrorState err(err_code, http_code, ftpState->request.getRaw(), ftpState->fwd->al);
if (ftpState->old_request)
err.ftp.request = xstrdup(ftpState->old_request);
// TODO: interpret as FTP-specific error code
err.detailError(code);
- ftpState->entry->replaceHttpReply( err.BuildHttpReply() );
+ ftpState->entry->replaceHttpReply(err.BuildHttpReply());
ftpSendQuit(ftpState);
}
assert(entry->isEmpty());
- EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
-
entry->buffer(); /* released when done processing current data payload */
SBuf urlPath = request->url.path();
if (mime_enc)
reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc);
- reply->sources |= HttpMsg::srcFtp;
+ reply->sources |= Http::Message::srcFtp;
setVirginReply(reply);
adaptOrFinalizeReply();
}
e->timestampsSet();
- if (flags.authenticated) {
- /*
- * Authenticated requests can't be cached.
- */
- e->release();
- } else if (!EBIT_TEST(e->flags, RELEASE_REQUEST) && !getCurrentOffset()) {
- e->setPublicKey();
- } else {
+ // makePublic() if allowed/possible or release() otherwise
+ if (flags.authenticated || // authenticated requests can't be cached
+ getCurrentOffset() ||
+ !e->makePublic()) {
e->release();
}
}
HttpReply *
-Ftp::Gateway::ftpAuthRequired(HttpRequest * request, const char *realm)
+Ftp::Gateway::ftpAuthRequired(HttpRequest * request, SBuf &realm, AccessLogEntry::Pointer &ale)
{
- ErrorState err(ERR_CACHE_ACCESS_DENIED, Http::scUnauthorized, request);
+ ErrorState err(ERR_CACHE_ACCESS_DENIED, Http::scUnauthorized, request, ale);
HttpReply *newrep = err.BuildHttpReply();
#if HAVE_AUTH_MODULE_BASIC
/* add Authenticate header */
- newrep->header.putAuth("Basic", realm);
+ // XXX: performance regression. c_str() may reallocate
+ newrep->header.putAuth("Basic", realm.c_str());
#endif
return newrep;
}
Ftp::Gateway::completeForwarding()
{
if (fwd == NULL || flags.completed_forwarding) {
- debugs(9, 3, HERE << "completeForwarding avoids " <<
- "double-complete on FD " << ctrl.conn->fd << ", Data FD " << data.conn->fd <<
+ debugs(9, 3, "avoid double-complete on FD " <<
+ (ctrl.conn ? ctrl.conn->fd : -1) << ", Data FD " << data.conn->fd <<
", this " << this << ", fwd " << fwd);
return;
}