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