FtpGateway.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 09 File Transfer Protocol (FTP) */
10 
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "base/PackableStream.h"
14 #include "clients/forward.h"
15 #include "clients/FtpClient.h"
16 #include "comm.h"
17 #include "comm/ConnOpener.h"
18 #include "comm/Read.h"
19 #include "comm/TcpAcceptor.h"
20 #include "CommCalls.h"
21 #include "compat/strtoll.h"
22 #include "errorpage.h"
23 #include "fd.h"
24 #include "fde.h"
25 #include "FwdState.h"
26 #include "html_quote.h"
27 #include "HttpHdrContRange.h"
28 #include "HttpHeader.h"
29 #include "HttpHeaderRange.h"
30 #include "HttpReply.h"
31 #include "ip/tools.h"
32 #include "MemBuf.h"
33 #include "mime.h"
34 #include "rfc1738.h"
35 #include "SquidConfig.h"
36 #include "SquidString.h"
37 #include "StatCounters.h"
38 #include "Store.h"
39 #include "tools.h"
40 #include "util.h"
41 #include "wordlist.h"
42 
43 #if USE_DELAY_POOLS
44 #include "DelayPools.h"
45 #include "MemObject.h"
46 #endif
47 
48 #include <cerrno>
49 
50 namespace Ftp
51 {
52 
53 struct GatewayFlags {
54 
55  /* passive mode */
58  bool pasv_only;
59  bool pasv_failed; // was FwdState::flags.ftp_pasv_failed
60 
61  /* authentication */
65 
66  /* other */
67  bool isdir;
71  bool tried_nlst;
73  bool dir_slash;
74  bool root_dir;
75  bool no_dotdot;
76  bool binary;
78  bool put;
79  bool put_mkdir;
81  bool listing;
83 };
84 
85 class Gateway;
86 typedef void (StateMethod)(Ftp::Gateway *);
87 
91 class Gateway : public Ftp::Client
92 {
94 
95 public:
96  Gateway(FwdState *);
97  virtual ~Gateway();
98  char user[MAX_URL];
101  char *reply_hdr;
106  int conn_att;
108  time_t mdtm;
109  int64_t theSize;
111  char *filepath;
112  char *dirpath;
113  int64_t restart_offset;
114  char *proxy_host;
115  size_t list_width;
118  char typecode;
120 
122 
123 public:
124  // these should all be private
125  virtual void start();
127  int restartable();
128  void appendSuccessHeader();
129  void hackShortcut(StateMethod *nextState);
130  void unhack();
131  void readStor();
132  void parseListing();
133  bool htmlifyListEntry(const char *line, PackableStream &);
134  void completedListing(void);
135 
138 
139  int checkAuth(const HttpHeader * req_hdr);
140  void checkUrlpath();
141  void buildTitleUrl();
142  void writeReplyBody(const char *, size_t len);
143  void printfReplyBody(const char *fmt, ...);
144  virtual void completeForwarding();
145  void processHeadResponse();
146  void processReplyBody();
147  void setCurrentOffset(int64_t offset) { currentOffset = offset; }
148  int64_t getCurrentOffset() const { return currentOffset; }
149 
150  virtual void dataChannelConnected(const CommConnectCbParams &io);
151  static PF ftpDataWrite;
152  virtual void timeout(const CommTimeoutCbParams &io);
154 
156  SBuf ftpRealm();
157  void loginFailed(void);
158 
159  virtual void haveParsedReplyHeaders();
160 
161  virtual bool haveControlChannel(const char *caller_name) const;
162 
163 protected:
164  virtual void handleControlReply();
165  virtual void dataClosed(const CommCloseCbParams &io);
166 
167 private:
168  virtual bool mayReadVirginReplyBody() const;
169  // BodyConsumer for HTTP: consume request body.
170  virtual void handleRequestBodyProducerAborted();
171 
172  void loginParser(const SBuf &login, bool escaped);
173 };
174 
175 } // namespace Ftp
176 
177 typedef Ftp::StateMethod FTPSM; // to avoid lots of non-changes
178 
180 
181 typedef struct {
182  char type;
183  int64_t size;
184  char *date;
185  char *name;
186  char *showname;
187  char *link;
188 } ftpListParts;
189 
190 #define CTRL_BUFLEN 16*1024
191 static char cbuf[CTRL_BUFLEN];
192 
193 /*
194  * State machine functions
195  * send == state transition
196  * read == wait for response, and select next state transition
197  * other == Transition logic
198  */
236 static FTPSM ftpFail;
239 
240 /************************************************
241 ** Debugs Levels used here **
242 *************************************************
243 0 CRITICAL Events
244 1 IMPORTANT Events
245  Protocol and Transmission failures.
246 2 FTP Protocol Chatter
247 3 Logic Flows
248 4 Data Parsing Flows
249 5 Data Dumps
250 7 ??
251 ************************************************/
252 
253 /************************************************
254 ** State Machine Description (excluding hacks) **
255 *************************************************
256 From To
257 ---------------------------------------
258 Welcome User
259 User Pass
260 Pass Type
261 Type TraverseDirectory / GetFile
262 TraverseDirectory Cwd / GetFile / ListDir
263 Cwd TraverseDirectory / Mkdir
264 GetFile Mdtm
265 Mdtm Size
266 Size Epsv
267 ListDir Epsv
268 Epsv FileOrList
269 FileOrList Rest / Retr / Nlst / List / Mkdir (PUT /xxx;type=d)
270 Rest Retr
271 Retr / Nlst / List DataRead* (on datachannel)
272 DataRead* ReadTransferDone
273 ReadTransferDone DataTransferDone
274 Stor DataWrite* (on datachannel)
275 DataWrite* RequestPutBody** (from client)
276 RequestPutBody** DataWrite* / WriteTransferDone
277 WriteTransferDone DataTransferDone
278 DataTransferDone Quit
279 Quit -
280 ************************************************/
281 
283  ftpReadWelcome, /* BEGIN */
284  ftpReadUser, /* SENT_USER */
285  ftpReadPass, /* SENT_PASS */
286  ftpReadType, /* SENT_TYPE */
287  ftpReadMdtm, /* SENT_MDTM */
288  ftpReadSize, /* SENT_SIZE */
289  ftpReadEPRT, /* SENT_EPRT */
290  ftpReadPORT, /* SENT_PORT */
291  ftpReadEPSV, /* SENT_EPSV_ALL */
292  ftpReadEPSV, /* SENT_EPSV_1 */
293  ftpReadEPSV, /* SENT_EPSV_2 */
294  ftpReadPasv, /* SENT_PASV */
295  ftpReadCwd, /* SENT_CWD */
296  ftpReadList, /* SENT_LIST */
297  ftpReadList, /* SENT_NLST */
298  ftpReadRest, /* SENT_REST */
299  ftpReadRetr, /* SENT_RETR */
300  ftpReadStor, /* SENT_STOR */
301  ftpReadQuit, /* SENT_QUIT */
302  ftpReadTransferDone, /* READING_DATA (RETR,LIST,NLST) */
303  ftpWriteTransferDone, /* WRITING_DATA (STOR) */
304  ftpReadMkdir, /* SENT_MKDIR */
305  NULL, /* SENT_FEAT */
306  NULL, /* SENT_PWD */
307  NULL, /* SENT_CDUP*/
308  NULL, /* SENT_DATA_REQUEST */
309  NULL /* SENT_COMMAND */
310 };
311 
313 void
315 {
318  /* failed closes ctrl.conn and frees ftpState */
319 
320  /* NP: failure recovery may be possible when its only a data.conn failure.
321  * if the ctrl.conn is still fine, we can send ABOR down it and retry.
322  * Just need to watch out for wider Squid states like shutting down or reconfigure.
323  */
324 }
325 
327  AsyncJob("FtpStateData"),
328  Ftp::Client(fwdState),
329  password_url(0),
330  reply_hdr(NULL),
331  reply_hdr_state(0),
332  conn_att(0),
333  login_att(0),
334  mdtm(-1),
335  theSize(-1),
336  pathcomps(NULL),
337  filepath(NULL),
338  dirpath(NULL),
339  restart_offset(0),
340  proxy_host(NULL),
341  list_width(0),
342  old_filepath(NULL),
343  typecode('\0')
344 {
345  debugs(9, 3, entry->url());
346 
347  *user = 0;
348  *password = 0;
349  memset(&flags, 0, sizeof(flags));
350 
352  flags.pasv_supported = 1;
353 
354  flags.rest_supported = 1;
355 
357  flags.put = 1;
358 
359  initReadBuf();
360 }
361 
363 {
364  debugs(9, 3, entry->url());
365 
366  if (Comm::IsConnOpen(ctrl.conn)) {
367  debugs(9, DBG_IMPORTANT, "ERROR: Squid BUG: FTP Gateway left open " <<
368  "control channel " << ctrl.conn);
369  }
370 
371  if (reply_hdr) {
372  memFree(reply_hdr, MEM_8K_BUF);
373  reply_hdr = NULL;
374  }
375 
376  if (pathcomps)
377  wordlistDestroy(&pathcomps);
378 
379  cwd_message.clean();
380  xfree(old_filepath);
381  title_url.clean();
382  base_href.clean();
383  xfree(filepath);
384  xfree(dirpath);
385 }
386 
394 void
395 Ftp::Gateway::loginParser(const SBuf &login, bool escaped)
396 {
397  debugs(9, 4, "login=" << login << ", escaped=" << escaped);
398  debugs(9, 9, "IN : login=" << login << ", escaped=" << escaped << ", user=" << user << ", password=" << password);
399 
400  if (login.isEmpty())
401  return;
402 
403  const SBuf::size_type colonPos = login.find(':');
404 
405  /* If there was a username part with at least one character use it.
406  * Ignore 0-length username portion, retain what we have already.
407  */
408  if (colonPos == SBuf::npos || colonPos > 0) {
409  const SBuf userName = login.substr(0, colonPos);
410  SBuf::size_type upto = userName.copy(user, sizeof(user)-1);
411  user[upto]='\0';
412  debugs(9, 9, "found user=" << userName << ' ' <<
413  (upto != userName.length() ? ", truncated-to=" : ", length=") << upto <<
414  ", escaped=" << escaped);
415  if (escaped)
416  rfc1738_unescape(user);
417  debugs(9, 9, "found user=" << user << " (" << strlen(user) << ") unescaped.");
418  }
419 
420  /* If there was a password part.
421  * For 0-length password clobber what we have already, this means explicitly none
422  */
423  if (colonPos != SBuf::npos) {
424  const SBuf pass = login.substr(colonPos+1, SBuf::npos);
425  SBuf::size_type upto = pass.copy(password, sizeof(password)-1);
426  password[upto]='\0';
427  debugs(9, 9, "found password=" << pass << " " <<
428  (upto != pass.length() ? ", truncated-to=" : ", length=") << upto <<
429  ", escaped=" << escaped);
430  if (escaped) {
431  rfc1738_unescape(password);
432  password_url = 1;
433  }
434  debugs(9, 9, "found password=" << password << " (" << strlen(password) << ") unescaped.");
435  }
436 
437  debugs(9, 9, "OUT: login=" << login << ", escaped=" << escaped << ", user=" << user << ", password=" << password);
438 }
439 
440 void
442 {
443  if (!Comm::IsConnOpen(ctrl.conn)) {
444  debugs(9, 5, "The control connection to the remote end is closed");
445  return;
446  }
447 
448  assert(!Comm::IsConnOpen(data.conn));
449 
450  typedef CommCbMemFunT<Gateway, CommAcceptCbParams> AcceptDialer;
451  typedef AsyncCallT<AcceptDialer> AcceptCall;
452  RefCount<AcceptCall> call = static_cast<AcceptCall*>(JobCallback(11, 5, AcceptDialer, this, Ftp::Gateway::ftpAcceptDataConnection));
454  const char *note = entry->url();
455 
456  /* open the conn if its not already open */
457  if (!Comm::IsConnOpen(conn)) {
458  conn->fd = comm_open_listener(SOCK_STREAM, IPPROTO_TCP, conn->local, conn->flags, note);
459  if (!Comm::IsConnOpen(conn)) {
460  debugs(5, DBG_CRITICAL, "ERROR: comm_open_listener failed:" << conn->local << " error: " << errno);
461  return;
462  }
463  debugs(9, 3, "Unconnected data socket created on " << conn);
464  }
465 
466  conn->tos = ctrl.conn->tos;
467  conn->nfmark = ctrl.conn->nfmark;
468 
470  AsyncJob::Start(new Comm::TcpAcceptor(conn, note, sub));
471 
472  // Ensure we have a copy of the FD opened for listening and a close handler on it.
473  data.opened(conn, dataCloser());
474  switchTimeoutToDataChannel();
475 }
476 
477 void
479 {
480  if (SENT_PASV == state) {
481  /* stupid ftp.netscape.com, of FTP server behind stupid firewall rules */
482  flags.pasv_supported = false;
483  debugs(9, DBG_IMPORTANT, "FTP Gateway timeout in SENT_PASV state");
484 
485  // cancel the data connection setup, if any
486  dataConnWait.cancel("timeout");
487 
488  data.close();
489  }
490 
492 }
493 
494 static const char *Month[] = {
495  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
496  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
497 };
498 
499 static int
500 is_month(const char *buf)
501 {
502  int i;
503 
504  for (i = 0; i < 12; ++i)
505  if (!strcasecmp(buf, Month[i]))
506  return 1;
507 
508  return 0;
509 }
510 
511 static void
513 {
514  safe_free((*parts)->date);
515  safe_free((*parts)->name);
516  safe_free((*parts)->showname);
517  safe_free((*parts)->link);
518  safe_free(*parts);
519 }
520 
521 #define MAX_TOKENS 64
522 
523 static ftpListParts *
524 ftpListParseParts(const char *buf, struct Ftp::GatewayFlags flags)
525 {
526  ftpListParts *p = NULL;
527  char *t = NULL;
528  struct FtpLineToken {
529  char *token = nullptr;
530  size_t pos = 0;
531  } tokens[MAX_TOKENS];
532  int i;
533  int n_tokens;
534  static char tbuf[128];
535  char *xbuf = NULL;
536  static int scan_ftp_initialized = 0;
537  static regex_t scan_ftp_integer;
538  static regex_t scan_ftp_time;
539  static regex_t scan_ftp_dostime;
540  static regex_t scan_ftp_dosdate;
541 
542  if (!scan_ftp_initialized) {
543  scan_ftp_initialized = 1;
544  regcomp(&scan_ftp_integer, "^[0123456789]+$", REG_EXTENDED | REG_NOSUB);
545  regcomp(&scan_ftp_time, "^[0123456789:]+$", REG_EXTENDED | REG_NOSUB);
546  regcomp(&scan_ftp_dosdate, "^[0123456789]+-[0123456789]+-[0123456789]+$", REG_EXTENDED | REG_NOSUB);
547  regcomp(&scan_ftp_dostime, "^[0123456789]+:[0123456789]+[AP]M$", REG_EXTENDED | REG_NOSUB | REG_ICASE);
548  }
549 
550  if (buf == NULL)
551  return NULL;
552 
553  if (*buf == '\0')
554  return NULL;
555 
556  p = (ftpListParts *)xcalloc(1, sizeof(ftpListParts));
557 
558  n_tokens = 0;
559 
560  xbuf = xstrdup(buf);
561 
562  if (flags.tried_nlst) {
563  /* Machine readable format, one name per line */
564  p->name = xbuf;
565  p->type = '\0';
566  return p;
567  }
568 
569  for (t = strtok(xbuf, w_space); t && n_tokens < MAX_TOKENS; t = strtok(NULL, w_space)) {
570  tokens[n_tokens].token = xstrdup(t);
571  tokens[n_tokens].pos = t - xbuf;
572  ++n_tokens;
573  }
574 
575  xfree(xbuf);
576 
577  /* locate the Month field */
578  for (i = 3; i < n_tokens - 2; ++i) {
579  const auto size = tokens[i - 1].token;
580  char *month = tokens[i].token;
581  char *day = tokens[i + 1].token;
582  char *year = tokens[i + 2].token;
583 
584  if (!is_month(month))
585  continue;
586 
587  if (regexec(&scan_ftp_integer, size, 0, NULL, 0) != 0)
588  continue;
589 
590  if (regexec(&scan_ftp_integer, day, 0, NULL, 0) != 0)
591  continue;
592 
593  if (regexec(&scan_ftp_time, year, 0, NULL, 0) != 0) /* Yr | hh:mm */
594  continue;
595 
596  const auto *copyFrom = buf + tokens[i].pos;
597 
598  // "MMM DD [ YYYY|hh:mm]" with at most two spaces between DD and YYYY
599  auto dateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %5s", month, day, year);
600  bool isTypeA = (dateSize == 12) && (strncmp(copyFrom, tbuf, dateSize) == 0);
601 
602  // "MMM DD [YYYY|hh:mm]" with one space between DD and YYYY
603  dateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %-5s", month, day, year);
604  bool isTypeB = (dateSize == 12 || dateSize == 11) && (strncmp(copyFrom, tbuf, dateSize) == 0);
605 
606  // TODO: replace isTypeA and isTypeB with a regex.
607  if (isTypeA || isTypeB) {
608  p->type = *tokens[0].token;
609  p->size = strtoll(size, NULL, 10);
610  const auto finalDateSize = snprintf(tbuf, sizeof(tbuf), "%s %2s %5s", month, day, year);
611  assert(finalDateSize >= 0);
612  p->date = xstrdup(tbuf);
613 
614  // point after tokens[i+2] :
615  copyFrom = buf + tokens[i + 2].pos + strlen(tokens[i + 2].token);
616  if (flags.skip_whitespace) {
617  while (strchr(w_space, *copyFrom))
618  ++copyFrom;
619  } else {
620  /* Handle the following four formats:
621  * "MMM DD YYYY Name"
622  * "MMM DD YYYYName"
623  * "MMM DD YYYY Name"
624  * "MMM DD YYYY Name"
625  * Assuming a single space between date and filename
626  * suggested by: Nathan.Bailey@cc.monash.edu.au and
627  * Mike Battersby <mike@starbug.bofh.asn.au> */
628  if (strchr(w_space, *copyFrom))
629  ++copyFrom;
630  }
631 
632  p->name = xstrdup(copyFrom);
633 
634  if (p->type == 'l' && (t = strstr(p->name, " -> "))) {
635  *t = '\0';
636  p->link = xstrdup(t + 4);
637  }
638 
639  goto found;
640  }
641 
642  break;
643  }
644 
645  /* try it as a DOS listing, 04-05-70 09:33PM ... */
646  if (n_tokens > 3 &&
647  regexec(&scan_ftp_dosdate, tokens[0].token, 0, NULL, 0) == 0 &&
648  regexec(&scan_ftp_dostime, tokens[1].token, 0, NULL, 0) == 0) {
649  if (!strcasecmp(tokens[2].token, "<dir>")) {
650  p->type = 'd';
651  } else {
652  p->type = '-';
653  p->size = strtoll(tokens[2].token, NULL, 10);
654  }
655 
656  snprintf(tbuf, sizeof(tbuf), "%s %s", tokens[0].token, tokens[1].token);
657  p->date = xstrdup(tbuf);
658 
659  if (p->type == 'd') {
660  // Directory.. name begins with first printable after <dir>
661  // Because of the "n_tokens > 3", the next printable after <dir>
662  // is stored at token[3]. No need for more checks here.
663  } else {
664  // A file. Name begins after size, with a space in between.
665  // Also a space should exist before size.
666  // But there is not needed to be very strict with spaces.
667  // The name is stored at token[3], take it from here.
668  }
669 
670  p->name = xstrdup(tokens[3].token);
671  goto found;
672  }
673 
674  /* Try EPLF format; carson@lehman.com */
675  if (buf[0] == '+') {
676  const char *ct = buf + 1;
677  p->type = 0;
678 
679  while (ct && *ct) {
680  time_t tm;
681  int l = strcspn(ct, ",");
682  char *tmp;
683 
684  if (l < 1)
685  goto blank;
686 
687  switch (*ct) {
688 
689  case '\t':
690  p->name = xstrndup(ct + 1, l + 1);
691  break;
692 
693  case 's':
694  p->size = atoi(ct + 1);
695  break;
696 
697  case 'm':
698  tm = (time_t) strtol(ct + 1, &tmp, 0);
699 
700  if (tmp != ct + 1)
701  break; /* not a valid integer */
702 
703  p->date = xstrdup(ctime(&tm));
704 
705  *(strstr(p->date, "\n")) = '\0';
706 
707  break;
708 
709  case '/':
710  p->type = 'd';
711 
712  break;
713 
714  case 'r':
715  p->type = '-';
716 
717  break;
718 
719  case 'i':
720  break;
721 
722  default:
723  break;
724  }
725 
726 blank:
727  ct = strstr(ct, ",");
728 
729  if (ct) {
730  ++ct;
731  }
732  }
733 
734  if (p->type == 0) {
735  p->type = '-';
736  }
737 
738  if (p->name)
739  goto found;
740  else
741  safe_free(p->date);
742  }
743 
744 found:
745 
746  for (i = 0; i < n_tokens; ++i)
747  xfree(tokens[i].token);
748 
749  if (!p->name)
750  ftpListPartsFree(&p); /* cleanup */
751 
752  return p;
753 }
754 
755 bool
757 {
758  debugs(9, 7, "line={" << line << "}");
759 
760  if (strlen(line) > 1024) {
761  html << "<tr><td colspan=\"5\">" << line << "</td></tr>\n";
762  return true;
763  }
764 
765  SBuf prefix;
766  if (flags.dir_slash && dirpath && typecode != 'D') {
767  prefix.append(rfc1738_escape_part(dirpath));
768  prefix.append("/", 1);
769  }
770 
771  ftpListParts *parts = ftpListParseParts(line, flags);
772  if (!parts) {
773  html << "<tr class=\"entry\"><td colspan=\"5\">" << line << "</td></tr>\n";
774 
775  const char *p;
776  for (p = line; *p && xisspace(*p); ++p);
777  if (*p && !xisspace(*p))
778  flags.listformat_unknown = 1;
779 
780  return true;
781  }
782 
783  if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) {
784  ftpListPartsFree(&parts);
785  return false;
786  }
787 
788  parts->size += 1023;
789  parts->size >>= 10;
790  parts->showname = xstrdup(parts->name);
791 
792  /* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}\n */
793  SBuf href(prefix);
794  href.append(rfc1738_escape_part(parts->name));
795 
796  SBuf text(parts->showname);
797 
798  SBuf icon, size, chdir, link;
799  switch (parts->type) {
800 
801  case 'd':
802  icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
803  mimeGetIconURL("internal-dir"),
804  "[DIR]");
805  href.append("/", 1); /* margin is allocated above */
806  break;
807 
808  case 'l':
809  icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
810  mimeGetIconURL("internal-link"),
811  "[LINK]");
812  /* sometimes there is an 'l' flag, but no "->" link */
813 
814  if (parts->link) {
815  SBuf link2(html_quote(rfc1738_escape(parts->link)));
816  link.appendf(" -&gt; <a href=\"%s" SQUIDSBUFPH "\">%s</a>",
817  link2[0] != '/' ? prefix.c_str() : "", SQUIDSBUFPRINT(link2),
818  html_quote(parts->link));
819  }
820 
821  break;
822 
823  case '\0':
824  icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
825  mimeGetIconURL(parts->name),
826  "[UNKNOWN]");
827  chdir.appendf("<a href=\"%s/;type=d\"><img border=\"0\" src=\"%s\" "
828  "alt=\"[DIR]\"></a>",
829  rfc1738_escape_part(parts->name),
830  mimeGetIconURL("internal-dir"));
831  break;
832 
833  case '-':
834 
835  default:
836  icon.appendf("<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
837  mimeGetIconURL(parts->name),
838  "[FILE]");
839  size.appendf(" %6" PRId64 "k", parts->size);
840  break;
841  }
842 
843  SBuf view, download;
844  if (parts->type != 'd') {
845  if (mimeGetViewOption(parts->name)) {
846  view.appendf("<a href=\"" SQUIDSBUFPH ";type=a\"><img border=\"0\" src=\"%s\" "
847  "alt=\"[VIEW]\"></a>",
848  SQUIDSBUFPRINT(href), mimeGetIconURL("internal-view"));
849  }
850 
851  if (mimeGetDownloadOption(parts->name)) {
852  download.appendf("<a href=\"" SQUIDSBUFPH ";type=i\"><img border=\"0\" src=\"%s\" "
853  "alt=\"[DOWNLOAD]\"></a>",
854  SQUIDSBUFPRINT(href), mimeGetIconURL("internal-download"));
855  }
856  }
857 
858  /* construct the table row from parts. */
859  html << "<tr class=\"entry\">"
860  "<td class=\"icon\"><a href=\"" << href << "\">" << icon << "</a></td>"
861  "<td class=\"filename\"><a href=\"" << href << "\">" << html_quote(text.c_str()) << "</a></td>"
862  "<td class=\"date\">" << parts->date << "</td>"
863  "<td class=\"size\">" << size << "</td>"
864  "<td class=\"actions\">" << chdir << view << download << link << "</td>"
865  "</tr>\n";
866 
867  ftpListPartsFree(&parts);
868  return true;
869 }
870 
871 void
873 {
874  char *buf = data.readBuf->content();
875  char *sbuf; /* NULL-terminated copy of termedBuf */
876  char *end;
877  char *line;
878  char *s;
879  size_t linelen;
880  size_t usable;
881  size_t len = data.readBuf->contentSize();
882 
883  if (!len) {
884  debugs(9, 3, "no content to parse for " << entry->url() );
885  return;
886  }
887 
888  /*
889  * We need a NULL-terminated buffer for scanning, ick
890  */
891  sbuf = (char *)xmalloc(len + 1);
892  xstrncpy(sbuf, buf, len + 1);
893  end = sbuf + len - 1;
894 
895  while (*end != '\r' && *end != '\n' && end > sbuf)
896  --end;
897 
898  usable = end - sbuf;
899 
900  debugs(9, 3, "usable = " << usable << " of " << len << " bytes.");
901 
902  if (usable == 0) {
903  if (buf[0] == '\0' && len == 1) {
904  debugs(9, 3, "NIL ends data from " << entry->url() << " transfer problem?");
905  data.readBuf->consume(len);
906  } else {
907  debugs(9, 3, "didn't find end for " << entry->url());
908  debugs(9, 3, "buffer remains (" << len << " bytes) '" << rfc1738_do_escape(buf,0) << "'");
909  }
910  xfree(sbuf);
911  return;
912  }
913 
914  debugs(9, 3, (unsigned long int)len << " bytes to play with");
915 
916  line = (char *)memAllocate(MEM_4K_BUF);
917  ++end;
918  s = sbuf;
919  s += strspn(s, crlf);
920 
921  for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
922  debugs(9, 7, "s = {" << s << "}");
923  linelen = strcspn(s, crlf) + 1;
924 
925  if (linelen < 2)
926  break;
927 
928  if (linelen > 4096)
929  linelen = 4096;
930 
931  xstrncpy(line, s, linelen);
932 
933  debugs(9, 7, "{" << line << "}");
934 
935  if (!strncmp(line, "total", 5))
936  continue;
937 
938  MemBuf htmlPage;
939  htmlPage.init();
940  PackableStream html(htmlPage);
941 
942  if (htmlifyListEntry(line, html)) {
943  html.flush();
944  debugs(9, 7, "listing append: t = {" << htmlPage.contentSize() << ", '" << htmlPage.content() << "'}");
945  listing.append(htmlPage.content(), htmlPage.contentSize());
946  }
947  }
948 
949  debugs(9, 7, "Done.");
950  data.readBuf->consume(usable);
951  memFree(line, MEM_4K_BUF);
952  xfree(sbuf);
953 }
954 
955 void
957 {
958  debugs(9, 3, status());
959 
960  if (request->method == Http::METHOD_HEAD && (flags.isdir || theSize != -1)) {
961  serverComplete();
962  return;
963  }
964 
965  /* Directory listings are special. They write ther own headers via the error objects */
966  if (!flags.http_header_sent && data.readBuf->contentSize() >= 0 && !flags.isdir)
967  appendSuccessHeader();
968 
969  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
970  /*
971  * probably was aborted because content length exceeds one
972  * of the maximum size limits.
973  */
974  abortAll("entry aborted after calling appendSuccessHeader()");
975  return;
976  }
977 
978 #if USE_ADAPTATION
979 
980  if (adaptationAccessCheckPending) {
981  debugs(9, 3, "returning from Ftp::Gateway::processReplyBody due to adaptationAccessCheckPending");
982  return;
983  }
984 
985 #endif
986 
987  if (flags.isdir) {
988  if (!flags.listing) {
989  flags.listing = 1;
990  listing.reset();
991  }
992  parseListing();
993  maybeReadVirginBody();
994  return;
995  } else if (const int csize = data.readBuf->contentSize()) {
996  writeReplyBody(data.readBuf->content(), csize);
997  debugs(9, 5, "consuming " << csize << " bytes of readBuf");
998  data.readBuf->consume(csize);
999  }
1000 
1001  entry->flush();
1002 
1003  maybeReadVirginBody();
1004 }
1005 
1022 int
1024 {
1025  /* default username */
1026  xstrncpy(user, "anonymous", MAX_URL);
1027 
1028 #if HAVE_AUTH_MODULE_BASIC
1029  /* Check HTTP Authorization: headers (better than defaults, but less than URL) */
1030  const auto auth(req_hdr->getAuthToken(Http::HdrType::AUTHORIZATION, "Basic"));
1031  if (!auth.isEmpty()) {
1032  flags.authenticated = 1;
1033  loginParser(auth, false);
1034  }
1035  /* we fail with authorization-required error later IFF the FTP server requests it */
1036 #else
1037  (void)req_hdr;
1038 #endif
1039 
1040  /* Test URL login syntax. Overrides any headers received. */
1041  loginParser(request->url.userInfo(), true);
1042 
1043  /* name is missing. that's fatal. */
1044  if (!user[0])
1045  fatal("FTP login parsing destroyed username info");
1046 
1047  /* name + password == success */
1048  if (password[0])
1049  return 1;
1050 
1051  /* Setup default FTP password settings */
1052  /* this has to be done last so that we can have a no-password case above. */
1053  if (!password[0]) {
1054  if (strcmp(user, "anonymous") == 0 && !flags.tried_auth_anonymous) {
1055  xstrncpy(password, Config.Ftp.anon_user, MAX_URL);
1056  flags.tried_auth_anonymous=1;
1057  return 1;
1058  } else if (!flags.tried_auth_nopass) {
1059  xstrncpy(password, null_string, MAX_URL);
1060  flags.tried_auth_nopass=1;
1061  return 1;
1062  }
1063  }
1064 
1065  return 0; /* different username */
1066 }
1067 
1068 void
1070 {
1071  // If typecode was specified, extract it and leave just the filename in
1072  // url.path. Tolerate trailing garbage or missing typecode value. Roughly:
1073  // [filename] ;type=[typecode char] [trailing garbage]
1074  static const SBuf middle(";type=");
1075  const auto typeSpecStart = request->url.path().find(middle);
1076  if (typeSpecStart != SBuf::npos) {
1077  const auto fullPath = request->url.path();
1078  const auto typecodePos = typeSpecStart + middle.length();
1079  typecode = (typecodePos < fullPath.length()) ?
1080  static_cast<char>(xtoupper(fullPath[typecodePos])) : '\0';
1081  request->url.path(fullPath.substr(0, typeSpecStart));
1082  }
1083 
1084  int l = request->url.path().length();
1085  /* check for null path */
1086 
1087  if (!l) {
1088  flags.isdir = 1;
1089  flags.root_dir = 1;
1090  flags.need_base_href = 1; /* Work around broken browsers */
1091  } else if (!request->url.path().cmp("/%2f/")) {
1092  /* UNIX root directory */
1093  flags.isdir = 1;
1094  flags.root_dir = 1;
1095  } else if ((l >= 1) && (request->url.path()[l-1] == '/')) {
1096  /* Directory URL, ending in / */
1097  flags.isdir = 1;
1098 
1099  if (l == 1)
1100  flags.root_dir = 1;
1101  } else {
1102  flags.dir_slash = 1;
1103  }
1104 }
1105 
1106 void
1108 {
1109  title_url = "ftp://";
1110 
1111  if (strcmp(user, "anonymous")) {
1112  title_url.append(user);
1113  title_url.append("@");
1114  }
1115 
1116  SBuf authority = request->url.authority(request->url.getScheme() != AnyP::PROTO_FTP);
1117 
1118  title_url.append(authority.rawContent(), authority.length());
1119  title_url.append(request->url.path().rawContent(), request->url.path().length());
1120 
1121  base_href = "ftp://";
1122 
1123  if (strcmp(user, "anonymous") != 0) {
1124  base_href.append(rfc1738_escape_part(user));
1125 
1126  if (password_url) {
1127  base_href.append(":");
1128  base_href.append(rfc1738_escape_part(password));
1129  }
1130 
1131  base_href.append("@");
1132  }
1133 
1134  base_href.append(authority.rawContent(), authority.length());
1135  base_href.append(request->url.path().rawContent(), request->url.path().length());
1136  base_href.append("/");
1137 }
1138 
1139 void
1141 {
1142  if (!checkAuth(&request->header)) {
1143  /* create appropriate reply */
1144  SBuf realm(ftpRealm()); // local copy so SBuf will not disappear too early
1145  const auto reply = ftpAuthRequired(request.getRaw(), realm, fwd->al);
1146  entry->replaceHttpReply(reply);
1147  serverComplete();
1148  return;
1149  }
1150 
1151  checkUrlpath();
1152  buildTitleUrl();
1153  debugs(9, 5, "FD " << (ctrl.conn ? ctrl.conn->fd : -1) << " : host=" << request->url.host() <<
1154  ", path=" << request->url.path() << ", user=" << user << ", passwd=" << password);
1155  state = BEGIN;
1157 }
1158 
1159 /* ====================================================================== */
1160 
1161 void
1163 {
1165  if (ctrl.message == NULL)
1166  return; // didn't get complete reply yet
1167 
1168  /* Copy the message except for the last line to cwd_message to be
1169  * printed in error messages.
1170  */
1171  for (wordlist *w = ctrl.message; w && w->next; w = w->next) {
1172  cwd_message.append('\n');
1173  cwd_message.append(w->key);
1174  }
1175 
1176  FTP_SM_FUNCS[state] (this);
1177 }
1178 
1179 /* ====================================================================== */
1180 
1181 static void
1183 {
1184  int code = ftpState->ctrl.replycode;
1185  debugs(9, 3, MYNAME);
1186 
1187  if (ftpState->flags.pasv_only)
1188  ++ ftpState->login_att;
1189 
1190  if (code == 220) {
1191  if (ftpState->ctrl.message) {
1192  if (strstr(ftpState->ctrl.message->key, "NetWare"))
1193  ftpState->flags.skip_whitespace = 1;
1194  }
1195 
1196  ftpSendUser(ftpState);
1197  } else if (code == 120) {
1198  if (NULL != ftpState->ctrl.message)
1199  debugs(9, DBG_IMPORTANT, "FTP server is busy: " << ftpState->ctrl.message->key);
1200 
1201  return;
1202  } else {
1203  ftpFail(ftpState);
1204  }
1205 }
1206 
1212 void
1214 {
1215  ErrorState *err = NULL;
1216 
1217  if ((state == SENT_USER || state == SENT_PASS) && ctrl.replycode >= 400) {
1218  if (ctrl.replycode == 421 || ctrl.replycode == 426) {
1219  // 421/426 - Service Overload - retry permitted.
1220  err = new ErrorState(ERR_FTP_UNAVAILABLE, Http::scServiceUnavailable, fwd->request, fwd->al);
1221  } else if (ctrl.replycode >= 430 && ctrl.replycode <= 439) {
1222  // 43x - Invalid or Credential Error - retry challenge required.
1223  err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request, fwd->al);
1224  } else if (ctrl.replycode >= 530 && ctrl.replycode <= 539) {
1225  // 53x - Credentials Missing - retry challenge required
1226  if (password_url) // but they were in the URI! major fail.
1227  err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scForbidden, fwd->request, fwd->al);
1228  else
1229  err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request, fwd->al);
1230  }
1231  }
1232 
1233  if (!err) {
1234  ftpFail(this);
1235  return;
1236  }
1237 
1238  failed(ERR_NONE, ctrl.replycode, err);
1239  // any other problems are general falures.
1240 
1241  HttpReply *newrep = err->BuildHttpReply();
1242  delete err;
1243 
1244 #if HAVE_AUTH_MODULE_BASIC
1245  /* add Authenticate header */
1246  // XXX: performance regression. c_str() may reallocate
1247  SBuf realm(ftpRealm()); // local copy so SBuf will not disappear too early
1248  newrep->header.putAuth("Basic", realm.c_str());
1249 #endif
1250 
1251  // add it to the store entry for response....
1252  entry->replaceHttpReply(newrep);
1253  serverComplete();
1254 }
1255 
1256 SBuf
1258 {
1259  SBuf realm;
1260 
1261  /* This request is not fully authenticated */
1262  realm.appendf("FTP %s ", user);
1263  if (!request)
1264  realm.append("unknown", 7);
1265  else {
1266  realm.append(request->url.host());
1267  if (request->url.port() != 21)
1268  realm.appendf(" port %d", request->url.port());
1269  }
1270  return realm;
1271 }
1272 
1273 static void
1275 {
1276  /* check the server control channel is still available */
1277  if (!ftpState || !ftpState->haveControlChannel("ftpSendUser"))
1278  return;
1279 
1280  if (ftpState->proxy_host != NULL)
1281  snprintf(cbuf, CTRL_BUFLEN, "USER %s@%s\r\n", ftpState->user, ftpState->request->url.host());
1282  else
1283  snprintf(cbuf, CTRL_BUFLEN, "USER %s\r\n", ftpState->user);
1284 
1285  ftpState->writeCommand(cbuf);
1286 
1287  ftpState->state = Ftp::Client::SENT_USER;
1288 }
1289 
1290 static void
1292 {
1293  int code = ftpState->ctrl.replycode;
1294  debugs(9, 3, MYNAME);
1295 
1296  if (code == 230) {
1297  ftpReadPass(ftpState);
1298  } else if (code == 331) {
1299  ftpSendPass(ftpState);
1300  } else {
1301  ftpState->loginFailed();
1302  }
1303 }
1304 
1305 static void
1307 {
1308  /* check the server control channel is still available */
1309  if (!ftpState || !ftpState->haveControlChannel("ftpSendPass"))
1310  return;
1311 
1312  snprintf(cbuf, CTRL_BUFLEN, "PASS %s\r\n", ftpState->password);
1313  ftpState->writeCommand(cbuf);
1314  ftpState->state = Ftp::Client::SENT_PASS;
1315 }
1316 
1317 static void
1319 {
1320  int code = ftpState->ctrl.replycode;
1321  debugs(9, 3, "code=" << code);
1322 
1323  if (code == 230) {
1324  ftpSendType(ftpState);
1325  } else {
1326  ftpState->loginFailed();
1327  }
1328 }
1329 
1330 static void
1332 {
1333  /* check the server control channel is still available */
1334  if (!ftpState || !ftpState->haveControlChannel("ftpSendType"))
1335  return;
1336 
1337  /*
1338  * Ref section 3.2.2 of RFC 1738
1339  */
1340  char mode = ftpState->typecode;
1341 
1342  switch (mode) {
1343 
1344  case 'D':
1345  mode = 'A';
1346  break;
1347 
1348  case 'A':
1349 
1350  case 'I':
1351  break;
1352 
1353  default:
1354 
1355  if (ftpState->flags.isdir) {
1356  mode = 'A';
1357  } else {
1358  auto t = ftpState->request->url.path().rfind('/');
1359  // XXX: performance regression, c_str() may reallocate
1360  SBuf filename = ftpState->request->url.path().substr(t != SBuf::npos ? t + 1 : 0);
1361  mode = mimeGetTransferMode(filename.c_str());
1362  }
1363 
1364  break;
1365  }
1366 
1367  if (mode == 'I')
1368  ftpState->flags.binary = 1;
1369  else
1370  ftpState->flags.binary = 0;
1371 
1372  snprintf(cbuf, CTRL_BUFLEN, "TYPE %c\r\n", mode);
1373 
1374  ftpState->writeCommand(cbuf);
1375 
1376  ftpState->state = Ftp::Client::SENT_TYPE;
1377 }
1378 
1379 static void
1381 {
1382  int code = ftpState->ctrl.replycode;
1383  char *path;
1384  char *d, *p;
1385  debugs(9, 3, "code=" << code);
1386 
1387  if (code == 200) {
1388  p = path = SBufToCstring(ftpState->request->url.path());
1389 
1390  if (*p == '/')
1391  ++p;
1392 
1393  while (*p) {
1394  d = p;
1395  p += strcspn(p, "/");
1396 
1397  if (*p) {
1398  *p = '\0';
1399  ++p;
1400  }
1401 
1402  rfc1738_unescape(d);
1403 
1404  if (*d)
1405  wordlistAdd(&ftpState->pathcomps, d);
1406  }
1407 
1408  xfree(path);
1409 
1410  if (ftpState->pathcomps)
1411  ftpTraverseDirectory(ftpState);
1412  else
1413  ftpListDir(ftpState);
1414  } else {
1415  ftpFail(ftpState);
1416  }
1417 }
1418 
1419 static void
1421 {
1422  debugs(9, 4, (ftpState->filepath ? ftpState->filepath : "<NULL>"));
1423 
1424  safe_free(ftpState->dirpath);
1425  ftpState->dirpath = ftpState->filepath;
1426  ftpState->filepath = NULL;
1427 
1428  /* Done? */
1429 
1430  if (ftpState->pathcomps == NULL) {
1431  debugs(9, 3, "the final component was a directory");
1432  ftpListDir(ftpState);
1433  return;
1434  }
1435 
1436  /* Go to next path component */
1437  ftpState->filepath = wordlistChopHead(& ftpState->pathcomps);
1438 
1439  /* Check if we are to CWD or RETR */
1440  if (ftpState->pathcomps != NULL || ftpState->flags.isdir) {
1441  ftpSendCwd(ftpState);
1442  } else {
1443  debugs(9, 3, "final component is probably a file");
1444  ftpGetFile(ftpState);
1445  return;
1446  }
1447 }
1448 
1449 static void
1451 {
1452  char *path = NULL;
1453 
1454  /* check the server control channel is still available */
1455  if (!ftpState || !ftpState->haveControlChannel("ftpSendCwd"))
1456  return;
1457 
1458  debugs(9, 3, MYNAME);
1459 
1460  path = ftpState->filepath;
1461 
1462  if (!strcmp(path, "..") || !strcmp(path, "/")) {
1463  ftpState->flags.no_dotdot = 1;
1464  } else {
1465  ftpState->flags.no_dotdot = 0;
1466  }
1467 
1468  snprintf(cbuf, CTRL_BUFLEN, "CWD %s\r\n", path);
1469 
1470  ftpState->writeCommand(cbuf);
1471 
1472  ftpState->state = Ftp::Client::SENT_CWD;
1473 }
1474 
1475 static void
1477 {
1478  int code = ftpState->ctrl.replycode;
1479  debugs(9, 3, MYNAME);
1480 
1481  if (code >= 200 && code < 300) {
1482  /* CWD OK */
1483  ftpState->unhack();
1484 
1485  /* Reset cwd_message to only include the last message */
1486  ftpState->cwd_message.reset("");
1487  for (wordlist *w = ftpState->ctrl.message; w; w = w->next) {
1488  ftpState->cwd_message.append('\n');
1489  ftpState->cwd_message.append(w->key);
1490  }
1491  ftpState->ctrl.message = NULL;
1492 
1493  /* Continue to traverse the path */
1494  ftpTraverseDirectory(ftpState);
1495  } else {
1496  /* CWD FAILED */
1497 
1498  if (!ftpState->flags.put)
1499  ftpFail(ftpState);
1500  else
1501  ftpSendMkdir(ftpState);
1502  }
1503 }
1504 
1505 static void
1507 {
1508  char *path = NULL;
1509 
1510  /* check the server control channel is still available */
1511  if (!ftpState || !ftpState->haveControlChannel("ftpSendMkdir"))
1512  return;
1513 
1514  path = ftpState->filepath;
1515  debugs(9, 3, "with path=" << path);
1516  snprintf(cbuf, CTRL_BUFLEN, "MKD %s\r\n", path);
1517  ftpState->writeCommand(cbuf);
1518  ftpState->state = Ftp::Client::SENT_MKDIR;
1519 }
1520 
1521 static void
1523 {
1524  char *path = ftpState->filepath;
1525  int code = ftpState->ctrl.replycode;
1526 
1527  debugs(9, 3, "path " << path << ", code " << code);
1528 
1529  if (code == 257) { /* success */
1530  ftpSendCwd(ftpState);
1531  } else if (code == 550) { /* dir exists */
1532 
1533  if (ftpState->flags.put_mkdir) {
1534  ftpState->flags.put_mkdir = 1;
1535  ftpSendCwd(ftpState);
1536  } else
1537  ftpSendReply(ftpState);
1538  } else
1539  ftpSendReply(ftpState);
1540 }
1541 
1542 static void
1544 {
1545  assert(*ftpState->filepath != '\0');
1546  ftpState->flags.isdir = 0;
1547  ftpSendMdtm(ftpState);
1548 }
1549 
1550 static void
1552 {
1553  if (ftpState->flags.dir_slash) {
1554  debugs(9, 3, "Directory path did not end in /");
1555  ftpState->title_url.append("/");
1556  ftpState->flags.isdir = 1;
1557  }
1558 
1559  ftpSendPassive(ftpState);
1560 }
1561 
1562 static void
1564 {
1565  /* check the server control channel is still available */
1566  if (!ftpState || !ftpState->haveControlChannel("ftpSendMdtm"))
1567  return;
1568 
1569  assert(*ftpState->filepath != '\0');
1570  snprintf(cbuf, CTRL_BUFLEN, "MDTM %s\r\n", ftpState->filepath);
1571  ftpState->writeCommand(cbuf);
1572  ftpState->state = Ftp::Client::SENT_MDTM;
1573 }
1574 
1575 static void
1577 {
1578  int code = ftpState->ctrl.replycode;
1579  debugs(9, 3, MYNAME);
1580 
1581  if (code == 213) {
1582  ftpState->mdtm = Time::ParseIso3307(ftpState->ctrl.last_reply);
1583  ftpState->unhack();
1584  } else if (code < 0) {
1585  ftpFail(ftpState);
1586  return;
1587  }
1588 
1589  ftpSendSize(ftpState);
1590 }
1591 
1592 static void
1594 {
1595  /* check the server control channel is still available */
1596  if (!ftpState || !ftpState->haveControlChannel("ftpSendSize"))
1597  return;
1598 
1599  /* Only send SIZE for binary transfers. The returned size
1600  * is useless on ASCII transfers */
1601 
1602  if (ftpState->flags.binary) {
1603  assert(ftpState->filepath != NULL);
1604  assert(*ftpState->filepath != '\0');
1605  snprintf(cbuf, CTRL_BUFLEN, "SIZE %s\r\n", ftpState->filepath);
1606  ftpState->writeCommand(cbuf);
1607  ftpState->state = Ftp::Client::SENT_SIZE;
1608  } else
1609  /* Skip to next state no non-binary transfers */
1610  ftpSendPassive(ftpState);
1611 }
1612 
1613 static void
1615 {
1616  int code = ftpState->ctrl.replycode;
1617  debugs(9, 3, MYNAME);
1618 
1619  if (code == 213) {
1620  ftpState->unhack();
1621  ftpState->theSize = strtoll(ftpState->ctrl.last_reply, NULL, 10);
1622 
1623  if (ftpState->theSize == 0) {
1624  debugs(9, 2, "SIZE reported " <<
1625  ftpState->ctrl.last_reply << " on " <<
1626  ftpState->title_url);
1627  ftpState->theSize = -1;
1628  }
1629  } else if (code < 0) {
1630  ftpFail(ftpState);
1631  return;
1632  }
1633 
1634  ftpSendPassive(ftpState);
1635 }
1636 
1637 static void
1639 {
1640  Ip::Address srvAddr; // unused
1641  if (ftpState->handleEpsvReply(srvAddr)) {
1642  if (ftpState->ctrl.message == NULL)
1643  return; // didn't get complete reply yet
1644 
1645  ftpState->connectDataChannel();
1646  }
1647 }
1648 
1653 static void
1655 {
1657  if (!ftpState || !ftpState->haveControlChannel("ftpSendPassive"))
1658  return;
1659 
1660  debugs(9, 3, MYNAME);
1661 
1664  if (ftpState->request->method == Http::METHOD_HEAD && (ftpState->flags.isdir || ftpState->theSize != -1)) {
1665  ftpState->processHeadResponse(); // may call serverComplete
1666  return;
1667  }
1668 
1669  if (ftpState->sendPassive()) {
1670  // SENT_EPSV_ALL blocks other non-EPSV connections being attempted
1671  if (ftpState->state == Ftp::Client::SENT_EPSV_ALL)
1672  ftpState->flags.epsv_all_sent = true;
1673  }
1674 }
1675 
1676 void
1678 {
1679  debugs(9, 5, "handling HEAD response");
1680  ftpSendQuit(this);
1681  appendSuccessHeader();
1682 
1683  /*
1684  * On rare occasions I'm seeing the entry get aborted after
1685  * readControlReply() and before here, probably when
1686  * trying to write to the client.
1687  */
1688  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1689  abortAll("entry aborted while processing HEAD");
1690  return;
1691  }
1692 
1693 #if USE_ADAPTATION
1694  if (adaptationAccessCheckPending) {
1695  debugs(9,3, "returning due to adaptationAccessCheckPending");
1696  return;
1697  }
1698 #endif
1699 
1700  // processReplyBody calls serverComplete() since there is no body
1701  processReplyBody();
1702 }
1703 
1704 static void
1706 {
1707  Ip::Address srvAddr; // unused
1708  if (ftpState->handlePasvReply(srvAddr))
1709  ftpState->connectDataChannel();
1710  else {
1711  ftpFail(ftpState);
1712  // Currently disabled, does not work correctly:
1713  // ftpSendEPRT(ftpState);
1714  return;
1715  }
1716 }
1717 
1718 void
1720 {
1721  debugs(9, 3, MYNAME);
1722  dataConnWait.finish();
1723 
1724  if (io.flag != Comm::OK) {
1725  debugs(9, 2, "Failed to connect. Retrying via another method.");
1726 
1727  // ABORT on timeouts. server may be waiting on a broken TCP link.
1728  if (io.xerrno == Comm::TIMEOUT)
1729  writeCommand("ABOR\r\n");
1730 
1731  // try another connection attempt with some other method
1732  ftpSendPassive(this);
1733  return;
1734  }
1735 
1736  data.opened(io.conn, dataCloser());
1737  ftpRestOrList(this);
1738 }
1739 
1740 static void
1741 ftpOpenListenSocket(Ftp::Gateway * ftpState, int fallback)
1742 {
1744  if (ftpState->data.conn != NULL) {
1745  if ((ftpState->data.conn->flags & COMM_REUSEADDR))
1746  // NP: in fact it points to the control channel. just clear it.
1747  ftpState->data.clear();
1748  else
1749  ftpState->data.close();
1750  }
1751  safe_free(ftpState->data.host);
1752 
1753  if (!Comm::IsConnOpen(ftpState->ctrl.conn)) {
1754  debugs(9, 5, "The control connection to the remote end is closed");
1755  return;
1756  }
1757 
1758  /*
1759  * Set up a listen socket on the same local address as the
1760  * control connection.
1761  */
1763  temp->local = ftpState->ctrl.conn->local;
1764 
1765  /*
1766  * REUSEADDR is needed in fallback mode, since the same port is
1767  * used for both control and data.
1768  */
1769  if (fallback) {
1770  int on = 1;
1771  errno = 0;
1772  if (setsockopt(ftpState->ctrl.conn->fd, SOL_SOCKET, SO_REUSEADDR,
1773  (char *) &on, sizeof(on)) == -1) {
1774  int xerrno = errno;
1775  // SO_REUSEADDR is only an optimization, no need to be verbose about error
1776  debugs(9, 4, "setsockopt failed: " << xstrerr(xerrno));
1777  }
1778  ftpState->ctrl.conn->flags |= COMM_REUSEADDR;
1779  temp->flags |= COMM_REUSEADDR;
1780  } else {
1781  /* if not running in fallback mode a new port needs to be retrieved */
1782  temp->local.port(0);
1783  }
1784 
1785  ftpState->listenForDataChannel(temp);
1786 }
1787 
1788 static void
1790 {
1791  /* check the server control channel is still available */
1792  if (!ftpState || !ftpState->haveControlChannel("ftpSendPort"))
1793  return;
1794 
1795  if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent) {
1796  debugs(9, DBG_IMPORTANT, "FTP does not allow PORT method after 'EPSV ALL' has been sent.");
1797  return;
1798  }
1799 
1800  debugs(9, 3, MYNAME);
1801  ftpState->flags.pasv_supported = 0;
1802  ftpOpenListenSocket(ftpState, 0);
1803 
1804  if (!Comm::IsConnOpen(ftpState->data.listenConn)) {
1805  if ( ftpState->data.listenConn != NULL && !ftpState->data.listenConn->local.isIPv4() ) {
1806  /* non-IPv4 CANNOT send PORT command. */
1807  /* we got here by attempting and failing an EPRT */
1808  /* using the same reply code should simulate a PORT failure */
1809  ftpReadPORT(ftpState);
1810  return;
1811  }
1812 
1813  /* XXX Need to set error message */
1814  ftpFail(ftpState);
1815  return;
1816  }
1817 
1818  // pull out the internal IP address bytes to send in PORT command...
1819  // source them from the listen_conn->local
1820 
1821  struct addrinfo *AI = NULL;
1822  ftpState->data.listenConn->local.getAddrInfo(AI, AF_INET);
1823  unsigned char *addrptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_addr;
1824  unsigned char *portptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_port;
1825  snprintf(cbuf, CTRL_BUFLEN, "PORT %d,%d,%d,%d,%d,%d\r\n",
1826  addrptr[0], addrptr[1], addrptr[2], addrptr[3],
1827  portptr[0], portptr[1]);
1828  ftpState->writeCommand(cbuf);
1829  ftpState->state = Ftp::Client::SENT_PORT;
1830 
1832 }
1833 
1834 static void
1836 {
1837  int code = ftpState->ctrl.replycode;
1838  debugs(9, 3, MYNAME);
1839 
1840  if (code != 200) {
1841  /* Fall back on using the same port as the control connection */
1842  debugs(9, 3, "PORT not supported by remote end");
1843  ftpOpenListenSocket(ftpState, 1);
1844  }
1845 
1846  ftpRestOrList(ftpState);
1847 }
1848 
1849 static void
1851 {
1852  int code = ftpState->ctrl.replycode;
1853  debugs(9, 3, MYNAME);
1854 
1855  if (code != 200) {
1856  /* Failover to attempting old PORT command. */
1857  debugs(9, 3, "EPRT not supported by remote end");
1858  ftpSendPORT(ftpState);
1859  return;
1860  }
1861 
1862  ftpRestOrList(ftpState);
1863 }
1864 
1869 void
1871 {
1872  debugs(9, 3, MYNAME);
1873 
1874  if (!Comm::IsConnOpen(ctrl.conn)) { /*Close handlers will cleanup*/
1875  debugs(9, 5, "The control connection to the remote end is closed");
1876  return;
1877  }
1878 
1879  if (io.flag != Comm::OK) {
1880  data.listenConn->close();
1881  data.listenConn = NULL;
1882  debugs(9, DBG_IMPORTANT, "FTP AcceptDataConnection: " << io.conn << ": " << xstrerr(io.xerrno));
1883  // TODO: need to send error message on control channel
1884  ftpFail(this);
1885  return;
1886  }
1887 
1888  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1889  abortAll("entry aborted when accepting data conn");
1890  data.listenConn->close();
1891  data.listenConn = NULL;
1892  io.conn->close();
1893  return;
1894  }
1895 
1896  /* data listening conn is no longer even open. abort. */
1897  if (!Comm::IsConnOpen(data.listenConn)) {
1898  data.listenConn = NULL; // ensure that it's cleared and not just closed.
1899  return;
1900  }
1901 
1902  /* data listening conn is no longer even open. abort. */
1903  if (!Comm::IsConnOpen(data.conn)) {
1904  data.clear(); // ensure that it's cleared and not just closed.
1905  return;
1906  }
1907 
1914  if (Config.Ftp.sanitycheck) {
1915  // accept if either our data or ctrl connection is talking to this remote peer.
1916  if (data.conn->remote != io.conn->remote && ctrl.conn->remote != io.conn->remote) {
1917  debugs(9, DBG_IMPORTANT,
1918  "ERROR: FTP data connection from unexpected server (" <<
1919  io.conn->remote << "), expecting " <<
1920  data.conn->remote << " or " << ctrl.conn->remote);
1921 
1922  /* close the bad sources connection down ASAP. */
1923  io.conn->close();
1924 
1925  /* drop the bad connection (io) by ignoring the attempt. */
1926  return;
1927  }
1928  }
1929 
1931  data.close();
1932  data.opened(io.conn, dataCloser());
1933  data.addr(io.conn->remote);
1934 
1935  debugs(9, 3, "Connected data socket on " <<
1936  io.conn << ". FD table says: " <<
1937  "ctrl-peer= " << fd_table[ctrl.conn->fd].ipaddr << ", " <<
1938  "data-peer= " << fd_table[data.conn->fd].ipaddr);
1939 
1940  assert(haveControlChannel("ftpAcceptDataConnection"));
1941  assert(ctrl.message == NULL);
1942 
1943  // Ctrl channel operations will determine what happens to this data connection
1944 }
1945 
1946 static void
1948 {
1949  debugs(9, 3, MYNAME);
1950 
1951  if (ftpState->typecode == 'D') {
1952  ftpState->flags.isdir = 1;
1953 
1954  if (ftpState->flags.put) {
1955  ftpSendMkdir(ftpState); /* PUT name;type=d */
1956  } else {
1957  ftpSendNlst(ftpState); /* GET name;type=d sec 3.2.2 of RFC 1738 */
1958  }
1959  } else if (ftpState->flags.put) {
1960  ftpSendStor(ftpState);
1961  } else if (ftpState->flags.isdir)
1962  ftpSendList(ftpState);
1963  else if (ftpState->restartable())
1964  ftpSendRest(ftpState);
1965  else
1966  ftpSendRetr(ftpState);
1967 }
1968 
1969 static void
1971 {
1972  /* check the server control channel is still available */
1973  if (!ftpState || !ftpState->haveControlChannel("ftpSendStor"))
1974  return;
1975 
1976  debugs(9, 3, MYNAME);
1977 
1978  if (ftpState->filepath != NULL) {
1979  /* Plain file upload */
1980  snprintf(cbuf, CTRL_BUFLEN, "STOR %s\r\n", ftpState->filepath);
1981  ftpState->writeCommand(cbuf);
1982  ftpState->state = Ftp::Client::SENT_STOR;
1983  } else if (ftpState->request->header.getInt64(Http::HdrType::CONTENT_LENGTH) > 0) {
1984  /* File upload without a filename. use STOU to generate one */
1985  snprintf(cbuf, CTRL_BUFLEN, "STOU\r\n");
1986  ftpState->writeCommand(cbuf);
1987  ftpState->state = Ftp::Client::SENT_STOR;
1988  } else {
1989  /* No file to transfer. Only create directories if needed */
1990  ftpSendReply(ftpState);
1991  }
1992 }
1993 
1995 static void
1997 {
1998  ftpState->readStor();
1999 }
2000 
2002 {
2003  int code = ctrl.replycode;
2004  debugs(9, 3, MYNAME);
2005 
2006  if (code == 125 || (code == 150 && Comm::IsConnOpen(data.conn))) {
2007  if (!originalRequest()->body_pipe) {
2008  debugs(9, 3, "zero-size STOR?");
2009  state = WRITING_DATA; // make ftpWriteTransferDone() responsible
2010  dataComplete(); // XXX: keep in sync with doneSendingRequestBody()
2011  return;
2012  }
2013 
2014  if (!startRequestBodyFlow()) { // register to receive body data
2015  ftpFail(this);
2016  return;
2017  }
2018 
2019  /* When client status is 125, or 150 and the data connection is open, Begin data transfer. */
2020  debugs(9, 3, "starting data transfer");
2021  switchTimeoutToDataChannel();
2022  sendMoreRequestBody();
2023  fwd->dontRetry(true); // do not permit re-trying if the body was sent.
2024  state = WRITING_DATA;
2025  debugs(9, 3, "writing data channel");
2026  } else if (code == 150) {
2027  /* When client code is 150 with no data channel, Accept data channel. */
2028  debugs(9, 3, "ftpReadStor: accepting data channel");
2029  listenForDataChannel(data.conn);
2030  } else {
2031  debugs(9, DBG_IMPORTANT, "ERROR: Unexpected reply code "<< std::setfill('0') << std::setw(3) << code);
2032  ftpFail(this);
2033  }
2034 }
2035 
2036 static void
2038 {
2039  /* check the server control channel is still available */
2040  if (!ftpState || !ftpState->haveControlChannel("ftpSendRest"))
2041  return;
2042 
2043  debugs(9, 3, MYNAME);
2044 
2045  snprintf(cbuf, CTRL_BUFLEN, "REST %" PRId64 "\r\n", ftpState->restart_offset);
2046  ftpState->writeCommand(cbuf);
2047  ftpState->state = Ftp::Client::SENT_REST;
2048 }
2049 
2050 int
2052 {
2053  if (restart_offset > 0)
2054  return 1;
2055 
2056  if (!request->range)
2057  return 0;
2058 
2059  if (!flags.binary)
2060  return 0;
2061 
2062  if (theSize <= 0)
2063  return 0;
2064 
2065  int64_t desired_offset = request->range->lowestOffset(theSize);
2066 
2067  if (desired_offset <= 0)
2068  return 0;
2069 
2070  if (desired_offset >= theSize)
2071  return 0;
2072 
2073  restart_offset = desired_offset;
2074  return 1;
2075 }
2076 
2077 static void
2079 {
2080  int code = ftpState->ctrl.replycode;
2081  debugs(9, 3, MYNAME);
2082  assert(ftpState->restart_offset > 0);
2083 
2084  if (code == 350) {
2085  ftpState->setCurrentOffset(ftpState->restart_offset);
2086  ftpSendRetr(ftpState);
2087  } else if (code > 0) {
2088  debugs(9, 3, "REST not supported");
2089  ftpState->flags.rest_supported = 0;
2090  ftpSendRetr(ftpState);
2091  } else {
2092  ftpFail(ftpState);
2093  }
2094 }
2095 
2096 static void
2098 {
2099  /* check the server control channel is still available */
2100  if (!ftpState || !ftpState->haveControlChannel("ftpSendList"))
2101  return;
2102 
2103  debugs(9, 3, MYNAME);
2104 
2105  if (ftpState->filepath) {
2106  snprintf(cbuf, CTRL_BUFLEN, "LIST %s\r\n", ftpState->filepath);
2107  } else {
2108  snprintf(cbuf, CTRL_BUFLEN, "LIST\r\n");
2109  }
2110 
2111  ftpState->writeCommand(cbuf);
2112  ftpState->state = Ftp::Client::SENT_LIST;
2113 }
2114 
2115 static void
2117 {
2118  /* check the server control channel is still available */
2119  if (!ftpState || !ftpState->haveControlChannel("ftpSendNlst"))
2120  return;
2121 
2122  debugs(9, 3, MYNAME);
2123 
2124  ftpState->flags.tried_nlst = 1;
2125 
2126  if (ftpState->filepath) {
2127  snprintf(cbuf, CTRL_BUFLEN, "NLST %s\r\n", ftpState->filepath);
2128  } else {
2129  snprintf(cbuf, CTRL_BUFLEN, "NLST\r\n");
2130  }
2131 
2132  ftpState->writeCommand(cbuf);
2133  ftpState->state = Ftp::Client::SENT_NLST;
2134 }
2135 
2136 static void
2138 {
2139  int code = ftpState->ctrl.replycode;
2140  debugs(9, 3, MYNAME);
2141 
2142  if (code == 125 || (code == 150 && Comm::IsConnOpen(ftpState->data.conn))) {
2143  /* Begin data transfer */
2144  debugs(9, 3, "begin data transfer from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2145  ftpState->switchTimeoutToDataChannel();
2146  ftpState->maybeReadVirginBody();
2147  ftpState->state = Ftp::Client::READING_DATA;
2148  return;
2149  } else if (code == 150) {
2150  /* Accept data channel */
2151  debugs(9, 3, "accept data channel from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2152  ftpState->listenForDataChannel(ftpState->data.conn);
2153  return;
2154  } else if (!ftpState->flags.tried_nlst && code > 300) {
2155  ftpSendNlst(ftpState);
2156  } else {
2157  ftpFail(ftpState);
2158  return;
2159  }
2160 }
2161 
2162 static void
2164 {
2165  /* check the server control channel is still available */
2166  if (!ftpState || !ftpState->haveControlChannel("ftpSendRetr"))
2167  return;
2168 
2169  debugs(9, 3, MYNAME);
2170 
2171  assert(ftpState->filepath != NULL);
2172  snprintf(cbuf, CTRL_BUFLEN, "RETR %s\r\n", ftpState->filepath);
2173  ftpState->writeCommand(cbuf);
2174  ftpState->state = Ftp::Client::SENT_RETR;
2175 }
2176 
2177 static void
2179 {
2180  int code = ftpState->ctrl.replycode;
2181  debugs(9, 3, MYNAME);
2182 
2183  if (code == 125 || (code == 150 && Comm::IsConnOpen(ftpState->data.conn))) {
2184  /* Begin data transfer */
2185  debugs(9, 3, "begin data transfer from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2186  ftpState->switchTimeoutToDataChannel();
2187  ftpState->maybeReadVirginBody();
2188  ftpState->state = Ftp::Client::READING_DATA;
2189  } else if (code == 150) {
2190  /* Accept data channel */
2191  ftpState->listenForDataChannel(ftpState->data.conn);
2192  } else if (code >= 300) {
2193  if (!ftpState->flags.try_slash_hack) {
2194  /* Try this as a directory missing trailing slash... */
2195  ftpState->hackShortcut(ftpSendCwd);
2196  } else {
2197  ftpFail(ftpState);
2198  }
2199  } else {
2200  ftpFail(ftpState);
2201  }
2202 }
2203 
2208 void
2210 {
2211  assert(entry);
2212  entry->lock("Ftp::Gateway");
2213  ErrorState ferr(ERR_DIR_LISTING, Http::scOkay, request.getRaw(), fwd->al);
2214  ferr.ftp.listing = &listing;
2215  ferr.ftp.cwd_msg = xstrdup(cwd_message.size()? cwd_message.termedBuf() : "");
2216  ferr.ftp.server_msg = ctrl.message;
2217  ctrl.message = NULL;
2218  entry->replaceHttpReply(ferr.BuildHttpReply());
2219  entry->flush();
2220  entry->unlock("Ftp::Gateway");
2221 }
2222 
2223 static void
2225 {
2226  int code = ftpState->ctrl.replycode;
2227  debugs(9, 3, MYNAME);
2228 
2229  if (code == 226 || code == 250) {
2230  /* Connection closed; retrieval done. */
2231  if (ftpState->flags.listing) {
2232  ftpState->completedListing();
2233  /* QUIT operation handles sending the reply to client */
2234  }
2235  ftpState->markParsedVirginReplyAsWhole("ftpReadTransferDone code 226 or 250");
2236  ftpSendQuit(ftpState);
2237  } else { /* != 226 */
2238  debugs(9, DBG_IMPORTANT, "Got code " << code << " after reading data");
2239  ftpState->failed(ERR_FTP_FAILURE, 0);
2240  /* failed closes ctrl.conn and frees ftpState */
2241  return;
2242  }
2243 }
2244 
2245 // premature end of the request body
2246 void
2248 {
2250  debugs(9, 3, "ftpState=" << this);
2251  failed(ERR_READ_ERROR, 0);
2252 }
2253 
2254 static void
2256 {
2257  int code = ftpState->ctrl.replycode;
2258  debugs(9, 3, MYNAME);
2259 
2260  if (!(code == 226 || code == 250)) {
2261  debugs(9, DBG_IMPORTANT, "Got code " << code << " after sending data");
2262  ftpState->failed(ERR_FTP_PUT_ERROR, 0);
2263  return;
2264  }
2265 
2266  ftpState->entry->timestampsSet(); /* XXX Is this needed? */
2267  ftpSendReply(ftpState);
2268 }
2269 
2270 static void
2272 {
2273  /* check the server control channel is still available */
2274  if (!ftpState || !ftpState->haveControlChannel("ftpSendQuit"))
2275  return;
2276 
2277  snprintf(cbuf, CTRL_BUFLEN, "QUIT\r\n");
2278  ftpState->writeCommand(cbuf);
2279  ftpState->state = Ftp::Client::SENT_QUIT;
2280 }
2281 
2285 static void
2287 {
2288  ftpState->serverComplete();
2289 }
2290 
2291 static void
2293 {
2294  char *path;
2295  ftpState->flags.try_slash_hack = 1;
2296  /* Free old paths */
2297 
2298  debugs(9, 3, MYNAME);
2299 
2300  if (ftpState->pathcomps)
2301  wordlistDestroy(&ftpState->pathcomps);
2302 
2303  safe_free(ftpState->filepath);
2304 
2305  /* Build the new path (urlpath begins with /) */
2306  path = SBufToCstring(ftpState->request->url.path());
2307 
2308  rfc1738_unescape(path);
2309 
2310  ftpState->filepath = path;
2311 
2312  /* And off we go */
2313  ftpGetFile(ftpState);
2314 }
2315 
2319 void
2321 {
2322  debugs(9, 3, MYNAME);
2323 
2324  if (old_request != NULL) {
2325  safe_free(old_request);
2326  safe_free(old_reply);
2327  }
2328 }
2329 
2330 void
2332 {
2333  /* Clear some unwanted state */
2334  setCurrentOffset(0);
2335  restart_offset = 0;
2336  /* Save old error message & some state info */
2337 
2338  debugs(9, 3, MYNAME);
2339 
2340  if (old_request == NULL) {
2341  old_request = ctrl.last_command;
2342  ctrl.last_command = NULL;
2343  old_reply = ctrl.last_reply;
2344  ctrl.last_reply = NULL;
2345 
2346  if (pathcomps == NULL && filepath != NULL)
2347  old_filepath = xstrdup(filepath);
2348  }
2349 
2350  /* Jump to the "hack" state */
2351  nextState(this);
2352 }
2353 
2354 static void
2356 {
2357  const bool slashHack = ftpState->request->url.path().caseCmp("/%2f", 4)==0;
2358  int code = ftpState->ctrl.replycode;
2359  err_type error_code = ERR_NONE;
2360 
2361  debugs(9, 6, "state " << ftpState->state <<
2362  " reply code " << code << "flags(" <<
2363  (ftpState->flags.isdir?"IS_DIR,":"") <<
2364  (ftpState->flags.try_slash_hack?"TRY_SLASH_HACK":"") << "), " <<
2365  "mdtm=" << ftpState->mdtm << ", size=" << ftpState->theSize <<
2366  "slashhack=" << (slashHack? "T":"F"));
2367 
2368  /* Try the / hack to support "Netscape" FTP URL's for retrieving files */
2369  if (!ftpState->flags.isdir && /* Not a directory */
2370  !ftpState->flags.try_slash_hack && !slashHack && /* Not doing slash hack */
2371  ftpState->mdtm <= 0 && ftpState->theSize < 0) { /* Not known as a file */
2372 
2373  switch (ftpState->state) {
2374 
2375  case Ftp::Client::SENT_CWD:
2376 
2378  /* Try the / hack */
2379  ftpState->hackShortcut(ftpTrySlashHack);
2380  return;
2381 
2382  default:
2383  break;
2384  }
2385  }
2386 
2387  Http::StatusCode sc = ftpState->failedHttpStatus(error_code);
2388  const auto ftperr = new ErrorState(error_code, sc, ftpState->fwd->request, ftpState->fwd->al);
2389  ftpState->failed(error_code, 0, ftperr);
2390  ftperr->detailError(new Ftp::ErrorDetail(code));
2391  HttpReply *newrep = ftperr->BuildHttpReply();
2392  delete ftperr;
2393 
2394  ftpState->entry->replaceHttpReply(newrep);
2395  ftpSendQuit(ftpState);
2396 }
2397 
2400 {
2401  if (error == ERR_NONE) {
2402  switch (state) {
2403 
2404  case SENT_USER:
2405 
2406  case SENT_PASS:
2407 
2408  if (ctrl.replycode > 500) {
2410  return password_url ? Http::scForbidden : Http::scUnauthorized;
2411  } else if (ctrl.replycode == 421) {
2414  }
2415  break;
2416 
2417  case SENT_CWD:
2418 
2419  case SENT_RETR:
2420  if (ctrl.replycode == 550) {
2422  return Http::scNotFound;
2423  }
2424  break;
2425 
2426  default:
2427  break;
2428  }
2429  }
2431 }
2432 
2433 static void
2435 {
2436  int code = ftpState->ctrl.replycode;
2437  Http::StatusCode http_code;
2438  err_type err_code = ERR_NONE;
2439 
2440  debugs(9, 3, ftpState->entry->url() << ", code " << code);
2441 
2442  if (cbdataReferenceValid(ftpState))
2443  debugs(9, 5, "ftpState (" << ftpState << ") is valid!");
2444 
2445  if (code == 226 || code == 250) {
2446  err_code = (ftpState->mdtm > 0) ? ERR_FTP_PUT_MODIFIED : ERR_FTP_PUT_CREATED;
2447  http_code = (ftpState->mdtm > 0) ? Http::scAccepted : Http::scCreated;
2448  } else if (code == 227) {
2449  err_code = ERR_FTP_PUT_CREATED;
2450  http_code = Http::scCreated;
2451  } else {
2452  err_code = ERR_FTP_PUT_ERROR;
2453  http_code = Http::scInternalServerError;
2454  }
2455 
2456  ErrorState err(err_code, http_code, ftpState->request.getRaw(), ftpState->fwd->al);
2457 
2458  if (ftpState->old_request)
2459  err.ftp.request = xstrdup(ftpState->old_request);
2460  else
2461  err.ftp.request = xstrdup(ftpState->ctrl.last_command);
2462 
2463  if (ftpState->old_reply)
2464  err.ftp.reply = xstrdup(ftpState->old_reply);
2465  else if (ftpState->ctrl.last_reply)
2466  err.ftp.reply = xstrdup(ftpState->ctrl.last_reply);
2467  else
2468  err.ftp.reply = xstrdup("");
2469 
2470  err.detailError(new Ftp::ErrorDetail(code));
2471 
2472  ftpState->entry->replaceHttpReply(err.BuildHttpReply());
2473 
2474  ftpSendQuit(ftpState);
2475 }
2476 
2477 void
2479 {
2480  debugs(9, 3, MYNAME);
2481 
2482  if (flags.http_header_sent)
2483  return;
2484 
2485  HttpReply *reply = new HttpReply;
2486 
2487  flags.http_header_sent = 1;
2488 
2489  assert(entry->isEmpty());
2490 
2491  entry->buffer(); /* released when done processing current data payload */
2492 
2493  SBuf urlPath = request->url.path();
2494  auto t = urlPath.rfind('/');
2495  SBuf filename = urlPath.substr(t != SBuf::npos ? t : 0);
2496 
2497  const char *mime_type = NULL;
2498  const char *mime_enc = NULL;
2499 
2500  if (flags.isdir) {
2501  mime_type = "text/html";
2502  } else {
2503  switch (typecode) {
2504 
2505  case 'I':
2506  mime_type = "application/octet-stream";
2507  // XXX: performance regression, c_str() may reallocate
2508  mime_enc = mimeGetContentEncoding(filename.c_str());
2509  break;
2510 
2511  case 'A':
2512  mime_type = "text/plain";
2513  break;
2514 
2515  default:
2516  // XXX: performance regression, c_str() may reallocate
2517  mime_type = mimeGetContentType(filename.c_str());
2518  mime_enc = mimeGetContentEncoding(filename.c_str());
2519  break;
2520  }
2521  }
2522 
2523  /* set standard stuff */
2524 
2525  if (0 == getCurrentOffset()) {
2526  /* Full reply */
2527  reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, theSize, mdtm, -2);
2528  } else if (theSize < getCurrentOffset()) {
2529  /*
2530  * DPW 2007-05-04
2531  * offset should not be larger than theSize. We should
2532  * not be seeing this condition any more because we'll only
2533  * send REST if we know the theSize and if it is less than theSize.
2534  */
2535  debugs(0, DBG_CRITICAL, "ERROR: " <<
2536  " current offset=" << getCurrentOffset() <<
2537  ", but theSize=" << theSize <<
2538  ". assuming full content response");
2539  reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, theSize, mdtm, -2);
2540  } else {
2541  /* Partial reply */
2542  HttpHdrRangeSpec range_spec;
2543  range_spec.offset = getCurrentOffset();
2544  range_spec.length = theSize - getCurrentOffset();
2545  reply->setHeaders(Http::scPartialContent, "Gatewaying", mime_type, theSize - getCurrentOffset(), mdtm, -2);
2546  httpHeaderAddContRange(&reply->header, range_spec, theSize);
2547  }
2548 
2549  /* additional info */
2550  if (mime_enc)
2551  reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc);
2552 
2553  reply->sources |= Http::Message::srcFtp;
2554  setVirginReply(reply);
2555  adaptOrFinalizeReply();
2556 }
2557 
2558 void
2560 {
2562 
2563  StoreEntry *e = entry;
2564 
2565  e->timestampsSet();
2566 
2567  // makePublic() if allowed/possible or release() otherwise
2568  if (flags.authenticated || // authenticated requests can't be cached
2569  getCurrentOffset() ||
2570  !e->makePublic()) {
2571  e->release();
2572  }
2573 }
2574 
2575 HttpReply *
2577 {
2579  HttpReply *newrep = err.BuildHttpReply();
2580 #if HAVE_AUTH_MODULE_BASIC
2581  /* add Authenticate header */
2582  // XXX: performance regression. c_str() may reallocate
2583  newrep->header.putAuth("Basic", realm.c_str());
2584 #else
2585  (void)realm;
2586 #endif
2587  return newrep;
2588 }
2589 
2590 const SBuf &
2592 {
2593  SBuf newbuf("%2f");
2594 
2595  if (request->url.getScheme() != AnyP::PROTO_FTP) {
2596  static const SBuf nil;
2597  return nil;
2598  }
2599 
2600  if (request->url.path()[0] == '/') {
2601  newbuf.append(request->url.path());
2602  request->url.path(newbuf);
2603  } else if (!request->url.path().startsWith(newbuf)) {
2604  newbuf.append(request->url.path().substr(1));
2605  request->url.path(newbuf);
2606  }
2607 
2608  return request->effectiveRequestUri();
2609 }
2610 
2611 void
2612 Ftp::Gateway::printfReplyBody(const char *fmt, ...)
2613 {
2614  va_list args;
2615  va_start (args, fmt);
2616  static char buf[4096];
2617  buf[0] = '\0';
2618  vsnprintf(buf, 4096, fmt, args);
2619  writeReplyBody(buf, strlen(buf));
2620  va_end(args);
2621 }
2622 
2627 void
2628 Ftp::Gateway::writeReplyBody(const char *dataToWrite, size_t dataLength)
2629 {
2630  debugs(9, 5, "writing " << dataLength << " bytes to the reply");
2631  addVirginReplyBody(dataToWrite, dataLength);
2632 }
2633 
2640 void
2642 {
2643  if (fwd == NULL || flags.completed_forwarding) {
2644  debugs(9, 3, "avoid double-complete on FD " <<
2645  (ctrl.conn ? ctrl.conn->fd : -1) << ", Data FD " << data.conn->fd <<
2646  ", this " << this << ", fwd " << fwd);
2647  return;
2648  }
2649 
2650  flags.completed_forwarding = true;
2652 }
2653 
2660 bool
2661 Ftp::Gateway::haveControlChannel(const char *caller_name) const
2662 {
2663  if (doneWithServer())
2664  return false;
2665 
2666  /* doneWithServer() only checks BOTH channels are closed. */
2667  if (!Comm::IsConnOpen(ctrl.conn)) {
2668  debugs(9, DBG_IMPORTANT, "WARNING: FTP Server Control channel is closed, but Data channel still active.");
2669  debugs(9, 2, caller_name << ": attempted on a closed FTP channel.");
2670  return false;
2671  }
2672 
2673  return true;
2674 }
2675 
2676 bool
2678 {
2679  // TODO: Can we do what Ftp::Relay::mayReadVirginReplyBody() does instead?
2680  return !doneWithServer();
2681 }
2682 
2683 void
2684 Ftp::StartGateway(FwdState *const fwdState)
2685 {
2686  AsyncJob::Start(new Ftp::Gateway(fwdState));
2687 }
2688 
virtual bool mayReadVirginReplyBody() const
whether we may receive more virgin response body bytes
Definition: FtpGateway.cc:2677
static void ftpTrySlashHack(Ftp::Gateway *ftpState)
Definition: FtpGateway.cc:2292
void fatal(const char *message)
Definition: fatal.cc:28
const char * xstrerr(int error)
Definition: xstrerror.cc:83
char method[16]
Definition: tcp-banger2.c:115
static FTPSM ftpSendCwd
Definition: FtpGateway.cc:219
static FTPSM ftpReadQuit
Definition: FtpGateway.cc:238
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
StoreEntry * entry
Definition: Client.h:176
size_type find(char c, size_type startPos=0) const
Definition: SBuf.cc:584
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
Gateway(FwdState *)
Definition: FtpGateway.cc:326
@ scAccepted
Definition: StatusCode.h:28
char * cwd_msg
Definition: errorpage.h:191
@ scUnauthorized
Definition: StatusCode.h:45
@ METHOD_HEAD
Definition: MethodType.h:28
#define DBG_CRITICAL
Definition: Stream.h:40
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
@ ERR_READ_ERROR
Definition: forward.h:28
#define xmalloc
@ ERR_FTP_FORBIDDEN
Definition: forward.h:56
static PF ftpDataWrite
Definition: FtpGateway.cc:151
MemBuf * listing
Definition: errorpage.h:192
SBuf ftpRealm()
Definition: FtpGateway.cc:1257
bool makePublic(const KeyScope keyScope=ksDefault)
Definition: store.cc:164
HttpHeader header
Definition: Message.h:75
char * old_filepath
Definition: FtpGateway.cc:117
static FTPSM ftpReadTransferDone
Definition: FtpGateway.cc:229
bool isEmpty() const
Definition: SBuf.h:431
@ ERR_FTP_PUT_ERROR
Definition: forward.h:54
char * reply
Definition: errorpage.h:190
const char * url() const
Definition: store.cc:1544
void connectDataChannel()
Definition: FtpClient.cc:762
const char *const crlf
Definition: FtpClient.cc:40
void writeCommand(const char *buf)
Definition: FtpClient.cc:823
bool pasv_supported
PASV command is allowed.
Definition: FtpGateway.cc:56
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
Definition: FtpGateway.cc:478
static void ftpOpenListenSocket(Ftp::Gateway *ftpState, int fallback)
Definition: FtpGateway.cc:1741
HttpReply * BuildHttpReply(void)
Definition: errorpage.cc:1279
virtual void handleRequestBodyProducerAborted()=0
Definition: Client.cc:351
void error(char *format,...)
AccessLogEntryPointer al
info for the future access.log entry
Definition: FwdState.h:171
static FTPSM ftpSendRest
Definition: FtpGateway.cc:225
@ ERR_CACHE_ACCESS_DENIED
Definition: forward.h:19
bool tried_auth_nopass
auth tried username with no password already.
Definition: FtpGateway.cc:64
struct ErrorState::@59 ftp
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
Definition: SBuf.h:94
static HttpReply * ftpAuthRequired(HttpRequest *request, SBuf &realm, AccessLogEntry::Pointer &)
Definition: FtpGateway.cc:2576
bool sendPassive()
Definition: FtpClient.cc:653
bool htmlifyListEntry(const char *line, PackableStream &)
Definition: FtpGateway.cc:756
static FTPSM ftpReadSize
Definition: FtpGateway.cc:209
void SBufToCstring(char *d, const SBuf &s)
Definition: SBuf.h:752
#define xtoupper(x)
Definition: xis.h:18
#define xstrdup
char * html_quote(const char *)
Definition: html_quote.c:53
@ TIMEOUT
Definition: Flag.h:19
static FTPSM ftpSendRetr
Definition: FtpGateway.cc:227
@ CONTENT_ENCODING
int checkAuth(const HttpHeader *req_hdr)
Definition: FtpGateway.cc:1023
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:686
C * getRaw() const
Definition: RefCount.h:80
void initReadBuf()
Definition: FtpClient.cc:221
void loginParser(const SBuf &login, bool escaped)
Definition: FtpGateway.cc:395
#define REG_ICASE
Definition: GnuRegex.h:230
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
void detailError(const ErrorDetail::Pointer &dCode)
set error type-specific detail code
Definition: errorpage.h:109
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:398
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
@ OK
Definition: Flag.h:16
static FTPSM ftpSendUser
Definition: FtpGateway.cc:200
#define rfc1738_escape(x)
Definition: rfc1738.h:52
virtual void failed(err_type error=ERR_NONE, int xerrno=0, ErrorState *ftperr=nullptr)
handle a fatal transaction error, closing the control connection
Definition: FtpClient.cc:262
static FTPSM ftpReadRest
Definition: FtpGateway.cc:226
static FTPSM ftpReadList
Definition: FtpGateway.cc:224
@ ERR_NONE
Definition: forward.h:15
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1683
void * memAllocate(mem_type)
Allocate one element from the typed pool.
Definition: old_api.cc:213
static FTPSM ftpSendNlst
Definition: FtpGateway.cc:223
bool isIPv4() const
Definition: Address.cc:158
StatusCode
Definition: StatusCode.h:20
static FTPSM ftpSendMkdir
Definition: FtpGateway.cc:234
err_type
Definition: forward.h:14
static FTPSM ftpRestOrList
Definition: FtpGateway.cc:221
virtual void haveParsedReplyHeaders()
called when we have final (possibly adapted) reply headers; kids extend
Definition: Client.cc:537
int64_t currentOffset
Definition: Client.h:172
void processReplyBody()
Definition: FtpGateway.cc:956
static FTPSM ftpSendType
Definition: FtpGateway.cc:204
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
Definition: FtpGateway.cc:314
char * wordlistChopHead(wordlist **wl)
Definition: wordlist.cc:55
#define w_space
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
int64_t theSize
Definition: FtpGateway.cc:109
SBuf substr(size_type pos, size_type n=npos) const
Definition: SBuf.cc:576
struct SquidConfig::@112 Ftp
virtual void dataChannelConnected(const CommConnectCbParams &io)
Definition: FtpGateway.cc:1719
void serverComplete()
Definition: Client.cc:180
FTP client functionality shared among FTP Gateway and Relay clients.
Definition: FtpClient.h:111
@ CONTENT_LENGTH
Definition: forward.h:28
virtual void maybeReadVirginBody()
read response data from the network
Definition: FtpClient.cc:915
virtual void completeForwarding()
Definition: Client.cc:230
CBDATA_CLASS(Gateway)
void() StateMethod(Ftp::Gateway *)
Definition: FtpGateway.cc:86
CBDATA_NAMESPACED_CLASS_INIT(Ftp, Gateway)
static FTPSM ftpListDir
Definition: FtpGateway.cc:217
@ ENTRY_ABORTED
Definition: enums.h:115
char * showname
Definition: FtpGateway.cc:186
struct sockaddr * ai_addr
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note)
Definition: comm.cc:233
virtual bool haveControlChannel(const char *caller_name) const
Definition: FtpGateway.cc:2661
size_type rfind(char c, size_type endPos=npos) const
Definition: SBuf.cc:692
#define MAX_URL
Definition: defines.h:78
bool handlePasvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:455
virtual void start()
called by AsyncStart; do not call directly
Definition: FtpClient.cc:215
static FTPSM ftpSendQuit
Definition: FtpGateway.cc:237
struct tok tokens[]
Definition: parse.c:168
static FTPSM ftpReadCwd
Definition: FtpGateway.cc:220
static FTPSM ftpReadUser
Definition: FtpGateway.cc:201
char * proxy_host
Definition: FtpGateway.cc:114
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
wordlist * server_msg
Definition: errorpage.h:188
int size
Definition: ModDevPoll.cc:75
wordlist * message
Definition: FtpClient.h:82
static FTPSM ftpReadMkdir
Definition: FtpGateway.cc:235
#define NULL
Definition: types.h:166
#define REG_EXTENDED
Definition: GnuRegex.h:226
static char cbuf[CTRL_BUFLEN]
Definition: FtpGateway.cc:191
const char * rawContent() const
Definition: SBuf.cc:509
bool completed_forwarding
Definition: FtpGateway.cc:82
String base_href
Definition: FtpGateway.cc:105
static FTPSM ftpSendSize
Definition: FtpGateway.cc:208
@ ERR_FTP_PUT_MODIFIED
Definition: forward.h:58
void rfc1738_unescape(char *url)
Definition: rfc1738.c:146
@ scForbidden
Definition: StatusCode.h:47
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
virtual void handleControlReply()
Definition: FtpClient.cc:419
char * last_command
Definition: FtpClient.h:83
static FTPSM ftpSendPass
Definition: FtpGateway.cc:202
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
Definition: FtpClient.cc:810
void buildTitleUrl()
Definition: FtpGateway.cc:1107
MemBlob::size_type size_type
Definition: SBuf.h:96
#define COMM_REUSEADDR
Definition: Connection.h:48
void append(char const *buf, int len)
Definition: String.cc:149
static FTPSM ftpReadWelcome
Definition: FtpGateway.cc:199
String clean_url
Definition: FtpGateway.cc:103
const char * text
Definition: errorpage.cc:153
int64_t size
Definition: FtpGateway.cc:183
HttpRequestPointer request
Definition: errorpage.h:175
void putAuth(const char *auth_scheme, const char *realm)
Definition: HttpHeader.cc:1061
void appendSuccessHeader()
Definition: FtpGateway.cc:2478
Definition: MemBuf.h:24
Ip::Address local
Definition: Connection.h:144
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
bool handleEpsvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:491
unsigned char code
Definition: html_quote.c:20
String cwd_message
Definition: FtpGateway.cc:116
@ scPartialContent
Definition: StatusCode.h:32
size_t list_width
Definition: FtpGateway.cc:115
static FTPSM ftpReadEPRT
Definition: FtpGateway.cc:210
FTPSM * FTP_SM_FUNCS[]
Definition: FtpGateway.cc:282
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
@ scCreated
Definition: StatusCode.h:27
int64_t getInt64(Http::HdrType id) const
Definition: HttpHeader.cc:1162
#define safe_free(x)
Definition: xalloc.h:73
@ srcFtp
ftp_port or FTP server
Definition: Message.h:40
Ip::Address remote
Definition: Connection.h:147
void close()
planned close: removes the close handler and calls comm_close
Definition: FtpClient.cc:107
void ftpAcceptDataConnection(const CommAcceptCbParams &io)
Definition: FtpGateway.cc:1870
SBuf getAuthToken(Http::HdrType id, const char *auth_scheme) const
Definition: HttpHeader.cc:1304
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:19
bool tried_auth_anonymous
auth has tried to use anonymous credentials already.
Definition: FtpGateway.cc:63
bool authenticated
authentication success
Definition: FtpGateway.cc:62
SSL Connection
Definition: Session.h:45
FwdState::Pointer fwd
Definition: Client.h:177
static FTPSM ftpSendList
Definition: FtpGateway.cc:222
@ METHOD_PUT
Definition: MethodType.h:27
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
void parseListing()
Definition: FtpGateway.cc:872
static FTPSM ftpTraverseDirectory
Definition: FtpGateway.cc:216
virtual void completeForwarding()
Definition: FtpGateway.cc:2641
String title_url
Definition: FtpGateway.cc:104
@ scServiceUnavailable
Definition: StatusCode.h:74
static FTPSM ftpReadEPSV
Definition: FtpGateway.cc:214
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:69
char * filepath
Definition: FtpGateway.cc:111
@ scInternalServerError
Definition: StatusCode.h:71
Comm::ConnectionPointer conn
channel descriptor
Definition: FtpClient.h:58
static int sc[16]
Definition: smbdes.c:121
wordlist * pathcomps
Definition: FtpGateway.cc:110
const char * null_string
const char * c_str()
Definition: SBuf.cc:516
int64_t strtoll(const char *nptr, char **endptr, int base)
Definition: strtoll.c:61
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
char * xstrndup(const char *s, size_t n)
Definition: xstring.cc:56
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
int reply_hdr_state
Definition: FtpGateway.cc:102
#define xfree
char * anon_user
Definition: SquidConfig.h:419
char * reply_hdr
Definition: FtpGateway.cc:101
DataChannel data
FTP data channel state.
Definition: FtpClient.h:143
static FTPSM ftpSendPassive
Definition: FtpGateway.cc:213
int regexec(regex_t *preg, const char *string, size_t nmatch, pmatch, int eflags) const
Definition: GnuRegex.c:4188
wordlist * next
Definition: wordlist.h:33
bool mimeGetViewOption(const char *fn)
Definition: mime.cc:219
static const size_type npos
Definition: SBuf.h:99
void markParsedVirginReplyAsWhole(const char *reasonWeAreSure)
Definition: Client.cc:158
virtual void handleControlReply()
Definition: FtpGateway.cc:1162
uint32_t sources
The message sources.
Definition: Message.h:100
void writeReplyBody(const char *, size_t len)
Definition: FtpGateway.cc:2628
static void ftpListPartsFree(ftpListParts **parts)
Definition: FtpGateway.cc:512
static const char * Month[]
Definition: FtpGateway.cc:494
void hackShortcut(StateMethod *nextState)
Definition: FtpGateway.cc:2331
#define fd_table
Definition: fde.h:189
static FTPSM ftpReadPasv
Definition: FtpGateway.cc:215
void clear()
remove the close handler, leave connection open
Definition: FtpClient.cc:128
virtual void handleRequestBodyProducerAborted()
Definition: FtpGateway.cc:2247
char user[MAX_URL]
Definition: FtpGateway.cc:98
@ MEM_4K_BUF
Definition: forward.h:42
void checkUrlpath()
Definition: FtpGateway.cc:1069
HttpRequestMethod method
Definition: HttpRequest.h:114
void path(const char *p)
Definition: Uri.h:99
static FTPSM ftpReadType
Definition: FtpGateway.cc:205
Comm::ConnectionPointer listenConn
Definition: FtpClient.h:65
@ scNotFound
Definition: StatusCode.h:48
const char * mimeGetContentType(const char *fn)
Definition: mime.cc:177
virtual Http::StatusCode failedHttpStatus(err_type &error)
Definition: FtpClient.cc:311
MemBuf listing
FTP directory listing in HTML format.
Definition: FtpGateway.cc:119
@ PROTO_FTP
Definition: ProtocolType.h:26
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
Definition: Address.cc:599
char * key
Definition: wordlist.h:32
virtual Http::StatusCode failedHttpStatus(err_type &error)
Definition: FtpGateway.cc:2399
@ MEM_8K_BUF
Definition: forward.h:43
char * last_reply
Definition: FtpClient.h:84
const SBuf & UrlWith2f(HttpRequest *)
Definition: FtpGateway.cc:2591
static ftpListParts * ftpListParseParts(const char *buf, struct Ftp::GatewayFlags flags)
Definition: FtpGateway.cc:524
int64_t restart_offset
Definition: FtpGateway.cc:113
static FTPSM ftpReadMdtm
Definition: FtpGateway.cc:207
void processHeadResponse()
Definition: FtpGateway.cc:1677
void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, int64_t)
bool timestampsSet()
Definition: store.cc:1371
GatewayFlags flags
Definition: FtpGateway.cc:121
char * content()
start of the added data
Definition: MemBuf.h:41
static int is_month(const char *buf)
Definition: FtpGateway.cc:500
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1052
static FTPSM ftpFail
Definition: FtpGateway.cc:236
Ftp::StateMethod FTPSM
Definition: FtpGateway.cc:177
char * url
Definition: tcp-banger2.c:114
static FTPSM ftpReadStor
Definition: FtpGateway.cc:231
void memFree(void *, int type)
Free a element allocated by memAllocate()
Definition: minimal.cc:60
void printfReplyBody(const char *fmt,...)
Definition: FtpGateway.cc:2612
void readStor()
Definition: FtpGateway.cc:2001
char * old_request
Definition: FtpClient.h:177
void loginFailed(void)
Definition: FtpGateway.cc:1213
void StartGateway(FwdState *const fwdState)
A new FTP Gateway job.
Definition: FtpGateway.cc:2684
static FTPSM ftpSendMdtm
Definition: FtpGateway.cc:206
const char * mimeGetContentEncoding(const char *fn)
Definition: mime.cc:191
void reset(char const *str)
Definition: String.cc:141
#define DBG_IMPORTANT
Definition: Stream.h:41
void listenForDataChannel(const Comm::ConnectionPointer &conn)
create a data channel acceptor and start listening.
Definition: FtpGateway.cc:441
char password[MAX_URL]
Definition: FtpGateway.cc:99
int restartable()
Definition: FtpGateway.cc:2051
void Controller::create() STUB void Controller Controller nil
#define MYNAME
Definition: Stream.h:238
void completedListing(void)
Definition: FtpGateway.cc:2209
void release(const bool shareable=false)
Definition: store.cc:1122
#define PRId64
Definition: types.h:110
time_t ParseIso3307(const char *)
Convert from ISO 3307 style time: YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx.
Definition: iso3307.cc:22
char * rfc1738_do_escape(const char *url, int flags)
Definition: rfc1738.c:56
int64_t getCurrentOffset() const
Definition: FtpGateway.cc:148
SBuf & appendf(const char *fmt,...)
Definition: SBuf.cc:229
CtrlChannel ctrl
FTP control channel state.
Definition: FtpClient.h:142
static FTPSM ftpReadPORT
Definition: FtpGateway.cc:212
virtual void haveParsedReplyHeaders()
called when we have final (possibly adapted) reply headers; kids extend
Definition: FtpGateway.cc:2559
int token
Definition: parse.c:163
@ ERR_FTP_FAILURE
Definition: forward.h:53
char * dirpath
Definition: FtpGateway.cc:112
const char * mimeGetIconURL(const char *fn)
Definition: mime.cc:158
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:167
#define xisspace(x)
Definition: xis.h:17
static FTPSM ftpSendPORT
Definition: FtpGateway.cc:211
#define CTRL_BUFLEN
Definition: FtpGateway.cc:190
@ scOkay
Definition: StatusCode.h:26
virtual ~Gateway()
Definition: FtpGateway.cc:362
bool mimeGetDownloadOption(const char *fn)
Definition: mime.cc:212
virtual void start()
called by AsyncStart; do not call directly
Definition: FtpGateway.cc:1140
#define rfc1738_escape_part(x)
Definition: rfc1738.h:55
const char * wordlistAdd(wordlist **list, const char *key)
Definition: wordlist.cc:25
static FTPSM ftpSendStor
Definition: FtpGateway.cc:230
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
Definition: FtpClient.cc:890
HttpRequest * request
Definition: FwdState.h:170
void host(const char *src)
Definition: Uri.cc:99
@ ERR_FTP_UNAVAILABLE
Definition: forward.h:52
#define MAX_TOKENS
Definition: FtpGateway.cc:521
static FTPSM ftpSendReply
Definition: FtpGateway.cc:233
HttpRequestPointer request
Definition: Client.h:178
@ ERR_FTP_NOT_FOUND
Definition: forward.h:55
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
@ ERR_FTP_PUT_CREATED
Definition: forward.h:57
static FTPSM ftpReadRetr
Definition: FtpGateway.cc:228
char * old_reply
Definition: FtpClient.h:178
@ ERR_DIR_LISTING
Definition: forward.h:70
static FTPSM ftpReadPass
Definition: FtpGateway.cc:203
void setCurrentOffset(int64_t offset)
Definition: FtpGateway.cc:147
void switchTimeoutToDataChannel()
Definition: FtpClient.cc:1065
size_type copy(char *dest, size_type n) const
Definition: SBuf.cc:500
#define SQUIDSBUFPH
Definition: SBuf.h:31
char mimeGetTransferMode(const char *fn)
Definition: mime.cc:205
void PF(int, void *)
Definition: forward.h:18
static FTPSM ftpWriteTransferDone
Definition: FtpGateway.cc:232
class SquidConfig Config
Definition: SquidConfig.cc:12
#define REG_NOSUB
Definition: GnuRegex.h:239
static FTPSM ftpGetFile
Definition: FtpGateway.cc:218
bool epsv_all_sent
EPSV ALL has been used. Must abort on failures.
Definition: FtpGateway.cc:57
static void Start(const Pointer &job)
Definition: AsyncJob.cc:24
int regcomp(regex_t *preg, const char *pattern, int cflags)
Definition: GnuRegex.c:4117

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors