FtpGateway.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 "clients/forward.h"
14 #include "clients/FtpClient.h"
15 #include "comm.h"
16 #include "comm/ConnOpener.h"
17 #include "comm/Read.h"
18 #include "comm/TcpAcceptor.h"
19 #include "CommCalls.h"
20 #include "compat/strtoll.h"
21 #include "errorpage.h"
22 #include "fd.h"
23 #include "fde.h"
24 #include "FwdState.h"
25 #include "html_quote.h"
26 #include "HttpHdrContRange.h"
27 #include "HttpHeader.h"
28 #include "HttpHeaderRange.h"
29 #include "HttpReply.h"
30 #include "ip/tools.h"
31 #include "MemBuf.h"
32 #include "mime.h"
33 #include "rfc1738.h"
34 #include "SquidConfig.h"
35 #include "SquidString.h"
36 #include "SquidTime.h"
37 #include "StatCounters.h"
38 #include "Store.h"
39 #include "tools.h"
40 #include "URL.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  MemBuf *htmlifyListEntry(const char *line);
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 
156  static HttpReply *ftpAuthRequired(HttpRequest * request, SBuf &realm);
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 
180 CBDATA_NAMESPACED_CLASS_INIT(Ftp, Gateway);
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  */
200 static FTPSM ftpReadWelcome;
201 static FTPSM ftpSendUser;
202 static FTPSM ftpReadUser;
203 static FTPSM ftpSendPass;
204 static FTPSM ftpReadPass;
205 static FTPSM ftpSendType;
206 static FTPSM ftpReadType;
207 static FTPSM ftpSendMdtm;
208 static FTPSM ftpReadMdtm;
209 static FTPSM ftpSendSize;
210 static FTPSM ftpReadSize;
211 #if 0
212 static FTPSM ftpSendEPRT;
213 #endif
214 static FTPSM ftpReadEPRT;
215 static FTPSM ftpSendPORT;
216 static FTPSM ftpReadPORT;
217 static FTPSM ftpSendPassive;
218 static FTPSM ftpReadEPSV;
219 static FTPSM ftpReadPasv;
220 static FTPSM ftpTraverseDirectory;
221 static FTPSM ftpListDir;
222 static FTPSM ftpGetFile;
223 static FTPSM ftpSendCwd;
224 static FTPSM ftpReadCwd;
225 static FTPSM ftpRestOrList;
226 static FTPSM ftpSendList;
227 static FTPSM ftpSendNlst;
228 static FTPSM ftpReadList;
229 static FTPSM ftpSendRest;
230 static FTPSM ftpReadRest;
231 static FTPSM ftpSendRetr;
232 static FTPSM ftpReadRetr;
233 static FTPSM ftpReadTransferDone;
234 static FTPSM ftpSendStor;
235 static FTPSM ftpReadStor;
236 static FTPSM ftpWriteTransferDone;
237 static FTPSM ftpSendReply;
238 static FTPSM ftpSendMkdir;
239 static FTPSM ftpReadMkdir;
240 static FTPSM ftpFail;
241 static FTPSM ftpSendQuit;
242 static FTPSM ftpReadQuit;
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 
473  assert(Comm::IsConnOpen(conn));
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  const char *ct = NULL;
535  char *tokens[MAX_TOKENS];
536  int i;
537  int n_tokens;
538  static char tbuf[128];
539  char *xbuf = NULL;
540  static int scan_ftp_initialized = 0;
541  static regex_t scan_ftp_integer;
542  static regex_t scan_ftp_time;
543  static regex_t scan_ftp_dostime;
544  static regex_t scan_ftp_dosdate;
545 
546  if (!scan_ftp_initialized) {
547  scan_ftp_initialized = 1;
548  regcomp(&scan_ftp_integer, "^[0123456789]+$", REG_EXTENDED | REG_NOSUB);
549  regcomp(&scan_ftp_time, "^[0123456789:]+$", REG_EXTENDED | REG_NOSUB);
550  regcomp(&scan_ftp_dosdate, "^[0123456789]+-[0123456789]+-[0123456789]+$", REG_EXTENDED | REG_NOSUB);
551  regcomp(&scan_ftp_dostime, "^[0123456789]+:[0123456789]+[AP]M$", REG_EXTENDED | REG_NOSUB | REG_ICASE);
552  }
553 
554  if (buf == NULL)
555  return NULL;
556 
557  if (*buf == '\0')
558  return NULL;
559 
560  p = (ftpListParts *)xcalloc(1, sizeof(ftpListParts));
561 
562  n_tokens = 0;
563 
564  memset(tokens, 0, sizeof(tokens));
565 
566  xbuf = xstrdup(buf);
567 
568  if (flags.tried_nlst) {
569  /* Machine readable format, one name per line */
570  p->name = xbuf;
571  p->type = '\0';
572  return p;
573  }
574 
575  for (t = strtok(xbuf, w_space); t && n_tokens < MAX_TOKENS; t = strtok(NULL, w_space)) {
576  tokens[n_tokens] = xstrdup(t);
577  ++n_tokens;
578  }
579 
580  xfree(xbuf);
581 
582  /* locate the Month field */
583  for (i = 3; i < n_tokens - 2; ++i) {
584  char *size = tokens[i - 1];
585  char *month = tokens[i];
586  char *day = tokens[i + 1];
587  char *year = tokens[i + 2];
588 
589  if (!is_month(month))
590  continue;
591 
592  if (regexec(&scan_ftp_integer, size, 0, NULL, 0) != 0)
593  continue;
594 
595  if (regexec(&scan_ftp_integer, day, 0, NULL, 0) != 0)
596  continue;
597 
598  if (regexec(&scan_ftp_time, year, 0, NULL, 0) != 0) /* Yr | hh:mm */
599  continue;
600 
601  snprintf(tbuf, 128, "%s %2s %5s",
602  month, day, year);
603 
604  if (!strstr(buf, tbuf))
605  snprintf(tbuf, 128, "%s %2s %-5s",
606  month, day, year);
607 
608  char const *copyFrom = NULL;
609 
610  if ((copyFrom = strstr(buf, tbuf))) {
611  p->type = *tokens[0];
612  p->size = strtoll(size, NULL, 10);
613  p->date = xstrdup(tbuf);
614 
615  if (flags.skip_whitespace) {
616  copyFrom += strlen(tbuf);
617 
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  copyFrom += strlen(tbuf);
630  if (strchr(w_space, *copyFrom))
631  ++copyFrom;
632  }
633 
634  p->name = xstrdup(copyFrom);
635 
636  if (p->type == 'l' && (t = strstr(p->name, " -> "))) {
637  *t = '\0';
638  p->link = xstrdup(t + 4);
639  }
640 
641  goto found;
642  }
643 
644  break;
645  }
646 
647  /* try it as a DOS listing, 04-05-70 09:33PM ... */
648  if (n_tokens > 3 &&
649  regexec(&scan_ftp_dosdate, tokens[0], 0, NULL, 0) == 0 &&
650  regexec(&scan_ftp_dostime, tokens[1], 0, NULL, 0) == 0) {
651  if (!strcasecmp(tokens[2], "<dir>")) {
652  p->type = 'd';
653  } else {
654  p->type = '-';
655  p->size = strtoll(tokens[2], NULL, 10);
656  }
657 
658  snprintf(tbuf, 128, "%s %s", tokens[0], tokens[1]);
659  p->date = xstrdup(tbuf);
660 
661  if (p->type == 'd') {
662  /* Directory.. name begins with first printable after <dir> */
663  ct = strstr(buf, tokens[2]);
664  ct += strlen(tokens[2]);
665 
666  while (xisspace(*ct))
667  ++ct;
668 
669  if (!*ct)
670  ct = NULL;
671  } else {
672  /* A file. Name begins after size, with a space in between */
673  snprintf(tbuf, 128, " %s %s", tokens[2], tokens[3]);
674  ct = strstr(buf, tbuf);
675 
676  if (ct) {
677  ct += strlen(tokens[2]) + 2;
678  }
679  }
680 
681  p->name = xstrdup(ct ? ct : tokens[3]);
682  goto found;
683  }
684 
685  /* Try EPLF format; carson@lehman.com */
686  if (buf[0] == '+') {
687  ct = buf + 1;
688  p->type = 0;
689 
690  while (ct && *ct) {
691  time_t tm;
692  int l = strcspn(ct, ",");
693  char *tmp;
694 
695  if (l < 1)
696  goto blank;
697 
698  switch (*ct) {
699 
700  case '\t':
701  p->name = xstrndup(ct + 1, l + 1);
702  break;
703 
704  case 's':
705  p->size = atoi(ct + 1);
706  break;
707 
708  case 'm':
709  tm = (time_t) strtol(ct + 1, &tmp, 0);
710 
711  if (tmp != ct + 1)
712  break; /* not a valid integer */
713 
714  p->date = xstrdup(ctime(&tm));
715 
716  *(strstr(p->date, "\n")) = '\0';
717 
718  break;
719 
720  case '/':
721  p->type = 'd';
722 
723  break;
724 
725  case 'r':
726  p->type = '-';
727 
728  break;
729 
730  case 'i':
731  break;
732 
733  default:
734  break;
735  }
736 
737 blank:
738  ct = strstr(ct, ",");
739 
740  if (ct) {
741  ++ct;
742  }
743  }
744 
745  if (p->type == 0) {
746  p->type = '-';
747  }
748 
749  if (p->name)
750  goto found;
751  else
752  safe_free(p->date);
753  }
754 
755 found:
756 
757  for (i = 0; i < n_tokens; ++i)
758  xfree(tokens[i]);
759 
760  if (!p->name)
761  ftpListPartsFree(&p); /* cleanup */
762 
763  return p;
764 }
765 
766 MemBuf *
768 {
769  char icon[2048];
770  char href[2048 + 40];
771  char text[ 2048];
772  char size[ 2048];
773  char chdir[ 2048 + 40];
774  char view[ 2048 + 40];
775  char download[ 2048 + 40];
776  char link[ 2048 + 40];
777  MemBuf *html;
778  char prefix[2048];
779  ftpListParts *parts;
780  *icon = *href = *text = *size = *chdir = *view = *download = *link = '\0';
781 
782  debugs(9, 7, HERE << " line ={" << line << "}");
783 
784  if (strlen(line) > 1024) {
785  html = new MemBuf();
786  html->init();
787  html->appendf("<tr><td colspan=\"5\">%s</td></tr>\n", line);
788  return html;
789  }
790 
791  if (flags.dir_slash && dirpath && typecode != 'D')
792  snprintf(prefix, 2048, "%s/", rfc1738_escape_part(dirpath));
793  else
794  prefix[0] = '\0';
795 
796  if ((parts = ftpListParseParts(line, flags)) == NULL) {
797  const char *p;
798 
799  html = new MemBuf();
800  html->init();
801  html->appendf("<tr class=\"entry\"><td colspan=\"5\">%s</td></tr>\n", line);
802 
803  for (p = line; *p && xisspace(*p); ++p);
804  if (*p && !xisspace(*p))
805  flags.listformat_unknown = 1;
806 
807  return html;
808  }
809 
810  if (!strcmp(parts->name, ".") || !strcmp(parts->name, "..")) {
811  ftpListPartsFree(&parts);
812  return NULL;
813  }
814 
815  parts->size += 1023;
816  parts->size >>= 10;
817  parts->showname = xstrdup(parts->name);
818 
819  /* {icon} {text} . . . {date}{size}{chdir}{view}{download}{link}\n */
820  xstrncpy(href, rfc1738_escape_part(parts->name), 2048);
821 
822  xstrncpy(text, parts->showname, 2048);
823 
824  switch (parts->type) {
825 
826  case 'd':
827  snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
828  mimeGetIconURL("internal-dir"),
829  "[DIR]");
830  strcat(href, "/"); /* margin is allocated above */
831  break;
832 
833  case 'l':
834  snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
835  mimeGetIconURL("internal-link"),
836  "[LINK]");
837  /* sometimes there is an 'l' flag, but no "->" link */
838 
839  if (parts->link) {
840  char *link2 = xstrdup(html_quote(rfc1738_escape(parts->link)));
841  snprintf(link, 2048, " -&gt; <a href=\"%s%s\">%s</a>",
842  *link2 != '/' ? prefix : "", link2,
843  html_quote(parts->link));
844  safe_free(link2);
845  }
846 
847  break;
848 
849  case '\0':
850  snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
851  mimeGetIconURL(parts->name),
852  "[UNKNOWN]");
853  snprintf(chdir, 2048, "<a href=\"%s/;type=d\"><img border=\"0\" src=\"%s\" "
854  "alt=\"[DIR]\"></a>",
855  rfc1738_escape_part(parts->name),
856  mimeGetIconURL("internal-dir"));
857  break;
858 
859  case '-':
860 
861  default:
862  snprintf(icon, 2048, "<img border=\"0\" src=\"%s\" alt=\"%-6s\">",
863  mimeGetIconURL(parts->name),
864  "[FILE]");
865  snprintf(size, 2048, " %6" PRId64 "k", parts->size);
866  break;
867  }
868 
869  if (parts->type != 'd') {
870  if (mimeGetViewOption(parts->name)) {
871  snprintf(view, 2048, "<a href=\"%s%s;type=a\"><img border=\"0\" src=\"%s\" "
872  "alt=\"[VIEW]\"></a>",
873  prefix, href, mimeGetIconURL("internal-view"));
874  }
875 
876  if (mimeGetDownloadOption(parts->name)) {
877  snprintf(download, 2048, "<a href=\"%s%s;type=i\"><img border=\"0\" src=\"%s\" "
878  "alt=\"[DOWNLOAD]\"></a>",
879  prefix, href, mimeGetIconURL("internal-download"));
880  }
881  }
882 
883  /* construct the table row from parts. */
884  html = new MemBuf();
885  html->init();
886  html->appendf("<tr class=\"entry\">"
887  "<td class=\"icon\"><a href=\"%s%s\">%s</a></td>"
888  "<td class=\"filename\"><a href=\"%s%s\">%s</a></td>"
889  "<td class=\"date\">%s</td>"
890  "<td class=\"size\">%s</td>"
891  "<td class=\"actions\">%s%s%s%s</td>"
892  "</tr>\n",
893  prefix, href, icon,
894  prefix, href, html_quote(text),
895  parts->date,
896  size,
897  chdir, view, download, link);
898 
899  ftpListPartsFree(&parts);
900  return html;
901 }
902 
903 void
905 {
906  char *buf = data.readBuf->content();
907  char *sbuf; /* NULL-terminated copy of termedBuf */
908  char *end;
909  char *line;
910  char *s;
911  MemBuf *t;
912  size_t linelen;
913  size_t usable;
914  size_t len = data.readBuf->contentSize();
915 
916  if (!len) {
917  debugs(9, 3, HERE << "no content to parse for " << entry->url() );
918  return;
919  }
920 
921  /*
922  * We need a NULL-terminated buffer for scanning, ick
923  */
924  sbuf = (char *)xmalloc(len + 1);
925  xstrncpy(sbuf, buf, len + 1);
926  end = sbuf + len - 1;
927 
928  while (*end != '\r' && *end != '\n' && end > sbuf)
929  --end;
930 
931  usable = end - sbuf;
932 
933  debugs(9, 3, HERE << "usable = " << usable << " of " << len << " bytes.");
934 
935  if (usable == 0) {
936  if (buf[0] == '\0' && len == 1) {
937  debugs(9, 3, HERE << "NIL ends data from " << entry->url() << " transfer problem?");
938  data.readBuf->consume(len);
939  } else {
940  debugs(9, 3, HERE << "didn't find end for " << entry->url());
941  debugs(9, 3, HERE << "buffer remains (" << len << " bytes) '" << rfc1738_do_escape(buf,0) << "'");
942  }
943  xfree(sbuf);
944  return;
945  }
946 
947  debugs(9, 3, HERE << (unsigned long int)len << " bytes to play with");
948 
949  line = (char *)memAllocate(MEM_4K_BUF);
950  ++end;
951  s = sbuf;
952  s += strspn(s, crlf);
953 
954  for (; s < end; s += strcspn(s, crlf), s += strspn(s, crlf)) {
955  debugs(9, 7, HERE << "s = {" << s << "}");
956  linelen = strcspn(s, crlf) + 1;
957 
958  if (linelen < 2)
959  break;
960 
961  if (linelen > 4096)
962  linelen = 4096;
963 
964  xstrncpy(line, s, linelen);
965 
966  debugs(9, 7, HERE << "{" << line << "}");
967 
968  if (!strncmp(line, "total", 5))
969  continue;
970 
971  t = htmlifyListEntry(line);
972 
973  if ( t != NULL) {
974  debugs(9, 7, HERE << "listing append: t = {" << t->contentSize() << ", '" << t->content() << "'}");
975  listing.append(t->content(), t->contentSize());
976  delete t;
977  }
978  }
979 
980  debugs(9, 7, HERE << "Done.");
981  data.readBuf->consume(usable);
982  memFree(line, MEM_4K_BUF);
983  xfree(sbuf);
984 }
985 
986 void
988 {
989  debugs(9, 3, status());
990 
991  if (request->method == Http::METHOD_HEAD && (flags.isdir || theSize != -1)) {
992  serverComplete();
993  return;
994  }
995 
996  /* Directory listings are special. They write ther own headers via the error objects */
997  if (!flags.http_header_sent && data.readBuf->contentSize() >= 0 && !flags.isdir)
998  appendSuccessHeader();
999 
1000  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1001  /*
1002  * probably was aborted because content length exceeds one
1003  * of the maximum size limits.
1004  */
1005  abortAll("entry aborted after calling appendSuccessHeader()");
1006  return;
1007  }
1008 
1009 #if USE_ADAPTATION
1010 
1011  if (adaptationAccessCheckPending) {
1012  debugs(9, 3, "returning from Ftp::Gateway::processReplyBody due to adaptationAccessCheckPending");
1013  return;
1014  }
1015 
1016 #endif
1017 
1018  if (flags.isdir) {
1019  if (!flags.listing) {
1020  flags.listing = 1;
1021  listing.reset();
1022  }
1023  parseListing();
1024  maybeReadVirginBody();
1025  return;
1026  } else if (const int csize = data.readBuf->contentSize()) {
1027  writeReplyBody(data.readBuf->content(), csize);
1028  debugs(9, 5, HERE << "consuming " << csize << " bytes of readBuf");
1029  data.readBuf->consume(csize);
1030  }
1031 
1032  entry->flush();
1033 
1034  maybeReadVirginBody();
1035 }
1036 
1053 int
1055 {
1056  /* default username */
1057  xstrncpy(user, "anonymous", MAX_URL);
1058 
1059 #if HAVE_AUTH_MODULE_BASIC
1060  /* Check HTTP Authorization: headers (better than defaults, but less than URL) */
1061  const SBuf auth(req_hdr->getAuth(Http::HdrType::AUTHORIZATION, "Basic"));
1062  if (!auth.isEmpty()) {
1063  flags.authenticated = 1;
1064  loginParser(auth, false);
1065  }
1066  /* we fail with authorization-required error later IFF the FTP server requests it */
1067 #endif
1068 
1069  /* Test URL login syntax. Overrides any headers received. */
1070  loginParser(request->url.userInfo(), true);
1071 
1072  /* name is missing. thats fatal. */
1073  if (!user[0])
1074  fatal("FTP login parsing destroyed username info");
1075 
1076  /* name + password == success */
1077  if (password[0])
1078  return 1;
1079 
1080  /* Setup default FTP password settings */
1081  /* this has to be done last so that we can have a no-password case above. */
1082  if (!password[0]) {
1083  if (strcmp(user, "anonymous") == 0 && !flags.tried_auth_anonymous) {
1084  xstrncpy(password, Config.Ftp.anon_user, MAX_URL);
1085  flags.tried_auth_anonymous=1;
1086  return 1;
1087  } else if (!flags.tried_auth_nopass) {
1088  xstrncpy(password, null_string, MAX_URL);
1089  flags.tried_auth_nopass=1;
1090  return 1;
1091  }
1092  }
1093 
1094  return 0; /* different username */
1095 }
1096 
1097 void
1099 {
1100  static SBuf str_type_eq("type=");
1101  auto t = request->url.path().rfind(';');
1102 
1103  if (t != SBuf::npos) {
1104  auto filenameEnd = t-1;
1105  if (request->url.path().substr(++t).cmp(str_type_eq, str_type_eq.length()) == 0) {
1106  t += str_type_eq.length();
1107  typecode = (char)xtoupper(request->url.path()[t]);
1108  request->url.path(request->url.path().substr(0,filenameEnd));
1109  }
1110  }
1111 
1112  int l = request->url.path().length();
1113  /* check for null path */
1114 
1115  if (!l) {
1116  flags.isdir = 1;
1117  flags.root_dir = 1;
1118  flags.need_base_href = 1; /* Work around broken browsers */
1119  } else if (!request->url.path().cmp("/%2f/")) {
1120  /* UNIX root directory */
1121  flags.isdir = 1;
1122  flags.root_dir = 1;
1123  } else if ((l >= 1) && (request->url.path()[l-1] == '/')) {
1124  /* Directory URL, ending in / */
1125  flags.isdir = 1;
1126 
1127  if (l == 1)
1128  flags.root_dir = 1;
1129  } else {
1130  flags.dir_slash = 1;
1131  }
1132 }
1133 
1134 void
1136 {
1137  title_url = "ftp://";
1138 
1139  if (strcmp(user, "anonymous")) {
1140  title_url.append(user);
1141  title_url.append("@");
1142  }
1143 
1144  SBuf authority = request->url.authority(request->url.getScheme() != AnyP::PROTO_FTP);
1145 
1146  title_url.append(authority.rawContent(), authority.length());
1147  title_url.append(request->url.path().rawContent(), request->url.path().length());
1148 
1149  base_href = "ftp://";
1150 
1151  if (strcmp(user, "anonymous") != 0) {
1152  base_href.append(rfc1738_escape_part(user));
1153 
1154  if (password_url) {
1155  base_href.append(":");
1156  base_href.append(rfc1738_escape_part(password));
1157  }
1158 
1159  base_href.append("@");
1160  }
1161 
1162  base_href.append(authority.rawContent(), authority.length());
1163  base_href.append(request->url.path().rawContent(), request->url.path().length());
1164  base_href.append("/");
1165 }
1166 
1167 void
1169 {
1170  if (!checkAuth(&request->header)) {
1171  /* create appropriate reply */
1172  SBuf realm(ftpRealm()); // local copy so SBuf wont disappear too early
1173  HttpReply *reply = ftpAuthRequired(request.getRaw(), realm);
1174  entry->replaceHttpReply(reply);
1175  serverComplete();
1176  return;
1177  }
1178 
1179  checkUrlpath();
1180  buildTitleUrl();
1181  debugs(9, 5, "FD " << (ctrl.conn ? ctrl.conn->fd : -1) << " : host=" << request->url.host() <<
1182  ", path=" << request->url.path() << ", user=" << user << ", passwd=" << password);
1183  state = BEGIN;
1185 }
1186 
1187 /* ====================================================================== */
1188 
1189 void
1191 {
1193  if (ctrl.message == NULL)
1194  return; // didn't get complete reply yet
1195 
1196  /* Copy the message except for the last line to cwd_message to be
1197  * printed in error messages.
1198  */
1199  for (wordlist *w = ctrl.message; w && w->next; w = w->next) {
1200  cwd_message.append('\n');
1201  cwd_message.append(w->key);
1202  }
1203 
1204  FTP_SM_FUNCS[state] (this);
1205 }
1206 
1207 /* ====================================================================== */
1208 
1209 static void
1211 {
1212  int code = ftpState->ctrl.replycode;
1213  debugs(9, 3, HERE);
1214 
1215  if (ftpState->flags.pasv_only)
1216  ++ ftpState->login_att;
1217 
1218  if (code == 220) {
1219  if (ftpState->ctrl.message) {
1220  if (strstr(ftpState->ctrl.message->key, "NetWare"))
1221  ftpState->flags.skip_whitespace = 1;
1222  }
1223 
1224  ftpSendUser(ftpState);
1225  } else if (code == 120) {
1226  if (NULL != ftpState->ctrl.message)
1227  debugs(9, DBG_IMPORTANT, "FTP server is busy: " << ftpState->ctrl.message->key);
1228 
1229  return;
1230  } else {
1231  ftpFail(ftpState);
1232  }
1233 }
1234 
1240 void
1242 {
1243  ErrorState *err = NULL;
1244 
1245  if ((state == SENT_USER || state == SENT_PASS) && ctrl.replycode >= 400) {
1246  if (ctrl.replycode == 421 || ctrl.replycode == 426) {
1247  // 421/426 - Service Overload - retry permitted.
1248  err = new ErrorState(ERR_FTP_UNAVAILABLE, Http::scServiceUnavailable, fwd->request);
1249  } else if (ctrl.replycode >= 430 && ctrl.replycode <= 439) {
1250  // 43x - Invalid or Credential Error - retry challenge required.
1251  err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request);
1252  } else if (ctrl.replycode >= 530 && ctrl.replycode <= 539) {
1253  // 53x - Credentials Missing - retry challenge required
1254  if (password_url) // but they were in the URI! major fail.
1255  err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scForbidden, fwd->request);
1256  else
1257  err = new ErrorState(ERR_FTP_FORBIDDEN, Http::scUnauthorized, fwd->request);
1258  }
1259  }
1260 
1261  if (!err) {
1262  ftpFail(this);
1263  return;
1264  }
1265 
1266  failed(ERR_NONE, ctrl.replycode, err);
1267  // any other problems are general falures.
1268 
1269  HttpReply *newrep = err->BuildHttpReply();
1270  delete err;
1271 
1272 #if HAVE_AUTH_MODULE_BASIC
1273  /* add Authenticate header */
1274  // XXX: performance regression. c_str() may reallocate
1275  SBuf realm(ftpRealm()); // local copy so SBuf wont disappear too early
1276  newrep->header.putAuth("Basic", realm.c_str());
1277 #endif
1278 
1279  // add it to the store entry for response....
1280  entry->replaceHttpReply(newrep);
1281  serverComplete();
1282 }
1283 
1284 SBuf
1286 {
1287  SBuf realm;
1288 
1289  /* This request is not fully authenticated */
1290  realm.appendf("FTP %s ", user);
1291  if (!request)
1292  realm.append("unknown", 7);
1293  else {
1294  realm.append(request->url.host());
1295  if (request->url.port() != 21)
1296  realm.appendf(" port %d", request->url.port());
1297  }
1298  return realm;
1299 }
1300 
1301 static void
1303 {
1304  /* check the server control channel is still available */
1305  if (!ftpState || !ftpState->haveControlChannel("ftpSendUser"))
1306  return;
1307 
1308  if (ftpState->proxy_host != NULL)
1309  snprintf(cbuf, CTRL_BUFLEN, "USER %s@%s\r\n", ftpState->user, ftpState->request->url.host());
1310  else
1311  snprintf(cbuf, CTRL_BUFLEN, "USER %s\r\n", ftpState->user);
1312 
1313  ftpState->writeCommand(cbuf);
1314 
1315  ftpState->state = Ftp::Client::SENT_USER;
1316 }
1317 
1318 static void
1320 {
1321  int code = ftpState->ctrl.replycode;
1322  debugs(9, 3, HERE);
1323 
1324  if (code == 230) {
1325  ftpReadPass(ftpState);
1326  } else if (code == 331) {
1327  ftpSendPass(ftpState);
1328  } else {
1329  ftpState->loginFailed();
1330  }
1331 }
1332 
1333 static void
1335 {
1336  /* check the server control channel is still available */
1337  if (!ftpState || !ftpState->haveControlChannel("ftpSendPass"))
1338  return;
1339 
1340  snprintf(cbuf, CTRL_BUFLEN, "PASS %s\r\n", ftpState->password);
1341  ftpState->writeCommand(cbuf);
1342  ftpState->state = Ftp::Client::SENT_PASS;
1343 }
1344 
1345 static void
1347 {
1348  int code = ftpState->ctrl.replycode;
1349  debugs(9, 3, HERE << "code=" << code);
1350 
1351  if (code == 230) {
1352  ftpSendType(ftpState);
1353  } else {
1354  ftpState->loginFailed();
1355  }
1356 }
1357 
1358 static void
1360 {
1361  /* check the server control channel is still available */
1362  if (!ftpState || !ftpState->haveControlChannel("ftpSendType"))
1363  return;
1364 
1365  /*
1366  * Ref section 3.2.2 of RFC 1738
1367  */
1368  char mode = ftpState->typecode;
1369 
1370  switch (mode) {
1371 
1372  case 'D':
1373  mode = 'A';
1374  break;
1375 
1376  case 'A':
1377 
1378  case 'I':
1379  break;
1380 
1381  default:
1382 
1383  if (ftpState->flags.isdir) {
1384  mode = 'A';
1385  } else {
1386  auto t = ftpState->request->url.path().rfind('/');
1387  // XXX: performance regression, c_str() may reallocate
1388  SBuf filename = ftpState->request->url.path().substr(t != SBuf::npos ? t + 1 : 0);
1389  mode = mimeGetTransferMode(filename.c_str());
1390  }
1391 
1392  break;
1393  }
1394 
1395  if (mode == 'I')
1396  ftpState->flags.binary = 1;
1397  else
1398  ftpState->flags.binary = 0;
1399 
1400  snprintf(cbuf, CTRL_BUFLEN, "TYPE %c\r\n", mode);
1401 
1402  ftpState->writeCommand(cbuf);
1403 
1404  ftpState->state = Ftp::Client::SENT_TYPE;
1405 }
1406 
1407 static void
1409 {
1410  int code = ftpState->ctrl.replycode;
1411  char *path;
1412  char *d, *p;
1413  debugs(9, 3, HERE << "code=" << code);
1414 
1415  if (code == 200) {
1416  p = path = SBufToCstring(ftpState->request->url.path());
1417 
1418  if (*p == '/')
1419  ++p;
1420 
1421  while (*p) {
1422  d = p;
1423  p += strcspn(p, "/");
1424 
1425  if (*p) {
1426  *p = '\0';
1427  ++p;
1428  }
1429 
1430  rfc1738_unescape(d);
1431 
1432  if (*d)
1433  wordlistAdd(&ftpState->pathcomps, d);
1434  }
1435 
1436  xfree(path);
1437 
1438  if (ftpState->pathcomps)
1439  ftpTraverseDirectory(ftpState);
1440  else
1441  ftpListDir(ftpState);
1442  } else {
1443  ftpFail(ftpState);
1444  }
1445 }
1446 
1447 static void
1449 {
1450  debugs(9, 4, HERE << (ftpState->filepath ? ftpState->filepath : "<NULL>"));
1451 
1452  safe_free(ftpState->dirpath);
1453  ftpState->dirpath = ftpState->filepath;
1454  ftpState->filepath = NULL;
1455 
1456  /* Done? */
1457 
1458  if (ftpState->pathcomps == NULL) {
1459  debugs(9, 3, HERE << "the final component was a directory");
1460  ftpListDir(ftpState);
1461  return;
1462  }
1463 
1464  /* Go to next path component */
1465  ftpState->filepath = wordlistChopHead(& ftpState->pathcomps);
1466 
1467  /* Check if we are to CWD or RETR */
1468  if (ftpState->pathcomps != NULL || ftpState->flags.isdir) {
1469  ftpSendCwd(ftpState);
1470  } else {
1471  debugs(9, 3, HERE << "final component is probably a file");
1472  ftpGetFile(ftpState);
1473  return;
1474  }
1475 }
1476 
1477 static void
1479 {
1480  char *path = NULL;
1481 
1482  /* check the server control channel is still available */
1483  if (!ftpState || !ftpState->haveControlChannel("ftpSendCwd"))
1484  return;
1485 
1486  debugs(9, 3, HERE);
1487 
1488  path = ftpState->filepath;
1489 
1490  if (!strcmp(path, "..") || !strcmp(path, "/")) {
1491  ftpState->flags.no_dotdot = 1;
1492  } else {
1493  ftpState->flags.no_dotdot = 0;
1494  }
1495 
1496  snprintf(cbuf, CTRL_BUFLEN, "CWD %s\r\n", path);
1497 
1498  ftpState->writeCommand(cbuf);
1499 
1500  ftpState->state = Ftp::Client::SENT_CWD;
1501 }
1502 
1503 static void
1505 {
1506  int code = ftpState->ctrl.replycode;
1507  debugs(9, 3, HERE);
1508 
1509  if (code >= 200 && code < 300) {
1510  /* CWD OK */
1511  ftpState->unhack();
1512 
1513  /* Reset cwd_message to only include the last message */
1514  ftpState->cwd_message.reset("");
1515  for (wordlist *w = ftpState->ctrl.message; w; w = w->next) {
1516  ftpState->cwd_message.append('\n');
1517  ftpState->cwd_message.append(w->key);
1518  }
1519  ftpState->ctrl.message = NULL;
1520 
1521  /* Continue to traverse the path */
1522  ftpTraverseDirectory(ftpState);
1523  } else {
1524  /* CWD FAILED */
1525 
1526  if (!ftpState->flags.put)
1527  ftpFail(ftpState);
1528  else
1529  ftpSendMkdir(ftpState);
1530  }
1531 }
1532 
1533 static void
1535 {
1536  char *path = NULL;
1537 
1538  /* check the server control channel is still available */
1539  if (!ftpState || !ftpState->haveControlChannel("ftpSendMkdir"))
1540  return;
1541 
1542  path = ftpState->filepath;
1543  debugs(9, 3, HERE << "with path=" << path);
1544  snprintf(cbuf, CTRL_BUFLEN, "MKD %s\r\n", path);
1545  ftpState->writeCommand(cbuf);
1546  ftpState->state = Ftp::Client::SENT_MKDIR;
1547 }
1548 
1549 static void
1551 {
1552  char *path = ftpState->filepath;
1553  int code = ftpState->ctrl.replycode;
1554 
1555  debugs(9, 3, HERE << "path " << path << ", code " << code);
1556 
1557  if (code == 257) { /* success */
1558  ftpSendCwd(ftpState);
1559  } else if (code == 550) { /* dir exists */
1560 
1561  if (ftpState->flags.put_mkdir) {
1562  ftpState->flags.put_mkdir = 1;
1563  ftpSendCwd(ftpState);
1564  } else
1565  ftpSendReply(ftpState);
1566  } else
1567  ftpSendReply(ftpState);
1568 }
1569 
1570 static void
1572 {
1573  assert(*ftpState->filepath != '\0');
1574  ftpState->flags.isdir = 0;
1575  ftpSendMdtm(ftpState);
1576 }
1577 
1578 static void
1580 {
1581  if (ftpState->flags.dir_slash) {
1582  debugs(9, 3, HERE << "Directory path did not end in /");
1583  ftpState->title_url.append("/");
1584  ftpState->flags.isdir = 1;
1585  }
1586 
1587  ftpSendPassive(ftpState);
1588 }
1589 
1590 static void
1592 {
1593  /* check the server control channel is still available */
1594  if (!ftpState || !ftpState->haveControlChannel("ftpSendMdtm"))
1595  return;
1596 
1597  assert(*ftpState->filepath != '\0');
1598  snprintf(cbuf, CTRL_BUFLEN, "MDTM %s\r\n", ftpState->filepath);
1599  ftpState->writeCommand(cbuf);
1600  ftpState->state = Ftp::Client::SENT_MDTM;
1601 }
1602 
1603 static void
1605 {
1606  int code = ftpState->ctrl.replycode;
1607  debugs(9, 3, HERE);
1608 
1609  if (code == 213) {
1610  ftpState->mdtm = parse_iso3307_time(ftpState->ctrl.last_reply);
1611  ftpState->unhack();
1612  } else if (code < 0) {
1613  ftpFail(ftpState);
1614  return;
1615  }
1616 
1617  ftpSendSize(ftpState);
1618 }
1619 
1620 static void
1622 {
1623  /* check the server control channel is still available */
1624  if (!ftpState || !ftpState->haveControlChannel("ftpSendSize"))
1625  return;
1626 
1627  /* Only send SIZE for binary transfers. The returned size
1628  * is useless on ASCII transfers */
1629 
1630  if (ftpState->flags.binary) {
1631  assert(ftpState->filepath != NULL);
1632  assert(*ftpState->filepath != '\0');
1633  snprintf(cbuf, CTRL_BUFLEN, "SIZE %s\r\n", ftpState->filepath);
1634  ftpState->writeCommand(cbuf);
1635  ftpState->state = Ftp::Client::SENT_SIZE;
1636  } else
1637  /* Skip to next state no non-binary transfers */
1638  ftpSendPassive(ftpState);
1639 }
1640 
1641 static void
1643 {
1644  int code = ftpState->ctrl.replycode;
1645  debugs(9, 3, HERE);
1646 
1647  if (code == 213) {
1648  ftpState->unhack();
1649  ftpState->theSize = strtoll(ftpState->ctrl.last_reply, NULL, 10);
1650 
1651  if (ftpState->theSize == 0) {
1652  debugs(9, 2, "SIZE reported " <<
1653  ftpState->ctrl.last_reply << " on " <<
1654  ftpState->title_url);
1655  ftpState->theSize = -1;
1656  }
1657  } else if (code < 0) {
1658  ftpFail(ftpState);
1659  return;
1660  }
1661 
1662  ftpSendPassive(ftpState);
1663 }
1664 
1665 static void
1667 {
1668  Ip::Address srvAddr; // unused
1669  if (ftpState->handleEpsvReply(srvAddr)) {
1670  if (ftpState->ctrl.message == NULL)
1671  return; // didn't get complete reply yet
1672 
1673  ftpState->connectDataChannel();
1674  }
1675 }
1676 
1681 static void
1683 {
1685  if (!ftpState || !ftpState->haveControlChannel("ftpSendPassive"))
1686  return;
1687 
1688  debugs(9, 3, HERE);
1689 
1692  if (ftpState->request->method == Http::METHOD_HEAD && (ftpState->flags.isdir || ftpState->theSize != -1)) {
1693  ftpState->processHeadResponse(); // may call serverComplete
1694  return;
1695  }
1696 
1697  if (ftpState->sendPassive()) {
1698  // SENT_EPSV_ALL blocks other non-EPSV connections being attempted
1699  if (ftpState->state == Ftp::Client::SENT_EPSV_ALL)
1700  ftpState->flags.epsv_all_sent = true;
1701  }
1702 }
1703 
1704 void
1706 {
1707  debugs(9, 5, HERE << "handling HEAD response");
1708  ftpSendQuit(this);
1709  appendSuccessHeader();
1710 
1711  /*
1712  * On rare occasions I'm seeing the entry get aborted after
1713  * readControlReply() and before here, probably when
1714  * trying to write to the client.
1715  */
1716  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1717  abortAll("entry aborted while processing HEAD");
1718  return;
1719  }
1720 
1721 #if USE_ADAPTATION
1722  if (adaptationAccessCheckPending) {
1723  debugs(9,3, HERE << "returning due to adaptationAccessCheckPending");
1724  return;
1725  }
1726 #endif
1727 
1728  // processReplyBody calls serverComplete() since there is no body
1729  processReplyBody();
1730 }
1731 
1732 static void
1734 {
1735  Ip::Address srvAddr; // unused
1736  if (ftpState->handlePasvReply(srvAddr))
1737  ftpState->connectDataChannel();
1738  else {
1739  ftpFail(ftpState);
1740  // Currently disabled, does not work correctly:
1741  // ftpSendEPRT(ftpState);
1742  return;
1743  }
1744 }
1745 
1746 void
1748 {
1749  debugs(9, 3, HERE);
1750  data.opener = NULL;
1751 
1752  if (io.flag != Comm::OK) {
1753  debugs(9, 2, HERE << "Failed to connect. Retrying via another method.");
1754 
1755  // ABORT on timeouts. server may be waiting on a broken TCP link.
1756  if (io.xerrno == Comm::TIMEOUT)
1757  writeCommand("ABOR\r\n");
1758 
1759  // try another connection attempt with some other method
1760  ftpSendPassive(this);
1761  return;
1762  }
1763 
1764  data.opened(io.conn, dataCloser());
1765  ftpRestOrList(this);
1766 }
1767 
1768 static void
1769 ftpOpenListenSocket(Ftp::Gateway * ftpState, int fallback)
1770 {
1772  if (ftpState->data.conn != NULL) {
1773  if ((ftpState->data.conn->flags & COMM_REUSEADDR))
1774  // NP: in fact it points to the control channel. just clear it.
1775  ftpState->data.clear();
1776  else
1777  ftpState->data.close();
1778  }
1779  safe_free(ftpState->data.host);
1780 
1781  if (!Comm::IsConnOpen(ftpState->ctrl.conn)) {
1782  debugs(9, 5, "The control connection to the remote end is closed");
1783  return;
1784  }
1785 
1786  /*
1787  * Set up a listen socket on the same local address as the
1788  * control connection.
1789  */
1791  temp->local = ftpState->ctrl.conn->local;
1792 
1793  /*
1794  * REUSEADDR is needed in fallback mode, since the same port is
1795  * used for both control and data.
1796  */
1797  if (fallback) {
1798  int on = 1;
1799  errno = 0;
1800  if (setsockopt(ftpState->ctrl.conn->fd, SOL_SOCKET, SO_REUSEADDR,
1801  (char *) &on, sizeof(on)) == -1) {
1802  int xerrno = errno;
1803  // SO_REUSEADDR is only an optimization, no need to be verbose about error
1804  debugs(9, 4, "setsockopt failed: " << xstrerr(xerrno));
1805  }
1806  ftpState->ctrl.conn->flags |= COMM_REUSEADDR;
1807  temp->flags |= COMM_REUSEADDR;
1808  } else {
1809  /* if not running in fallback mode a new port needs to be retrieved */
1810  temp->local.port(0);
1811  }
1812 
1813  ftpState->listenForDataChannel(temp);
1814 }
1815 
1816 static void
1818 {
1819  /* check the server control channel is still available */
1820  if (!ftpState || !ftpState->haveControlChannel("ftpSendPort"))
1821  return;
1822 
1823  if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent) {
1824  debugs(9, DBG_IMPORTANT, "FTP does not allow PORT method after 'EPSV ALL' has been sent.");
1825  return;
1826  }
1827 
1828  debugs(9, 3, HERE);
1829  ftpState->flags.pasv_supported = 0;
1830  ftpOpenListenSocket(ftpState, 0);
1831 
1832  if (!Comm::IsConnOpen(ftpState->data.listenConn)) {
1833  if ( ftpState->data.listenConn != NULL && !ftpState->data.listenConn->local.isIPv4() ) {
1834  /* non-IPv4 CANNOT send PORT command. */
1835  /* we got here by attempting and failing an EPRT */
1836  /* using the same reply code should simulate a PORT failure */
1837  ftpReadPORT(ftpState);
1838  return;
1839  }
1840 
1841  /* XXX Need to set error message */
1842  ftpFail(ftpState);
1843  return;
1844  }
1845 
1846  // pull out the internal IP address bytes to send in PORT command...
1847  // source them from the listen_conn->local
1848 
1849  struct addrinfo *AI = NULL;
1850  ftpState->data.listenConn->local.getAddrInfo(AI, AF_INET);
1851  unsigned char *addrptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_addr;
1852  unsigned char *portptr = (unsigned char *) &((struct sockaddr_in*)AI->ai_addr)->sin_port;
1853  snprintf(cbuf, CTRL_BUFLEN, "PORT %d,%d,%d,%d,%d,%d\r\n",
1854  addrptr[0], addrptr[1], addrptr[2], addrptr[3],
1855  portptr[0], portptr[1]);
1856  ftpState->writeCommand(cbuf);
1857  ftpState->state = Ftp::Client::SENT_PORT;
1858 
1860 }
1861 
1862 static void
1864 {
1865  int code = ftpState->ctrl.replycode;
1866  debugs(9, 3, HERE);
1867 
1868  if (code != 200) {
1869  /* Fall back on using the same port as the control connection */
1870  debugs(9, 3, "PORT not supported by remote end");
1871  ftpOpenListenSocket(ftpState, 1);
1872  }
1873 
1874  ftpRestOrList(ftpState);
1875 }
1876 
1877 #if 0
1878 static void
1879 ftpSendEPRT(Ftp::Gateway * ftpState)
1880 {
1881  /* check the server control channel is still available */
1882  if (!ftpState || !ftpState->haveControlChannel("ftpSendEPRT"))
1883  return;
1884 
1885  if (Config.Ftp.epsv_all && ftpState->flags.epsv_all_sent) {
1886  debugs(9, DBG_IMPORTANT, "FTP does not allow EPRT method after 'EPSV ALL' has been sent.");
1887  return;
1888  }
1889 
1890  if (!Config.Ftp.eprt) {
1891  /* Disabled. Switch immediately to attempting old PORT command. */
1892  debugs(9, 3, "EPRT disabled by local administrator");
1893  ftpSendPORT(ftpState);
1894  return;
1895  }
1896 
1897  debugs(9, 3, HERE);
1898  ftpState->flags.pasv_supported = 0;
1899 
1900  ftpOpenListenSocket(ftpState, 0);
1901  debugs(9, 3, "Listening for FTP data connection with FD " << ftpState->data.conn);
1902  if (!Comm::IsConnOpen(ftpState->data.conn)) {
1903  /* XXX Need to set error message */
1904  ftpFail(ftpState);
1905  return;
1906  }
1907 
1908  char buf[MAX_IPSTRLEN];
1909 
1910  /* RFC 2428 defines EPRT as IPv6 equivalent to IPv4 PORT command. */
1911  /* Which can be used by EITHER protocol. */
1912  snprintf(cbuf, CTRL_BUFLEN, "EPRT |%d|%s|%d|\r\n",
1913  ( ftpState->data.listenConn->local.isIPv6() ? 2 : 1 ),
1914  ftpState->data.listenConn->local.toStr(buf,MAX_IPSTRLEN),
1915  ftpState->data.listenConn->local.port() );
1916 
1917  ftpState->writeCommand(cbuf);
1918  ftpState->state = Ftp::Client::SENT_EPRT;
1919 }
1920 #endif
1921 
1922 static void
1924 {
1925  int code = ftpState->ctrl.replycode;
1926  debugs(9, 3, HERE);
1927 
1928  if (code != 200) {
1929  /* Failover to attempting old PORT command. */
1930  debugs(9, 3, "EPRT not supported by remote end");
1931  ftpSendPORT(ftpState);
1932  return;
1933  }
1934 
1935  ftpRestOrList(ftpState);
1936 }
1937 
1942 void
1944 {
1945  debugs(9, 3, HERE);
1946 
1947  if (!Comm::IsConnOpen(ctrl.conn)) { /*Close handlers will cleanup*/
1948  debugs(9, 5, "The control connection to the remote end is closed");
1949  return;
1950  }
1951 
1952  if (io.flag != Comm::OK) {
1953  data.listenConn->close();
1954  data.listenConn = NULL;
1955  debugs(9, DBG_IMPORTANT, "FTP AcceptDataConnection: " << io.conn << ": " << xstrerr(io.xerrno));
1957  ftpFail(this);
1958  return;
1959  }
1960 
1961  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1962  abortAll("entry aborted when accepting data conn");
1963  data.listenConn->close();
1964  data.listenConn = NULL;
1965  io.conn->close();
1966  return;
1967  }
1968 
1969  /* data listening conn is no longer even open. abort. */
1970  if (!Comm::IsConnOpen(data.listenConn)) {
1971  data.listenConn = NULL; // ensure that it's cleared and not just closed.
1972  return;
1973  }
1974 
1975  /* data listening conn is no longer even open. abort. */
1976  if (!Comm::IsConnOpen(data.conn)) {
1977  data.clear(); // ensure that it's cleared and not just closed.
1978  return;
1979  }
1980 
1987  if (Config.Ftp.sanitycheck) {
1988  // accept if either our data or ctrl connection is talking to this remote peer.
1989  if (data.conn->remote != io.conn->remote && ctrl.conn->remote != io.conn->remote) {
1990  debugs(9, DBG_IMPORTANT,
1991  "FTP data connection from unexpected server (" <<
1992  io.conn->remote << "), expecting " <<
1993  data.conn->remote << " or " << ctrl.conn->remote);
1994 
1995  /* close the bad sources connection down ASAP. */
1996  io.conn->close();
1997 
1998  /* drop the bad connection (io) by ignoring the attempt. */
1999  return;
2000  }
2001  }
2002 
2004  data.close();
2005  data.opened(io.conn, dataCloser());
2006  data.addr(io.conn->remote);
2007 
2008  debugs(9, 3, HERE << "Connected data socket on " <<
2009  io.conn << ". FD table says: " <<
2010  "ctrl-peer= " << fd_table[ctrl.conn->fd].ipaddr << ", " <<
2011  "data-peer= " << fd_table[data.conn->fd].ipaddr);
2012 
2013  assert(haveControlChannel("ftpAcceptDataConnection"));
2014  assert(ctrl.message == NULL);
2015 
2016  // Ctrl channel operations will determine what happens to this data connection
2017 }
2018 
2019 static void
2021 {
2022  debugs(9, 3, HERE);
2023 
2024  if (ftpState->typecode == 'D') {
2025  ftpState->flags.isdir = 1;
2026 
2027  if (ftpState->flags.put) {
2028  ftpSendMkdir(ftpState); /* PUT name;type=d */
2029  } else {
2030  ftpSendNlst(ftpState); /* GET name;type=d sec 3.2.2 of RFC 1738 */
2031  }
2032  } else if (ftpState->flags.put) {
2033  ftpSendStor(ftpState);
2034  } else if (ftpState->flags.isdir)
2035  ftpSendList(ftpState);
2036  else if (ftpState->restartable())
2037  ftpSendRest(ftpState);
2038  else
2039  ftpSendRetr(ftpState);
2040 }
2041 
2042 static void
2044 {
2045  /* check the server control channel is still available */
2046  if (!ftpState || !ftpState->haveControlChannel("ftpSendStor"))
2047  return;
2048 
2049  debugs(9, 3, HERE);
2050 
2051  if (ftpState->filepath != NULL) {
2052  /* Plain file upload */
2053  snprintf(cbuf, CTRL_BUFLEN, "STOR %s\r\n", ftpState->filepath);
2054  ftpState->writeCommand(cbuf);
2055  ftpState->state = Ftp::Client::SENT_STOR;
2056  } else if (ftpState->request->header.getInt64(Http::HdrType::CONTENT_LENGTH) > 0) {
2057  /* File upload without a filename. use STOU to generate one */
2058  snprintf(cbuf, CTRL_BUFLEN, "STOU\r\n");
2059  ftpState->writeCommand(cbuf);
2060  ftpState->state = Ftp::Client::SENT_STOR;
2061  } else {
2062  /* No file to transfer. Only create directories if needed */
2063  ftpSendReply(ftpState);
2064  }
2065 }
2066 
2068 static void
2070 {
2071  ftpState->readStor();
2072 }
2073 
2075 {
2076  int code = ctrl.replycode;
2077  debugs(9, 3, HERE);
2078 
2079  if (code == 125 || (code == 150 && Comm::IsConnOpen(data.conn))) {
2080  if (!originalRequest()->body_pipe) {
2081  debugs(9, 3, "zero-size STOR?");
2082  state = WRITING_DATA; // make ftpWriteTransferDone() responsible
2083  dataComplete(); // XXX: keep in sync with doneSendingRequestBody()
2084  return;
2085  }
2086 
2087  if (!startRequestBodyFlow()) { // register to receive body data
2088  ftpFail(this);
2089  return;
2090  }
2091 
2092  /* When client status is 125, or 150 and the data connection is open, Begin data transfer. */
2093  debugs(9, 3, HERE << "starting data transfer");
2094  switchTimeoutToDataChannel();
2095  sendMoreRequestBody();
2096  fwd->dontRetry(true); // dont permit re-trying if the body was sent.
2097  state = WRITING_DATA;
2098  debugs(9, 3, HERE << "writing data channel");
2099  } else if (code == 150) {
2100  /* When client code is 150 with no data channel, Accept data channel. */
2101  debugs(9, 3, "ftpReadStor: accepting data channel");
2102  listenForDataChannel(data.conn);
2103  } else {
2104  debugs(9, DBG_IMPORTANT, HERE << "Unexpected reply code "<< std::setfill('0') << std::setw(3) << code);
2105  ftpFail(this);
2106  }
2107 }
2108 
2109 static void
2111 {
2112  /* check the server control channel is still available */
2113  if (!ftpState || !ftpState->haveControlChannel("ftpSendRest"))
2114  return;
2115 
2116  debugs(9, 3, HERE);
2117 
2118  snprintf(cbuf, CTRL_BUFLEN, "REST %" PRId64 "\r\n", ftpState->restart_offset);
2119  ftpState->writeCommand(cbuf);
2120  ftpState->state = Ftp::Client::SENT_REST;
2121 }
2122 
2123 int
2125 {
2126  if (restart_offset > 0)
2127  return 1;
2128 
2129  if (!request->range)
2130  return 0;
2131 
2132  if (!flags.binary)
2133  return 0;
2134 
2135  if (theSize <= 0)
2136  return 0;
2137 
2138  int64_t desired_offset = request->range->lowestOffset(theSize);
2139 
2140  if (desired_offset <= 0)
2141  return 0;
2142 
2143  if (desired_offset >= theSize)
2144  return 0;
2145 
2146  restart_offset = desired_offset;
2147  return 1;
2148 }
2149 
2150 static void
2152 {
2153  int code = ftpState->ctrl.replycode;
2154  debugs(9, 3, HERE);
2155  assert(ftpState->restart_offset > 0);
2156 
2157  if (code == 350) {
2158  ftpState->setCurrentOffset(ftpState->restart_offset);
2159  ftpSendRetr(ftpState);
2160  } else if (code > 0) {
2161  debugs(9, 3, HERE << "REST not supported");
2162  ftpState->flags.rest_supported = 0;
2163  ftpSendRetr(ftpState);
2164  } else {
2165  ftpFail(ftpState);
2166  }
2167 }
2168 
2169 static void
2171 {
2172  /* check the server control channel is still available */
2173  if (!ftpState || !ftpState->haveControlChannel("ftpSendList"))
2174  return;
2175 
2176  debugs(9, 3, HERE);
2177 
2178  if (ftpState->filepath) {
2179  snprintf(cbuf, CTRL_BUFLEN, "LIST %s\r\n", ftpState->filepath);
2180  } else {
2181  snprintf(cbuf, CTRL_BUFLEN, "LIST\r\n");
2182  }
2183 
2184  ftpState->writeCommand(cbuf);
2185  ftpState->state = Ftp::Client::SENT_LIST;
2186 }
2187 
2188 static void
2190 {
2191  /* check the server control channel is still available */
2192  if (!ftpState || !ftpState->haveControlChannel("ftpSendNlst"))
2193  return;
2194 
2195  debugs(9, 3, HERE);
2196 
2197  ftpState->flags.tried_nlst = 1;
2198 
2199  if (ftpState->filepath) {
2200  snprintf(cbuf, CTRL_BUFLEN, "NLST %s\r\n", ftpState->filepath);
2201  } else {
2202  snprintf(cbuf, CTRL_BUFLEN, "NLST\r\n");
2203  }
2204 
2205  ftpState->writeCommand(cbuf);
2206  ftpState->state = Ftp::Client::SENT_NLST;
2207 }
2208 
2209 static void
2211 {
2212  int code = ftpState->ctrl.replycode;
2213  debugs(9, 3, HERE);
2214 
2215  if (code == 125 || (code == 150 && Comm::IsConnOpen(ftpState->data.conn))) {
2216  /* Begin data transfer */
2217  debugs(9, 3, HERE << "begin data transfer from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2218  ftpState->switchTimeoutToDataChannel();
2219  ftpState->maybeReadVirginBody();
2220  ftpState->state = Ftp::Client::READING_DATA;
2221  return;
2222  } else if (code == 150) {
2223  /* Accept data channel */
2224  debugs(9, 3, HERE << "accept data channel from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2225  ftpState->listenForDataChannel(ftpState->data.conn);
2226  return;
2227  } else if (!ftpState->flags.tried_nlst && code > 300) {
2228  ftpSendNlst(ftpState);
2229  } else {
2230  ftpFail(ftpState);
2231  return;
2232  }
2233 }
2234 
2235 static void
2237 {
2238  /* check the server control channel is still available */
2239  if (!ftpState || !ftpState->haveControlChannel("ftpSendRetr"))
2240  return;
2241 
2242  debugs(9, 3, HERE);
2243 
2244  assert(ftpState->filepath != NULL);
2245  snprintf(cbuf, CTRL_BUFLEN, "RETR %s\r\n", ftpState->filepath);
2246  ftpState->writeCommand(cbuf);
2247  ftpState->state = Ftp::Client::SENT_RETR;
2248 }
2249 
2250 static void
2252 {
2253  int code = ftpState->ctrl.replycode;
2254  debugs(9, 3, HERE);
2255 
2256  if (code == 125 || (code == 150 && Comm::IsConnOpen(ftpState->data.conn))) {
2257  /* Begin data transfer */
2258  debugs(9, 3, HERE << "begin data transfer from " << ftpState->data.conn->remote << " (" << ftpState->data.conn->local << ")");
2259  ftpState->switchTimeoutToDataChannel();
2260  ftpState->maybeReadVirginBody();
2261  ftpState->state = Ftp::Client::READING_DATA;
2262  } else if (code == 150) {
2263  /* Accept data channel */
2264  ftpState->listenForDataChannel(ftpState->data.conn);
2265  } else if (code >= 300) {
2266  if (!ftpState->flags.try_slash_hack) {
2267  /* Try this as a directory missing trailing slash... */
2268  ftpState->hackShortcut(ftpSendCwd);
2269  } else {
2270  ftpFail(ftpState);
2271  }
2272  } else {
2273  ftpFail(ftpState);
2274  }
2275 }
2276 
2281 void
2283 {
2284  assert(entry);
2285  entry->lock("Ftp::Gateway");
2287  ferr.ftp.listing = &listing;
2288  ferr.ftp.cwd_msg = xstrdup(cwd_message.size()? cwd_message.termedBuf() : "");
2289  ferr.ftp.server_msg = ctrl.message;
2290  ctrl.message = NULL;
2291  entry->replaceHttpReply(ferr.BuildHttpReply());
2292  EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
2293  entry->flush();
2294  entry->unlock("Ftp::Gateway");
2295 }
2296 
2297 static void
2299 {
2300  int code = ftpState->ctrl.replycode;
2301  debugs(9, 3, HERE);
2302 
2303  if (code == 226 || code == 250) {
2304  /* Connection closed; retrieval done. */
2305  if (ftpState->flags.listing) {
2306  ftpState->completedListing();
2307  /* QUIT operation handles sending the reply to client */
2308  }
2309  ftpSendQuit(ftpState);
2310  } else { /* != 226 */
2311  debugs(9, DBG_IMPORTANT, HERE << "Got code " << code << " after reading data");
2312  ftpState->failed(ERR_FTP_FAILURE, 0);
2313  /* failed closes ctrl.conn and frees ftpState */
2314  return;
2315  }
2316 }
2317 
2318 // premature end of the request body
2319 void
2321 {
2323  debugs(9, 3, HERE << "ftpState=" << this);
2324  failed(ERR_READ_ERROR, 0);
2325 }
2326 
2327 static void
2329 {
2330  int code = ftpState->ctrl.replycode;
2331  debugs(9, 3, HERE);
2332 
2333  if (!(code == 226 || code == 250)) {
2334  debugs(9, DBG_IMPORTANT, HERE << "Got code " << code << " after sending data");
2335  ftpState->failed(ERR_FTP_PUT_ERROR, 0);
2336  return;
2337  }
2338 
2339  ftpState->entry->timestampsSet(); /* XXX Is this needed? */
2340  ftpSendReply(ftpState);
2341 }
2342 
2343 static void
2345 {
2346  /* check the server control channel is still available */
2347  if (!ftpState || !ftpState->haveControlChannel("ftpSendQuit"))
2348  return;
2349 
2350  snprintf(cbuf, CTRL_BUFLEN, "QUIT\r\n");
2351  ftpState->writeCommand(cbuf);
2352  ftpState->state = Ftp::Client::SENT_QUIT;
2353 }
2354 
2358 static void
2360 {
2361  ftpState->serverComplete();
2362 }
2363 
2364 static void
2366 {
2367  char *path;
2368  ftpState->flags.try_slash_hack = 1;
2369  /* Free old paths */
2370 
2371  debugs(9, 3, HERE);
2372 
2373  if (ftpState->pathcomps)
2374  wordlistDestroy(&ftpState->pathcomps);
2375 
2376  safe_free(ftpState->filepath);
2377 
2378  /* Build the new path (urlpath begins with /) */
2379  path = SBufToCstring(ftpState->request->url.path());
2380 
2381  rfc1738_unescape(path);
2382 
2383  ftpState->filepath = path;
2384 
2385  /* And off we go */
2386  ftpGetFile(ftpState);
2387 }
2388 
2392 void
2394 {
2395  debugs(9, 3, HERE);
2396 
2397  if (old_request != NULL) {
2398  safe_free(old_request);
2399  safe_free(old_reply);
2400  }
2401 }
2402 
2403 void
2404 Ftp::Gateway::hackShortcut(FTPSM * nextState)
2405 {
2406  /* Clear some unwanted state */
2407  setCurrentOffset(0);
2408  restart_offset = 0;
2409  /* Save old error message & some state info */
2410 
2411  debugs(9, 3, HERE);
2412 
2413  if (old_request == NULL) {
2414  old_request = ctrl.last_command;
2415  ctrl.last_command = NULL;
2416  old_reply = ctrl.last_reply;
2417  ctrl.last_reply = NULL;
2418 
2419  if (pathcomps == NULL && filepath != NULL)
2420  old_filepath = xstrdup(filepath);
2421  }
2422 
2423  /* Jump to the "hack" state */
2424  nextState(this);
2425 }
2426 
2427 static void
2429 {
2430  const bool slashHack = ftpState->request->url.path().caseCmp("/%2f", 4)==0;
2431  int code = ftpState->ctrl.replycode;
2432  err_type error_code = ERR_NONE;
2433 
2434  debugs(9, 6, "state " << ftpState->state <<
2435  " reply code " << code << "flags(" <<
2436  (ftpState->flags.isdir?"IS_DIR,":"") <<
2437  (ftpState->flags.try_slash_hack?"TRY_SLASH_HACK":"") << "), " <<
2438  "mdtm=" << ftpState->mdtm << ", size=" << ftpState->theSize <<
2439  "slashhack=" << (slashHack? "T":"F"));
2440 
2441  /* Try the / hack to support "Netscape" FTP URL's for retreiving files */
2442  if (!ftpState->flags.isdir && /* Not a directory */
2443  !ftpState->flags.try_slash_hack && !slashHack && /* Not doing slash hack */
2444  ftpState->mdtm <= 0 && ftpState->theSize < 0) { /* Not known as a file */
2445 
2446  switch (ftpState->state) {
2447 
2448  case Ftp::Client::SENT_CWD:
2449 
2451  /* Try the / hack */
2452  ftpState->hackShortcut(ftpTrySlashHack);
2453  return;
2454 
2455  default:
2456  break;
2457  }
2458  }
2459 
2460  Http::StatusCode sc = ftpState->failedHttpStatus(error_code);
2461  ErrorState *ftperr = new ErrorState(error_code, sc, ftpState->fwd->request);
2462  ftpState->failed(error_code, code, ftperr);
2463  ftperr->detailError(code);
2464  HttpReply *newrep = ftperr->BuildHttpReply();
2465  delete ftperr;
2466 
2467  ftpState->entry->replaceHttpReply(newrep);
2468  ftpSendQuit(ftpState);
2469 }
2470 
2473 {
2474  if (error == ERR_NONE) {
2475  switch (state) {
2476 
2477  case SENT_USER:
2478 
2479  case SENT_PASS:
2480 
2481  if (ctrl.replycode > 500) {
2482  error = ERR_FTP_FORBIDDEN;
2483  return password_url ? Http::scForbidden : Http::scUnauthorized;
2484  } else if (ctrl.replycode == 421) {
2485  error = ERR_FTP_UNAVAILABLE;
2487  }
2488  break;
2489 
2490  case SENT_CWD:
2491 
2492  case SENT_RETR:
2493  if (ctrl.replycode == 550) {
2494  error = ERR_FTP_NOT_FOUND;
2495  return Http::scNotFound;
2496  }
2497  break;
2498 
2499  default:
2500  break;
2501  }
2502  }
2503  return Ftp::Client::failedHttpStatus(error);
2504 }
2505 
2506 static void
2508 {
2509  int code = ftpState->ctrl.replycode;
2510  Http::StatusCode http_code;
2511  err_type err_code = ERR_NONE;
2512 
2513  debugs(9, 3, HERE << ftpState->entry->url() << ", code " << code);
2514 
2515  if (cbdataReferenceValid(ftpState))
2516  debugs(9, 5, HERE << "ftpState (" << ftpState << ") is valid!");
2517 
2518  if (code == 226 || code == 250) {
2519  err_code = (ftpState->mdtm > 0) ? ERR_FTP_PUT_MODIFIED : ERR_FTP_PUT_CREATED;
2520  http_code = (ftpState->mdtm > 0) ? Http::scAccepted : Http::scCreated;
2521  } else if (code == 227) {
2522  err_code = ERR_FTP_PUT_CREATED;
2523  http_code = Http::scCreated;
2524  } else {
2525  err_code = ERR_FTP_PUT_ERROR;
2526  http_code = Http::scInternalServerError;
2527  }
2528 
2529  ErrorState err(err_code, http_code, ftpState->request.getRaw());
2530 
2531  if (ftpState->old_request)
2532  err.ftp.request = xstrdup(ftpState->old_request);
2533  else
2534  err.ftp.request = xstrdup(ftpState->ctrl.last_command);
2535 
2536  if (ftpState->old_reply)
2537  err.ftp.reply = xstrdup(ftpState->old_reply);
2538  else if (ftpState->ctrl.last_reply)
2539  err.ftp.reply = xstrdup(ftpState->ctrl.last_reply);
2540  else
2541  err.ftp.reply = xstrdup("");
2542 
2543  // TODO: interpret as FTP-specific error code
2544  err.detailError(code);
2545 
2546  ftpState->entry->replaceHttpReply(err.BuildHttpReply());
2547 
2548  ftpSendQuit(ftpState);
2549 }
2550 
2551 void
2553 {
2554  debugs(9, 3, HERE);
2555 
2556  if (flags.http_header_sent)
2557  return;
2558 
2559  HttpReply *reply = new HttpReply;
2560 
2561  flags.http_header_sent = 1;
2562 
2563  assert(entry->isEmpty());
2564 
2565  EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
2566 
2567  entry->buffer(); /* released when done processing current data payload */
2568 
2569  SBuf urlPath = request->url.path();
2570  auto t = urlPath.rfind('/');
2571  SBuf filename = urlPath.substr(t != SBuf::npos ? t : 0);
2572 
2573  const char *mime_type = NULL;
2574  const char *mime_enc = NULL;
2575 
2576  if (flags.isdir) {
2577  mime_type = "text/html";
2578  } else {
2579  switch (typecode) {
2580 
2581  case 'I':
2582  mime_type = "application/octet-stream";
2583  // XXX: performance regression, c_str() may reallocate
2584  mime_enc = mimeGetContentEncoding(filename.c_str());
2585  break;
2586 
2587  case 'A':
2588  mime_type = "text/plain";
2589  break;
2590 
2591  default:
2592  // XXX: performance regression, c_str() may reallocate
2593  mime_type = mimeGetContentType(filename.c_str());
2594  mime_enc = mimeGetContentEncoding(filename.c_str());
2595  break;
2596  }
2597  }
2598 
2599  /* set standard stuff */
2600 
2601  if (0 == getCurrentOffset()) {
2602  /* Full reply */
2603  reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, theSize, mdtm, -2);
2604  } else if (theSize < getCurrentOffset()) {
2605  /*
2606  * DPW 2007-05-04
2607  * offset should not be larger than theSize. We should
2608  * not be seeing this condition any more because we'll only
2609  * send REST if we know the theSize and if it is less than theSize.
2610  */
2611  debugs(0,DBG_CRITICAL,HERE << "Whoops! " <<
2612  " current offset=" << getCurrentOffset() <<
2613  ", but theSize=" << theSize <<
2614  ". assuming full content response");
2615  reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, theSize, mdtm, -2);
2616  } else {
2617  /* Partial reply */
2618  HttpHdrRangeSpec range_spec;
2619  range_spec.offset = getCurrentOffset();
2620  range_spec.length = theSize - getCurrentOffset();
2621  reply->setHeaders(Http::scPartialContent, "Gatewaying", mime_type, theSize - getCurrentOffset(), mdtm, -2);
2622  httpHeaderAddContRange(&reply->header, range_spec, theSize);
2623  }
2624 
2625  /* additional info */
2626  if (mime_enc)
2627  reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc);
2628 
2629  reply->sources |= Http::Message::srcFtp;
2630  setVirginReply(reply);
2631  adaptOrFinalizeReply();
2632 }
2633 
2634 void
2636 {
2638 
2639  StoreEntry *e = entry;
2640 
2641  e->timestampsSet();
2642 
2643  if (flags.authenticated) {
2644  /*
2645  * Authenticated requests can't be cached.
2646  */
2647  e->release();
2648  } else if (!EBIT_TEST(e->flags, RELEASE_REQUEST) && !getCurrentOffset()) {
2649  e->setPublicKey();
2650  } else {
2651  e->release();
2652  }
2653 }
2654 
2655 HttpReply *
2657 {
2659  HttpReply *newrep = err.BuildHttpReply();
2660 #if HAVE_AUTH_MODULE_BASIC
2661  /* add Authenticate header */
2662  // XXX: performance regression. c_str() may reallocate
2663  newrep->header.putAuth("Basic", realm.c_str());
2664 #endif
2665  return newrep;
2666 }
2667 
2668 const SBuf &
2670 {
2671  SBuf newbuf("%2f");
2672 
2673  if (request->url.getScheme() != AnyP::PROTO_FTP) {
2674  static const SBuf nil;
2675  return nil;
2676  }
2677 
2678  if (request->url.path()[0] == '/') {
2679  newbuf.append(request->url.path());
2680  request->url.path(newbuf);
2681  } else if (!request->url.path().startsWith(newbuf)) {
2682  newbuf.append(request->url.path().substr(1));
2683  request->url.path(newbuf);
2684  }
2685 
2686  return request->effectiveRequestUri();
2687 }
2688 
2689 void
2690 Ftp::Gateway::printfReplyBody(const char *fmt, ...)
2691 {
2692  va_list args;
2693  va_start (args, fmt);
2694  static char buf[4096];
2695  buf[0] = '\0';
2696  vsnprintf(buf, 4096, fmt, args);
2697  writeReplyBody(buf, strlen(buf));
2698  va_end(args);
2699 }
2700 
2705 void
2706 Ftp::Gateway::writeReplyBody(const char *dataToWrite, size_t dataLength)
2707 {
2708  debugs(9, 5, HERE << "writing " << dataLength << " bytes to the reply");
2709  addVirginReplyBody(dataToWrite, dataLength);
2710 }
2711 
2718 void
2720 {
2721  if (fwd == NULL || flags.completed_forwarding) {
2722  debugs(9, 3, "avoid double-complete on FD " <<
2723  (ctrl.conn ? ctrl.conn->fd : -1) << ", Data FD " << data.conn->fd <<
2724  ", this " << this << ", fwd " << fwd);
2725  return;
2726  }
2727 
2728  flags.completed_forwarding = true;
2730 }
2731 
2738 bool
2739 Ftp::Gateway::haveControlChannel(const char *caller_name) const
2740 {
2741  if (doneWithServer())
2742  return false;
2743 
2744  /* doneWithServer() only checks BOTH channels are closed. */
2745  if (!Comm::IsConnOpen(ctrl.conn)) {
2746  debugs(9, DBG_IMPORTANT, "WARNING! FTP Server Control channel is closed, but Data channel still active.");
2747  debugs(9, 2, caller_name << ": attempted on a closed FTP channel.");
2748  return false;
2749  }
2750 
2751  return true;
2752 }
2753 
2754 bool
2756 {
2757  // TODO: Can we do what Ftp::Relay::mayReadVirginReplyBody() does instead?
2758  return !doneWithServer();
2759 }
2760 
2762 Ftp::StartGateway(FwdState *const fwdState)
2763 {
2764  return AsyncJob::Start(new Ftp::Gateway(fwdState));
2765 }
2766 
static FTPSM ftpSendMkdir
Definition: FtpGateway.cc:238
String cwd_message
Definition: FtpGateway.cc:117
#define JobCallback(dbgSection, dbgLevel, Dialer, job, method)
Convenience macro to create a Dialer-based job callback.
Definition: AsyncJobCalls.h:68
char method[16]
Definition: tcp-banger2.c:115
#define fd_table
Definition: fde.h:157
CBDATA_CLASS(Gateway)
int regcomp(regex_t *preg, const char *pattern, int cflags)
Definition: GnuRegex.c:4120
static FTPSM ftpSendReply
Definition: FtpGateway.cc:237
#define assert(EX)
Definition: assert.h:17
SBuf & appendf(const char *fmt,...)
Definition: SBuf.cc:251
bool mimeGetDownloadOption(const char *fn)
Definition: mime.cc:211
const char * wordlistAdd(wordlist **list, const char *key)
Definition: wordlist.cc:25
static int sc[16]
Definition: smbdes.c:121
static FTPSM ftpReadStor
Definition: FtpGateway.cc:235
bool handleEpsvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:475
#define xtoupper(x)
Definition: xis.h:18
const char * mimeGetIconURL(const char *fn)
Definition: mime.cc:157
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
Definition: FtpClient.cc:792
String title_url
Definition: FtpGateway.cc:105
static FTPSM ftpSendMdtm
Definition: FtpGateway.cc:207
static FTPSM ftpSendRest
Definition: FtpGateway.cc:229
void writeCommand(const char *buf)
Definition: FtpClient.cc:804
char * filepath
Definition: FtpGateway.cc:112
#define MAX_TOKENS
Definition: FtpGateway.cc:527
static FTPSM ftpSendQuit
Definition: FtpGateway.cc:241
String base_href
Definition: FtpGateway.cc:106
virtual void release(const bool shareable=false)
Definition: store.cc:1250
wordlist * pathcomps
Definition: FtpGateway.cc:111
nfmark_t nfmark
Definition: Connection.h:150
char password[MAX_URL]
Definition: FtpGateway.cc:100
int restartable()
Definition: FtpGateway.cc:2124
static void FreeAddr(struct addrinfo *&ai)
Definition: Address.cc:694
bool mimeGetViewOption(const char *fn)
Definition: mime.cc:218
bool tried_auth_anonymous
auth has tried to use anonymous credentials already.
Definition: FtpGateway.cc:64
virtual bool mayReadVirginReplyBody() const
whether we may receive more virgin response body bytes
Definition: FtpGateway.cc:2755
bool timestampsSet()
Definition: store.cc:1525
Definition: SBuf.h:87
static FTPSM ftpReadTransferDone
Definition: FtpGateway.cc:233
const char * null_string
const char *const crlf
Definition: FtpClient.cc:35
char * reply_hdr
Definition: FtpGateway.cc:102
#define xcalloc
Definition: membanger.c:57
void writeReplyBody(const char *, size_t len)
Definition: FtpGateway.cc:2706
void comm_open_listener(int sock_type, int proto, Comm::ConnectionPointer &conn, const char *note)
Definition: comm.cc:232
char * showname
Definition: FtpGateway.cc:187
static FTPSM ftpSendList
Definition: FtpGateway.cc:226
HttpRequestMethod method
Definition: HttpRequest.h:102
void serverComplete()
Definition: Client.cc:157
void hackShortcut(StateMethod *nextState)
Definition: FtpGateway.cc:2404
struct _request * request(char *urlin)
Definition: tcp-banger2.c:291
void error(char *format,...)
char * dirpath
Definition: FtpGateway.cc:113
int i
Definition: membanger.c:49
StoreEntry * entry
Definition: Client.h:164
#define xstrdup
#define PRId64
Definition: types.h:110
void setPublicKey(const KeyScope keyScope=ksDefault)
Definition: store.cc:623
SBuf & append(const SBuf &S)
Definition: SBuf.cc:207
static HttpReply * ftpAuthRequired(HttpRequest *request, SBuf &realm)
Definition: FtpGateway.cc:2656
FTP client functionality shared among FTP Gateway and Relay clients.
Definition: FtpClient.h:92
void clear()
remove the close handler, leave connection open
Definition: FtpClient.cc:108
char user[MAX_URL]
Definition: FtpGateway.cc:99
virtual void maybeReadVirginBody()
read response data from the network
Definition: FtpClient.cc:887
char * xstrndup(const char *s, size_t n)
Definition: xstring.cc:56
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
Definition: FtpClient.cc:869
CtrlChannel ctrl
FTP control channel state.
Definition: FtpClient.h:124
#define safe_free(x)
Definition: xalloc.h:73
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
char * html_quote(const char *)
Definition: html_quote.c:53
void checkUrlpath()
Definition: FtpGateway.cc:1098
bool pasv_supported
PASV command is allowed.
Definition: FtpGateway.cc:57
Definition: Flag.h:16
#define rfc1738_escape(x)
Definition: rfc1738.h:52
virtual Http::StatusCode failedHttpStatus(err_type &error)
Definition: FtpGateway.cc:2472
const char * mimeGetContentEncoding(const char *fn)
Definition: mime.cc:190
static FTPSM ftpSendType
Definition: FtpGateway.cc:205
static FTPSM ftpReadPass
Definition: FtpGateway.cc:204
void ftpAcceptDataConnection(const CommAcceptCbParams &io)
Definition: FtpGateway.cc:1943
HttpRequest * request
Definition: FwdState.h:138
char * key
Definition: wordlist.h:33
int64_t restart_offset
Definition: FtpGateway.cc:114
AsyncJobPointer StartGateway(FwdState *const fwdState)
A new FTP Gateway job.
Definition: FtpGateway.cc:2762
#define xisspace(x)
Definition: xis.h:17
bool isEmpty() const
Definition: SBuf.h:422
#define DBG_CRITICAL
Definition: Debug.h:44
char * p
Definition: membanger.c:43
void printfReplyBody(const char *fmt,...)
Definition: FtpGateway.cc:2690
int conn
the current server connection FD
Definition: Transport.cc:26
void readStor()
Definition: FtpGateway.cc:2074
void loginFailed(void)
Definition: FtpGateway.cc:1241
void SBufToCstring(char *d, const SBuf &s)
Definition: SBuf.h:743
static FTPSM ftpListDir
Definition: FtpGateway.cc:221
static FTPSM ftpSendUser
Definition: FtpGateway.cc:201
char mimeGetTransferMode(const char *fn)
Definition: mime.cc:204
char * anon_user
Definition: SquidConfig.h:408
void append(char const *buf, int len)
Definition: String.cc:161
FTPSM * FTP_SM_FUNCS[]
Definition: FtpGateway.cc:286
void completedListing(void)
Definition: FtpGateway.cc:2282
#define w_space
void replaceHttpReply(HttpReply *, bool andStartWriting=true)
Definition: store.cc:1854
ftp_port or FTP server
Definition: Message.h:39
void host(const char *src)
Definition: url.cc:47
int64_t getCurrentOffset() const
Definition: FtpGateway.cc:149
static FTPSM ftpReadEPRT
Definition: FtpGateway.cc:214
virtual void haveParsedReplyHeaders()
called when we have final (possibly adapted) reply headers; kids extend
Definition: FtpGateway.cc:2635
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:105
virtual void handleControlReply()
Definition: FtpGateway.cc:1190
MemBuf * htmlifyListEntry(const char *line)
Definition: FtpGateway.cc:767
MemBuf * listing
Definition: errorpage.h:163
static Pointer Start(AsyncJob *job)
starts a freshly created job (i.e., makes the job asynchronous)
Definition: AsyncJob.cc:23
char * rfc1738_do_escape(const char *url, int flags)
Definition: rfc1738.c:56
#define REG_EXTENDED
Definition: GnuRegex.h:226
bool isIPv6() const
Definition: Address.cc:157
static FTPSM ftpReadSize
Definition: FtpGateway.cc:210
bool completed_forwarding
Definition: FtpGateway.cc:83
static void ftpOpenListenSocket(Ftp::Gateway *ftpState, int fallback)
Definition: FtpGateway.cc:1769
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
const char * xstrerr(int error)
Definition: xstrerror.cc:83
virtual ~Gateway()
Definition: FtpGateway.cc:366
DataChannel data
FTP data channel state.
Definition: FtpClient.h:125
char * last_command
Definition: FtpClient.h:65
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:24
void const char HLPCB void * data
Definition: stub_helper.cc:16
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:405
const char * getAuth(Http::HdrType id, const char *auth_scheme) const
Definition: HttpHeader.cc:1318
static PF ftpDataWrite
Definition: FtpGateway.cc:152
StatusCode
Definition: StatusCode.h:20
MemBuf listing
FTP directory listing in HTML format.
Definition: FtpGateway.cc:120
static char cbuf[CTRL_BUFLEN]
Definition: FtpGateway.cc:192
#define REG_ICASE
Definition: GnuRegex.h:230
virtual void start()
called by AsyncStart; do not call directly
Definition: FtpGateway.cc:1168
static FTPSM ftpSendRetr
Definition: FtpGateway.cc:231
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:123
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:151
struct SquidConfig::@114 Ftp
void processHeadResponse()
Definition: FtpGateway.cc:1705
#define DBG_IMPORTANT
Definition: Debug.h:45
char * old_filepath
Definition: FtpGateway.cc:118
static FTPSM ftpReadMkdir
Definition: FtpGateway.cc:239
virtual void haveParsedReplyHeaders()
called when we have final (possibly adapted) reply headers; kids extend
Definition: Client.cc:507
void * memAllocate(mem_type)
Allocate one element from the typed pool.
Definition: old_api.cc:219
FwdState::Pointer fwd
Definition: Client.h:165
GatewayFlags flags
Definition: FtpGateway.cc:122
void httpHeaderAddContRange(HttpHeader *, HttpHdrRangeSpec, int64_t)
SQUIDCEXTERN time_t parse_iso3307_time(const char *buf)
Definition: iso3307.c:25
virtual void handleRequestBodyProducerAborted()=0
Definition: Client.cc:328
static ftpListParts * ftpListParseParts(const char *buf, struct Ftp::GatewayFlags flags)
Definition: FtpGateway.cc:530
static FTPSM ftpSendNlst
Definition: FtpGateway.cc:227
static FTPSM ftpReadMdtm
Definition: FtpGateway.cc:208
wordlist * message
Definition: FtpClient.h:64
static FTPSM ftpReadPasv
Definition: FtpGateway.cc:219
void setCurrentOffset(int64_t offset)
Definition: FtpGateway.cc:148
#define EBIT_CLR(flag, bit)
Definition: defines.h:106
int64_t getInt64(Http::HdrType id) const
Definition: HttpHeader.cc:1173
AnyP::UriScheme const & getScheme() const
Definition: URL.h:58
int xerrno
The last errno to occur. non-zero if flag is Comm::COMM_ERROR.
Definition: CommCalls.h:88
void listenForDataChannel(const Comm::ConnectionPointer &conn)
create a data channel acceptor and start listening.
Definition: FtpGateway.cc:445
const SBuf & effectiveRequestUri() const
RFC 7230 section 5.5 - Effective Request URI.
Definition: HttpRequest.cc:673
const SBuf & UrlWith2f(HttpRequest *)
Definition: FtpGateway.cc:2669
virtual void start()
called by AsyncStart; do not call directly
Definition: FtpClient.cc:199
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
static FTPSM ftpReadEPSV
Definition: FtpGateway.cc:218
bool sendPassive()
Definition: FtpClient.cc:637
void fatal(const char *message)
Definition: fatal.cc:39
static FTPSM ftpSendPass
Definition: FtpGateway.cc:203
static FTPSM ftpReadUser
Definition: FtpGateway.cc:202
void PF(int, void *)
Definition: forward.h:18
static FTPSM ftpReadPORT
Definition: FtpGateway.cc:216
CBDATA_NAMESPACED_CLASS_INIT(Ftp, Gateway)
static FTPSM ftpSendSize
Definition: FtpGateway.cc:209
const char * c_str()
Definition: SBuf.cc:546
uint16_t flags
Definition: Store.h:173
static void ftpListPartsFree(ftpListParts **parts)
Definition: FtpGateway.cc:518
Gateway(FwdState *)
Definition: FtpGateway.cc:330
char * wordlistChopHead(wordlist **wl)
Definition: wordlist.cc:55
virtual void completeForwarding()
Definition: Client.cc:207
bool isIPv4() const
Definition: Address.cc:151
Comm::ConnectionPointer listenConn
Definition: FtpClient.h:46
URL url
the request URI
Definition: HttpRequest.h:103
static FTPSM ftpReadRest
Definition: FtpGateway.cc:230
int64_t currentOffset
Definition: Client.h:160
static FTPSM ftpReadList
Definition: FtpGateway.cc:228
struct ErrorState::@60 ftp
void path(const char *p)
Definition: URL.h:77
SBuf ftpRealm()
Definition: FtpGateway.cc:1285
static FTPSM ftpRestOrList
Definition: FtpGateway.cc:225
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
char * content()
start of the added data
Definition: MemBuf.h:41
const char * url() const
Definition: store.cc:1672
void processReplyBody()
Definition: FtpGateway.cc:987
virtual void handleControlReply()
Definition: FtpClient.cc:403
virtual bool haveControlChannel(const char *caller_name) const
Definition: FtpGateway.cc:2739
unsigned char code
Definition: html_quote.c:20
virtual void dataClosed(const CommCloseCbParams &io)
handler called by Comm when FTP data channel is closed unexpectedly
Definition: FtpGateway.cc:318
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:147
int64_t theSize
Definition: FtpGateway.cc:110
unsigned short port() const
Definition: Address.cc:786
static FTPSM ftpReadRetr
Definition: FtpGateway.cc:232
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
void switchTimeoutToDataChannel()
Definition: FtpClient.cc:1030
Ip::Address local
Definition: Connection.h:135
virtual void dataChannelConnected(const CommConnectCbParams &io)
Definition: FtpGateway.cc:1747
HttpHeader header
Definition: Message.h:74
void close()
planned close: removes the close handler and calls comm_close
Definition: FtpClient.cc:87
virtual void timeout(const CommTimeoutCbParams &io)
read timeout handler
Definition: FtpGateway.cc:482
static FTPSM ftpSendPassive
Definition: FtpGateway.cc:217
static FTPSM ftpReadCwd
Definition: FtpGateway.cc:224
static FTPSM ftpGetFile
Definition: FtpGateway.cc:222
static FTPSM ftpReadWelcome
Definition: FtpGateway.cc:200
char * last_reply
Definition: FtpClient.h:66
char * url
Definition: tcp-banger2.c:114
static int is_month(const char *buf)
Definition: FtpGateway.cc:506
Ip::Address remote
Definition: Connection.h:138
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1063
int regexec(regex_t *preg, const char *string, size_t nmatch, pmatch, int eflags) const
Definition: GnuRegex.c:4191
HttpRequestPointer request
Definition: Client.h:166
#define xmalloc
char * proxy_host
Definition: FtpGateway.cc:115
int64_t size
Definition: FtpGateway.cc:184
void connectDataChannel()
Definition: FtpClient.cc:746
void memFree(void *, int type)
Free a element allocated by memAllocate()
Definition: old_api.cc:227
Comm::Flag flag
comm layer result status.
Definition: CommCalls.h:87
HttpRequestPointer request
Definition: errorpage.h:146
bool epsv_all_sent
EPSV ALL has been used. Must abort on failures.
Definition: FtpGateway.cc:58
struct sockaddr * ai_addr
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
String clean_url
Definition: FtpGateway.cc:104
static const size_type npos
Definition: SBuf.h:93
Definition: MemBuf.h:23
Ftp::StateMethod FTPSM
Definition: FtpGateway.cc:178
size_type find(char c, size_type startPos=0) const
Definition: SBuf.cc:614
bool authenticated
authentication success
Definition: FtpGateway.cc:63
void rfc1738_unescape(char *url)
Definition: rfc1738.c:146
static FTPSM ftpSendPORT
Definition: FtpGateway.cc:215
static FTPSM ftpWriteTransferDone
Definition: FtpGateway.cc:236
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:412
virtual Http::StatusCode failedHttpStatus(err_type &error)
Definition: FtpClient.cc:295
size_t list_width
Definition: FtpGateway.cc:116
HttpReply * BuildHttpReply(void)
Definition: errorpage.cc:1104
void detailError(int dCode)
set error type-specific detail code
Definition: errorpage.h:96
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:23
static void ftpTrySlashHack(Ftp::Gateway *ftpState)
Definition: FtpGateway.cc:2365
#define REG_NOSUB
Definition: GnuRegex.h:239
size_type rfind(char c, size_type endPos=npos) const
Definition: SBuf.cc:722
#define MAX_URL
Definition: defines.h:118
void reset(char const *str)
Definition: String.cc:151
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
#define rfc1738_escape_part(x)
Definition: rfc1738.h:55
static FTPSM ftpReadQuit
Definition: FtpGateway.cc:242
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:810
int64_t strtoll(const char *nptr, char **endptr, int base)
Definition: strtoll.c:81
struct tok tokens[]
Definition: parse.c:168
bool tried_auth_nopass
auth tried username with no password already.
Definition: FtpGateway.cc:65
char * old_reply
Definition: FtpClient.h:160
static FTPSM ftpFail
Definition: FtpGateway.cc:240
const char * mimeGetContentType(const char *fn)
Definition: mime.cc:176
const char * rawContent() const
Definition: SBuf.cc:539
void parseListing()
Definition: FtpGateway.cc:904
virtual void completeForwarding()
Definition: FtpGateway.cc:2719
static FTPSM ftpSendStor
Definition: FtpGateway.cc:234
#define xfree
static FTPSM ftpSendCwd
Definition: FtpGateway.cc:223
MemBlob::size_type size_type
Definition: SBuf.h:90
static FTPSM ftpReadType
Definition: FtpGateway.cc:206
void putAuth(const char *auth_scheme, const char *realm)
Definition: HttpHeader.cc:1072
Comm::ConnectionPointer conn
channel descriptor
Definition: FtpClient.h:39
int checkAuth(const HttpHeader *req_hdr)
Definition: FtpGateway.cc:1054
#define EBIT_TEST(flag, bit)
Definition: defines.h:107
SBuf substr(size_type pos, size_type n=npos) const
Definition: SBuf.cc:606
C * getRaw() const
Definition: RefCount.h:74
size_type copy(char *dest, size_type n) const
Definition: SBuf.cc:530
void loginParser(const SBuf &login, bool escaped)
Definition: FtpGateway.cc:399
static const char * Month[]
Definition: FtpGateway.cc:500
class SquidConfig Config
Definition: SquidConfig.cc:12
#define COMM_REUSEADDR
Definition: Connection.h:46
#define NULL
Definition: types.h:166
int reply_hdr_state
Definition: FtpGateway.cc:103
uint32_t sources
The message sources.
Definition: Message.h:99
const char * text
Definition: errorpage.cc:80
void buildTitleUrl()
Definition: FtpGateway.cc:1135
int size
Definition: ModDevPoll.cc:77
#define CTRL_BUFLEN
Definition: FtpGateway.cc:191
void getAddrInfo(struct addrinfo *&ai, int force=AF_UNSPEC) const
Definition: Address.cc:607
err_type
Definition: err_type.h:12
void( StateMethod)(Ftp::Gateway *)
Definition: FtpGateway.cc:87
void appendSuccessHeader()
Definition: FtpGateway.cc:2552
void initReadBuf()
Definition: FtpClient.cc:205
wordlist * next
Definition: wordlist.h:34
static FTPSM ftpTraverseDirectory
Definition: FtpGateway.cc:220
bool handlePasvReply(Ip::Address &remoteAddr)
Definition: FtpClient.cc:439
virtual void handleRequestBodyProducerAborted()
Definition: FtpGateway.cc:2320
char * old_request
Definition: FtpClient.h:159

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors