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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors