]> git.ipfire.org Git - thirdparty/squid.git/blob - src/clients/FtpRelay.cc
merge from trunk r14597
[thirdparty/squid.git] / src / clients / FtpRelay.cc
1 /*
2 * Copyright (C) 1996-2016 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 "anyp/PortCfg.h"
13 #include "client_side.h"
14 #include "clients/forward.h"
15 #include "clients/FtpClient.h"
16 #include "ftp/Elements.h"
17 #include "ftp/Parsing.h"
18 #include "http/Stream.h"
19 #include "HttpHdrCc.h"
20 #include "HttpRequest.h"
21 #include "sbuf/SBuf.h"
22 #include "servers/FtpServer.h"
23 #include "SquidTime.h"
24 #include "Store.h"
25 #include "wordlist.h"
26
27 namespace Ftp
28 {
29
30 /// An FTP client receiving native FTP commands from our FTP server
31 /// (Ftp::Server), forwarding them to the next FTP hop,
32 /// and then relaying FTP replies back to our FTP server.
33 class Relay: public Ftp::Client
34 {
35 CBDATA_CLASS(Relay);
36
37 public:
38 explicit Relay(FwdState *const fwdState);
39 virtual ~Relay();
40
41 protected:
42 const Ftp::MasterState &master() const;
43 Ftp::MasterState &updateMaster();
44 Ftp::ServerState serverState() const { return master().serverState; }
45 void serverState(const Ftp::ServerState newState);
46
47 /* Ftp::Client API */
48 virtual void failed(err_type error = ERR_NONE, int xerrno = 0, ErrorState *ftperr = nullptr);
49 virtual void dataChannelConnected(const CommConnectCbParams &io);
50
51 /* Client API */
52 virtual void serverComplete();
53 virtual void handleControlReply();
54 virtual void processReplyBody();
55 virtual void handleRequestBodyProducerAborted();
56 virtual bool mayReadVirginReplyBody() const;
57 virtual void completeForwarding();
58 virtual bool abortOnData(const char *reason);
59
60 /* AsyncJob API */
61 virtual void start();
62 virtual void swanSong();
63
64 void forwardReply();
65 void forwardError(err_type error = ERR_NONE, int xerrno = 0);
66 void failedErrorMessage(err_type error, int xerrno);
67 HttpReply *createHttpReply(const Http::StatusCode httpStatus, const int64_t clen = 0);
68 void handleDataRequest();
69 void startDataDownload();
70 void startDataUpload();
71 bool startDirTracking();
72 void stopDirTracking();
73 bool weAreTrackingDir() const {return savedReply.message != NULL;}
74
75 typedef void (Relay::*PreliminaryCb)();
76 void forwardPreliminaryReply(const PreliminaryCb cb);
77 void proceedAfterPreliminaryReply();
78 PreliminaryCb thePreliminaryCb;
79
80 typedef void (Relay::*SM_FUNC)();
81 static const SM_FUNC SM_FUNCS[];
82 void readGreeting();
83 void sendCommand();
84 void readReply();
85 void readFeatReply();
86 void readPasvReply();
87 void readDataReply();
88 void readTransferDoneReply();
89 void readEpsvReply();
90 void readCwdOrCdupReply();
91 void readUserOrPassReply();
92
93 void scheduleReadControlReply();
94
95 /// Inform Ftp::Server that we are done if originWaitInProgress
96 void stopOriginWait(int code);
97
98 static void abort(void *d); // TODO: Capitalize this and FwdState::abort().
99
100 bool forwardingCompleted; ///< completeForwarding() has been called
101
102 /// whether we are between Ftp::Server::startWaitingForOrigin() and
103 /// Ftp::Server::stopWaitingForOrigin() calls
104 bool originWaitInProgress;
105
106 struct {
107 wordlist *message; ///< reply message, one wordlist entry per message line
108 char *lastCommand; ///< the command caused the reply
109 char *lastReply; ///< last line of reply: reply status plus message
110 int replyCode; ///< the reply status
111 } savedReply; ///< set and delayed while we are tracking using PWD
112 };
113
114 } // namespace Ftp
115
116 CBDATA_NAMESPACED_CLASS_INIT(Ftp, Relay);
117
118 const Ftp::Relay::SM_FUNC Ftp::Relay::SM_FUNCS[] = {
119 &Ftp::Relay::readGreeting, // BEGIN
120 &Ftp::Relay::readUserOrPassReply, // SENT_USER
121 &Ftp::Relay::readUserOrPassReply, // SENT_PASS
122 NULL,/* &Ftp::Relay::readReply */ // SENT_TYPE
123 NULL,/* &Ftp::Relay::readReply */ // SENT_MDTM
124 NULL,/* &Ftp::Relay::readReply */ // SENT_SIZE
125 NULL, // SENT_EPRT
126 NULL, // SENT_PORT
127 &Ftp::Relay::readEpsvReply, // SENT_EPSV_ALL
128 &Ftp::Relay::readEpsvReply, // SENT_EPSV_1
129 &Ftp::Relay::readEpsvReply, // SENT_EPSV_2
130 &Ftp::Relay::readPasvReply, // SENT_PASV
131 &Ftp::Relay::readCwdOrCdupReply, // SENT_CWD
132 NULL,/* &Ftp::Relay::readDataReply, */ // SENT_LIST
133 NULL,/* &Ftp::Relay::readDataReply, */ // SENT_NLST
134 NULL,/* &Ftp::Relay::readReply */ // SENT_REST
135 NULL,/* &Ftp::Relay::readDataReply */ // SENT_RETR
136 NULL,/* &Ftp::Relay::readReply */ // SENT_STOR
137 NULL,/* &Ftp::Relay::readReply */ // SENT_QUIT
138 &Ftp::Relay::readTransferDoneReply, // READING_DATA
139 &Ftp::Relay::readReply, // WRITING_DATA
140 NULL,/* &Ftp::Relay::readReply */ // SENT_MKDIR
141 &Ftp::Relay::readFeatReply, // SENT_FEAT
142 NULL,/* &Ftp::Relay::readPwdReply */ // SENT_PWD
143 &Ftp::Relay::readCwdOrCdupReply, // SENT_CDUP
144 &Ftp::Relay::readDataReply,// SENT_DATA_REQUEST
145 &Ftp::Relay::readReply, // SENT_COMMAND
146 NULL
147 };
148
149 Ftp::Relay::Relay(FwdState *const fwdState):
150 AsyncJob("Ftp::Relay"),
151 Ftp::Client(fwdState),
152 thePreliminaryCb(NULL),
153 forwardingCompleted(false),
154 originWaitInProgress(false)
155 {
156 savedReply.message = NULL;
157 savedReply.lastCommand = NULL;
158 savedReply.lastReply = NULL;
159 savedReply.replyCode = 0;
160
161 // Nothing we can do at request creation time can mark the response as
162 // uncachable, unfortunately. This prevents "found KEY_PRIVATE" WARNINGs.
163 entry->releaseRequest();
164 // TODO: Convert registerAbort() to use AsyncCall
165 entry->registerAbort(Ftp::Relay::abort, this);
166 }
167
168 Ftp::Relay::~Relay()
169 {
170 closeServer(); // TODO: move to clients/Client.cc?
171 if (savedReply.message)
172 wordlistDestroy(&savedReply.message);
173
174 xfree(savedReply.lastCommand);
175 xfree(savedReply.lastReply);
176 }
177
178 void
179 Ftp::Relay::start()
180 {
181 if (!master().clientReadGreeting)
182 Ftp::Client::start();
183 else if (serverState() == fssHandleDataRequest ||
184 serverState() == fssHandleUploadRequest)
185 handleDataRequest();
186 else
187 sendCommand();
188 }
189
190 void
191 Ftp::Relay::swanSong()
192 {
193 stopOriginWait(0);
194 Ftp::Client::swanSong();
195 }
196
197 /// Keep control connection for future requests, after we are done with it.
198 /// Similar to COMPLETE_PERSISTENT_MSG handling in http.cc.
199 void
200 Ftp::Relay::serverComplete()
201 {
202 stopOriginWait(ctrl.replycode);
203
204 CbcPointer<ConnStateData> &mgr = fwd->request->clientConnectionManager;
205 if (mgr.valid()) {
206 if (Comm::IsConnOpen(ctrl.conn)) {
207 debugs(9, 7, "completing FTP server " << ctrl.conn <<
208 " after " << ctrl.replycode);
209 fwd->unregister(ctrl.conn);
210 if (ctrl.replycode == 221) { // Server sends FTP 221 before closing
211 mgr->unpinConnection(false);
212 ctrl.close();
213 } else {
214 mgr->pinConnection(ctrl.conn, fwd->request,
215 ctrl.conn->getPeer(),
216 fwd->request->flags.connectionAuth);
217 ctrl.forget();
218 }
219 }
220 }
221 Ftp::Client::serverComplete();
222 }
223
224 /// Safely returns the master state,
225 /// with safety checks in case the Ftp::Server side of the master xact is gone.
226 Ftp::MasterState &
227 Ftp::Relay::updateMaster()
228 {
229 CbcPointer<ConnStateData> &mgr = fwd->request->clientConnectionManager;
230 if (mgr.valid()) {
231 if (Ftp::Server *srv = dynamic_cast<Ftp::Server*>(mgr.get()))
232 return *srv->master;
233 }
234 // this code will not be necessary once the master is inside MasterXaction
235 debugs(9, 3, "our server side is gone: " << mgr);
236 static Ftp::MasterState Master;
237 Master = Ftp::MasterState();
238 return Master;
239 }
240
241 /// A const variant of updateMaster().
242 const Ftp::MasterState &
243 Ftp::Relay::master() const
244 {
245 return const_cast<Ftp::Relay*>(this)->updateMaster(); // avoid code dupe
246 }
247
248 /// Changes server state and debugs about that important event.
249 void
250 Ftp::Relay::serverState(const Ftp::ServerState newState)
251 {
252 Ftp::ServerState &cltState = updateMaster().serverState;
253 debugs(9, 3, "client state was " << cltState << " now: " << newState);
254 cltState = newState;
255 }
256
257 /**
258 * Ensure we do not double-complete on the forward entry.
259 * We complete forwarding when the response adaptation is over
260 * (but we may still be waiting for 226 from the FTP server) and
261 * also when we get that 226 from the server (and adaptation is done).
262 *
263 \todo Rewrite FwdState to ignore double completion?
264 */
265 void
266 Ftp::Relay::completeForwarding()
267 {
268 debugs(9, 5, forwardingCompleted);
269 if (forwardingCompleted)
270 return;
271 forwardingCompleted = true;
272 Ftp::Client::completeForwarding();
273 }
274
275 void
276 Ftp::Relay::failed(err_type error, int xerrno, ErrorState *ftpErr)
277 {
278 if (!doneWithServer())
279 serverState(fssError);
280
281 // TODO: we need to customize ErrorState instead
282 if (entry->isEmpty())
283 failedErrorMessage(error, xerrno); // as a reply
284
285 Ftp::Client::failed(error, xerrno, ftpErr);
286 }
287
288 void
289 Ftp::Relay::failedErrorMessage(err_type error, int xerrno)
290 {
291 const Http::StatusCode httpStatus = failedHttpStatus(error);
292 HttpReply *const reply = createHttpReply(httpStatus);
293 entry->replaceHttpReply(reply);
294 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
295 fwd->request->detailError(error, xerrno);
296 }
297
298 void
299 Ftp::Relay::processReplyBody()
300 {
301 debugs(9, 3, status());
302
303 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
304 /*
305 * probably was aborted because content length exceeds one
306 * of the maximum size limits.
307 */
308 abortOnData("entry aborted after calling appendSuccessHeader()");
309 return;
310 }
311
312 if (master().userDataDone) {
313 // Squid-to-client data transfer done. Abort data transfer on our
314 // side to allow new commands from ftp client
315 abortOnData("Squid-to-client data connection is closed");
316 return;
317 }
318
319 #if USE_ADAPTATION
320
321 if (adaptationAccessCheckPending) {
322 debugs(9, 3, "returning due to adaptationAccessCheckPending");
323 return;
324 }
325
326 #endif
327
328 if (data.readBuf != NULL && data.readBuf->hasContent()) {
329 const mb_size_t csize = data.readBuf->contentSize();
330 debugs(9, 5, "writing " << csize << " bytes to the reply");
331 addVirginReplyBody(data.readBuf->content(), csize);
332 data.readBuf->consume(csize);
333 }
334
335 entry->flush();
336
337 maybeReadVirginBody();
338 }
339
340 void
341 Ftp::Relay::handleControlReply()
342 {
343 if (!request->clientConnectionManager.valid()) {
344 debugs(9, 5, "client connection gone");
345 closeServer();
346 return;
347 }
348
349 Ftp::Client::handleControlReply();
350 if (ctrl.message == NULL)
351 return; // didn't get complete reply yet
352
353 assert(state < END);
354 assert(this->SM_FUNCS[state] != NULL);
355 (this->*SM_FUNCS[state])();
356 }
357
358 void
359 Ftp::Relay::handleRequestBodyProducerAborted()
360 {
361 ::Client::handleRequestBodyProducerAborted();
362
363 failed(ERR_READ_ERROR);
364 }
365
366 bool
367 Ftp::Relay::mayReadVirginReplyBody() const
368 {
369 // TODO: move this method to the regular FTP server?
370 return Comm::IsConnOpen(data.conn);
371 }
372
373 void
374 Ftp::Relay::forwardReply()
375 {
376 assert(entry->isEmpty());
377 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
378
379 HttpReply *const reply = createHttpReply(Http::scNoContent);
380 reply->sources |= HttpMsg::srcFtp;
381
382 setVirginReply(reply);
383 adaptOrFinalizeReply();
384
385 serverComplete();
386 }
387
388 void
389 Ftp::Relay::forwardPreliminaryReply(const PreliminaryCb cb)
390 {
391 debugs(9, 5, "forwarding preliminary reply to client");
392
393 // we must prevent concurrent ConnStateData::sendControlMsg() calls
394 Must(thePreliminaryCb == NULL);
395 thePreliminaryCb = cb;
396
397 const HttpReply::Pointer reply = createHttpReply(Http::scContinue);
398
399 // the Sink will use this to call us back after writing 1xx to the client
400 typedef NullaryMemFunT<Relay> CbDialer;
401 const AsyncCall::Pointer call = JobCallback(11, 3, CbDialer, this,
402 Ftp::Relay::proceedAfterPreliminaryReply);
403
404 CallJobHere1(9, 4, request->clientConnectionManager, ConnStateData,
405 ConnStateData::sendControlMsg, HttpControlMsg(reply, call));
406 }
407
408 void
409 Ftp::Relay::proceedAfterPreliminaryReply()
410 {
411 debugs(9, 5, "proceeding after preliminary reply to client");
412
413 Must(thePreliminaryCb != NULL);
414 const PreliminaryCb cb = thePreliminaryCb;
415 thePreliminaryCb = NULL;
416 (this->*cb)();
417 }
418
419 void
420 Ftp::Relay::forwardError(err_type error, int xerrno)
421 {
422 failed(error, xerrno);
423 }
424
425 HttpReply *
426 Ftp::Relay::createHttpReply(const Http::StatusCode httpStatus, const int64_t clen)
427 {
428 HttpReply *const reply = Ftp::HttpReplyWrapper(ctrl.replycode, ctrl.last_reply, httpStatus, clen);
429 if (ctrl.message) {
430 for (wordlist *W = ctrl.message; W && W->next; W = W->next)
431 reply->header.putStr(Http::HdrType::FTP_PRE, httpHeaderQuoteString(W->key).c_str());
432 // no hdrCacheInit() is needed for after Http::HdrType::FTP_PRE addition
433 }
434 return reply;
435 }
436
437 void
438 Ftp::Relay::handleDataRequest()
439 {
440 data.addr(master().clientDataAddr);
441 connectDataChannel();
442 }
443
444 void
445 Ftp::Relay::startDataDownload()
446 {
447 assert(Comm::IsConnOpen(data.conn));
448
449 debugs(9, 3, "begin data transfer from " << data.conn->remote <<
450 " (" << data.conn->local << ")");
451
452 HttpReply *const reply = createHttpReply(Http::scOkay, -1);
453 reply->sources |= HttpMsg::srcFtp;
454
455 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
456 setVirginReply(reply);
457 adaptOrFinalizeReply();
458
459 maybeReadVirginBody();
460 state = READING_DATA;
461 }
462
463 void
464 Ftp::Relay::startDataUpload()
465 {
466 assert(Comm::IsConnOpen(data.conn));
467
468 debugs(9, 3, "begin data transfer to " << data.conn->remote <<
469 " (" << data.conn->local << ")");
470
471 if (!startRequestBodyFlow()) { // register to receive body data
472 failed();
473 return;
474 }
475
476 state = WRITING_DATA;
477 }
478
479 void
480 Ftp::Relay::readGreeting()
481 {
482 assert(!master().clientReadGreeting);
483
484 switch (ctrl.replycode) {
485 case 220:
486 updateMaster().clientReadGreeting = true;
487 if (serverState() == fssBegin)
488 serverState(fssConnected);
489
490 // Do not forward server greeting to the user because our FTP Server
491 // has greeted the user already. Also, an original origin greeting may
492 // confuse a user that has changed the origin mid-air.
493
494 start();
495 break;
496 case 120:
497 if (NULL != ctrl.message)
498 debugs(9, DBG_IMPORTANT, "FTP server is busy: " << ctrl.message->key);
499 forwardPreliminaryReply(&Ftp::Relay::scheduleReadControlReply);
500 break;
501 default:
502 failed();
503 break;
504 }
505 }
506
507 void
508 Ftp::Relay::sendCommand()
509 {
510 if (!fwd->request->header.has(Http::HdrType::FTP_COMMAND)) {
511 abortAll("Internal error: FTP relay request with no command");
512 return;
513 }
514
515 HttpHeader &header = fwd->request->header;
516 assert(header.has(Http::HdrType::FTP_COMMAND));
517 const String &cmd = header.findEntry(Http::HdrType::FTP_COMMAND)->value;
518 assert(header.has(Http::HdrType::FTP_ARGUMENTS));
519 const String &params = header.findEntry(Http::HdrType::FTP_ARGUMENTS)->value;
520
521 if (params.size() > 0)
522 debugs(9, 5, "command: " << cmd << ", parameters: " << params);
523 else
524 debugs(9, 5, "command: " << cmd << ", no parameters");
525
526 if (serverState() == fssHandlePasv ||
527 serverState() == fssHandleEpsv ||
528 serverState() == fssHandleEprt ||
529 serverState() == fssHandlePort) {
530 sendPassive();
531 return;
532 }
533
534 SBuf buf;
535 if (params.size() > 0)
536 buf.Printf("%s %s%s", cmd.termedBuf(), params.termedBuf(), Ftp::crlf);
537 else
538 buf.Printf("%s%s", cmd.termedBuf(), Ftp::crlf);
539
540 writeCommand(buf.c_str());
541
542 state =
543 serverState() == fssHandleCdup ? SENT_CDUP :
544 serverState() == fssHandleCwd ? SENT_CWD :
545 serverState() == fssHandleFeat ? SENT_FEAT :
546 serverState() == fssHandleDataRequest ? SENT_DATA_REQUEST :
547 serverState() == fssHandleUploadRequest ? SENT_DATA_REQUEST :
548 serverState() == fssConnected ? SENT_USER :
549 serverState() == fssHandlePass ? SENT_PASS :
550 SENT_COMMAND;
551
552 if (state == SENT_DATA_REQUEST) {
553 CbcPointer<ConnStateData> &mgr = fwd->request->clientConnectionManager;
554 if (mgr.valid()) {
555 if (Ftp::Server *srv = dynamic_cast<Ftp::Server*>(mgr.get())) {
556 typedef NullaryMemFunT<Ftp::Server> CbDialer;
557 AsyncCall::Pointer call = JobCallback(11, 3, CbDialer, srv,
558 Ftp::Server::startWaitingForOrigin);
559 ScheduleCallHere(call);
560 originWaitInProgress = true;
561 }
562 }
563 }
564 }
565
566 void
567 Ftp::Relay::readReply()
568 {
569 assert(serverState() == fssConnected ||
570 serverState() == fssHandleUploadRequest);
571
572 if (100 <= ctrl.replycode && ctrl.replycode < 200)
573 forwardPreliminaryReply(&Ftp::Relay::scheduleReadControlReply);
574 else
575 forwardReply();
576 }
577
578 void
579 Ftp::Relay::readFeatReply()
580 {
581 assert(serverState() == fssHandleFeat);
582
583 if (100 <= ctrl.replycode && ctrl.replycode < 200)
584 return; // ignore preliminary replies
585
586 forwardReply();
587 }
588
589 void
590 Ftp::Relay::readPasvReply()
591 {
592 assert(serverState() == fssHandlePasv || serverState() == fssHandleEpsv || serverState() == fssHandlePort || serverState() == fssHandleEprt);
593
594 if (100 <= ctrl.replycode && ctrl.replycode < 200)
595 return; // ignore preliminary replies
596
597 if (handlePasvReply(updateMaster().clientDataAddr))
598 forwardReply();
599 else
600 forwardError();
601 }
602
603 void
604 Ftp::Relay::readEpsvReply()
605 {
606 if (100 <= ctrl.replycode && ctrl.replycode < 200)
607 return; // ignore preliminary replies
608
609 if (handleEpsvReply(updateMaster().clientDataAddr)) {
610 if (ctrl.message == NULL)
611 return; // didn't get complete reply yet
612
613 forwardReply();
614 } else
615 forwardError();
616 }
617
618 void
619 Ftp::Relay::readDataReply()
620 {
621 assert(serverState() == fssHandleDataRequest ||
622 serverState() == fssHandleUploadRequest);
623
624 if (ctrl.replycode == 125 || ctrl.replycode == 150) {
625 if (serverState() == fssHandleDataRequest)
626 forwardPreliminaryReply(&Ftp::Relay::startDataDownload);
627 else if (fwd->request->forcedBodyContinuation /*&& serverState() == fssHandleUploadRequest*/)
628 startDataUpload();
629 else // serverState() == fssHandleUploadRequest
630 forwardPreliminaryReply(&Ftp::Relay::startDataUpload);
631 } else
632 forwardReply();
633 }
634
635 bool
636 Ftp::Relay::startDirTracking()
637 {
638 if (!fwd->request->clientConnectionManager->port->ftp_track_dirs)
639 return false;
640
641 debugs(9, 5, "start directory tracking");
642 savedReply.message = ctrl.message;
643 savedReply.lastCommand = ctrl.last_command;
644 savedReply.lastReply = ctrl.last_reply;
645 savedReply.replyCode = ctrl.replycode;
646
647 ctrl.last_command = NULL;
648 ctrl.last_reply = NULL;
649 ctrl.message = NULL;
650 ctrl.offset = 0;
651 writeCommand("PWD\r\n");
652 return true;
653 }
654
655 void
656 Ftp::Relay::stopDirTracking()
657 {
658 debugs(9, 5, "got code from pwd: " << ctrl.replycode << ", msg: " << ctrl.last_reply);
659
660 if (ctrl.replycode == 257)
661 updateMaster().workingDir = Ftp::UnescapeDoubleQuoted(ctrl.last_reply);
662
663 wordlistDestroy(&ctrl.message);
664 safe_free(ctrl.last_command);
665 safe_free(ctrl.last_reply);
666
667 ctrl.message = savedReply.message;
668 ctrl.last_command = savedReply.lastCommand;
669 ctrl.last_reply = savedReply.lastReply;
670 ctrl.replycode = savedReply.replyCode;
671
672 savedReply.message = NULL;
673 savedReply.lastReply = NULL;
674 savedReply.lastCommand = NULL;
675 }
676
677 void
678 Ftp::Relay::readCwdOrCdupReply()
679 {
680 assert(serverState() == fssHandleCwd ||
681 serverState() == fssHandleCdup);
682
683 debugs(9, 5, "got code " << ctrl.replycode << ", msg: " << ctrl.last_reply);
684
685 if (100 <= ctrl.replycode && ctrl.replycode < 200)
686 return;
687
688 if (weAreTrackingDir()) { // we are tracking
689 stopDirTracking(); // and forward the delayed response below
690 } else if (startDirTracking())
691 return;
692
693 forwardReply();
694 }
695
696 void
697 Ftp::Relay::readUserOrPassReply()
698 {
699 if (100 <= ctrl.replycode && ctrl.replycode < 200)
700 return; //Just ignore
701
702 if (weAreTrackingDir()) { // we are tracking
703 stopDirTracking(); // and forward the delayed response below
704 } else if (ctrl.replycode == 230) { // successful login
705 if (startDirTracking())
706 return;
707 }
708
709 forwardReply();
710 }
711
712 void
713 Ftp::Relay::readTransferDoneReply()
714 {
715 debugs(9, 3, status());
716
717 if (ctrl.replycode != 226 && ctrl.replycode != 250) {
718 debugs(9, DBG_IMPORTANT, "got FTP code " << ctrl.replycode <<
719 " after reading response data");
720 }
721
722 debugs(9, 2, "Complete data downloading");
723
724 serverComplete();
725 }
726
727 void
728 Ftp::Relay::dataChannelConnected(const CommConnectCbParams &io)
729 {
730 debugs(9, 3, status());
731 data.opener = NULL;
732
733 if (io.flag != Comm::OK) {
734 debugs(9, 2, "failed to connect FTP server data channel");
735 forwardError(ERR_CONNECT_FAIL, io.xerrno);
736 return;
737 }
738
739 debugs(9, 2, "connected FTP server data channel: " << io.conn);
740
741 data.opened(io.conn, dataCloser());
742
743 sendCommand();
744 }
745
746 void
747 Ftp::Relay::scheduleReadControlReply()
748 {
749 Ftp::Client::scheduleReadControlReply(0);
750 }
751
752 bool
753 Ftp::Relay::abortOnData(const char *reason)
754 {
755 debugs(9, 3, "aborting transaction for " << reason <<
756 "; FD " << (ctrl.conn != NULL ? ctrl.conn->fd : -1) << ", Data FD " << (data.conn != NULL ? data.conn->fd : -1) << ", this " << this);
757 // this method is only called to handle data connection problems
758 // the control connection should keep going
759
760 #if USE_ADAPTATION
761 if (adaptedBodySource != NULL)
762 stopConsumingFrom(adaptedBodySource);
763 #endif
764
765 if (Comm::IsConnOpen(data.conn))
766 dataComplete();
767
768 return !Comm::IsConnOpen(ctrl.conn);
769 }
770
771 void
772 Ftp::Relay::stopOriginWait(int code)
773 {
774 if (originWaitInProgress) {
775 CbcPointer<ConnStateData> &mgr = fwd->request->clientConnectionManager;
776 if (mgr.valid()) {
777 if (Ftp::Server *srv = dynamic_cast<Ftp::Server*>(mgr.get())) {
778 typedef UnaryMemFunT<Ftp::Server, int> CbDialer;
779 AsyncCall::Pointer call = asyncCall(11, 3, "Ftp::Server::stopWaitingForOrigin",
780 CbDialer(srv, &Ftp::Server::stopWaitingForOrigin, code));
781 ScheduleCallHere(call);
782 }
783 }
784 originWaitInProgress = false;
785 }
786 }
787
788 void
789 Ftp::Relay::abort(void *d)
790 {
791 Ftp::Relay *ftpClient = (Ftp::Relay *)d;
792 debugs(9, 2, "Client Data connection closed!");
793 if (!cbdataReferenceValid(ftpClient))
794 return;
795 if (Comm::IsConnOpen(ftpClient->data.conn))
796 ftpClient->dataComplete();
797 }
798
799 AsyncJob::Pointer
800 Ftp::StartRelay(FwdState *const fwdState)
801 {
802 return AsyncJob::Start(new Ftp::Relay(fwdState));
803 }
804