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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors