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