]> git.ipfire.org Git - thirdparty/squid.git/blame - src/clients/FtpRelay.cc
Boilerplate: update copyright blurbs on src/
[thirdparty/squid.git] / src / clients / FtpRelay.cc
CommitLineData
434a79b0 1/*
bbc27441 2 * Copyright (C) 1996-2014 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"
434a79b0 21#include "Server.h"
27c841f6 22#include "servers/FtpServer.h"
434a79b0
DK
23#include "SquidTime.h"
24#include "Store.h"
434a79b0
DK
25#include "wordlist.h"
26
27c841f6
AR
27namespace Ftp
28{
434a79b0 29
e7ce227f 30/// An FTP client receiving native FTP commands from our FTP server
5517260a
AR
31/// (Ftp::Server), forwarding them to the next FTP hop,
32/// and then relaying FTP replies back to our FTP server.
33class Relay: public Ftp::Client
434a79b0
DK
34{
35public:
5517260a
AR
36 explicit Relay(FwdState *const fwdState);
37 virtual ~Relay();
434a79b0
DK
38
39protected:
92ae4c86
AR
40 const Ftp::MasterState &master() const;
41 Ftp::MasterState &updateMaster();
e7ce227f
AR
42 Ftp::ServerState serverState() const { return master().serverState; }
43 void serverState(const Ftp::ServerState newState);
92ae4c86 44
5517260a 45 /* Ftp::Client API */
434a79b0 46 virtual void failed(err_type error = ERR_NONE, int xerrno = 0);
43446566 47 virtual void dataChannelConnected(const CommConnectCbParams &io);
5517260a
AR
48
49 /* ServerStateData API */
50 virtual void serverComplete();
434a79b0 51 virtual void handleControlReply();
5517260a 52 virtual void processReplyBody();
434a79b0 53 virtual void handleRequestBodyProducerAborted();
719c885c
AR
54 virtual bool mayReadVirginReplyBody() const;
55 virtual void completeForwarding();
5517260a
AR
56
57 /* AsyncJob API */
58 virtual void start();
59
434a79b0
DK
60 void forwardReply();
61 void forwardError(err_type error = ERR_NONE, int xerrno = 0);
719c885c 62 void failedErrorMessage(err_type error, int xerrno);
43446566 63 HttpReply *createHttpReply(const Http::StatusCode httpStatus, const int64_t clen = 0);
434a79b0 64 void handleDataRequest();
a5d444a5
DK
65 void startDataDownload();
66 void startDataUpload();
54a6c0cd
CT
67 bool startDirTracking();
68 void stopDirTracking();
69 bool weAreTrackingDir() const {return savedReply.message != NULL;}
434a79b0 70
5517260a 71 typedef void (Relay::*PreliminaryCb)();
434a79b0
DK
72 void forwardPreliminaryReply(const PreliminaryCb cb);
73 void proceedAfterPreliminaryReply();
74 PreliminaryCb thePreliminaryCb;
75
5517260a 76 typedef void (Relay::*SM_FUNC)();
434a79b0 77 static const SM_FUNC SM_FUNCS[];
125894ba 78 void readGreeting();
434a79b0
DK
79 void sendCommand();
80 void readReply();
5f3898e2 81 void readFeatReply();
434a79b0
DK
82 void readPasvReply();
83 void readDataReply();
84 void readTransferDoneReply();
000e664b 85 void readEpsvReply();
54a6c0cd
CT
86 void readCwdOrCdupReply();
87 void readUserOrPassReply();
434a79b0 88
434a79b0
DK
89 void scheduleReadControlReply();
90
719c885c
AR
91 bool forwardingCompleted; ///< completeForwarding() has been called
92
54a6c0cd
CT
93 struct {
94 wordlist *message; ///< reply message, one wordlist entry per message line
95 char *lastCommand; ///< the command caused the reply
96 char *lastReply; ///< last line of reply: reply status plus message
97 int replyCode; ///< the reply status
98 } savedReply; ///< set and delayed while we are tracking using PWD
99
5517260a 100 CBDATA_CLASS2(Relay);
434a79b0
DK
101};
102
5517260a 103} // namespace Ftp
434a79b0 104
5517260a
AR
105CBDATA_NAMESPACED_CLASS_INIT(Ftp, Relay);
106
107const Ftp::Relay::SM_FUNC Ftp::Relay::SM_FUNCS[] = {
108 &Ftp::Relay::readGreeting, // BEGIN
109 &Ftp::Relay::readUserOrPassReply, // SENT_USER
110 &Ftp::Relay::readUserOrPassReply, // SENT_PASS
e7ce227f
AR
111 NULL,/* &Ftp::Relay::readReply */ // SENT_TYPE
112 NULL,/* &Ftp::Relay::readReply */ // SENT_MDTM
113 NULL,/* &Ftp::Relay::readReply */ // SENT_SIZE
000e664b
AR
114 NULL, // SENT_EPRT
115 NULL, // SENT_PORT
5517260a
AR
116 &Ftp::Relay::readEpsvReply, // SENT_EPSV_ALL
117 &Ftp::Relay::readEpsvReply, // SENT_EPSV_1
118 &Ftp::Relay::readEpsvReply, // SENT_EPSV_2
119 &Ftp::Relay::readPasvReply, // SENT_PASV
120 &Ftp::Relay::readCwdOrCdupReply, // SENT_CWD
e7ce227f
AR
121 NULL,/* &Ftp::Relay::readDataReply, */ // SENT_LIST
122 NULL,/* &Ftp::Relay::readDataReply, */ // SENT_NLST
123 NULL,/* &Ftp::Relay::readReply */ // SENT_REST
124 NULL,/* &Ftp::Relay::readDataReply */ // SENT_RETR
125 NULL,/* &Ftp::Relay::readReply */ // SENT_STOR
126 NULL,/* &Ftp::Relay::readReply */ // SENT_QUIT
5517260a
AR
127 &Ftp::Relay::readTransferDoneReply, // READING_DATA
128 &Ftp::Relay::readReply, // WRITING_DATA
e7ce227f 129 NULL,/* &Ftp::Relay::readReply */ // SENT_MKDIR
5517260a 130 &Ftp::Relay::readFeatReply, // SENT_FEAT
e7ce227f 131 NULL,/* &Ftp::Relay::readPwdReply */ // SENT_PWD
5517260a
AR
132 &Ftp::Relay::readCwdOrCdupReply, // SENT_CDUP
133 &Ftp::Relay::readDataReply,// SENT_DATA_REQUEST
134 &Ftp::Relay::readReply, // SENT_COMMAND
000e664b 135 NULL
434a79b0
DK
136};
137
5517260a
AR
138Ftp::Relay::Relay(FwdState *const fwdState):
139 AsyncJob("Ftp::Relay"),
140 Ftp::Client(fwdState),
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{
29f23abf 155 closeServer(); // TODO: move to Server.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
DK
329{
330 ::ServerStateData::handleRequestBodyProducerAborted();
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);
434a79b0
DK
349
350 setVirginReply(reply);
351 adaptOrFinalizeReply();
352
434a79b0
DK
353 serverComplete();
354}
355
356void
5517260a 357Ftp::Relay::forwardPreliminaryReply(const PreliminaryCb cb)
434a79b0 358{
e7ce227f 359 debugs(9, 5, "forwarding preliminary reply to client");
434a79b0 360
92ae4c86
AR
361 // we must prevent concurrent ConnStateData::sendControlMsg() calls
362 Must(thePreliminaryCb == NULL);
434a79b0
DK
363 thePreliminaryCb = cb;
364
365 const HttpReply::Pointer reply = createHttpReply(Http::scContinue);
366
367 // the Sink will use this to call us back after writing 1xx to the client
5517260a 368 typedef NullaryMemFunT<Relay> CbDialer;
434a79b0 369 const AsyncCall::Pointer call = JobCallback(11, 3, CbDialer, this,
27c841f6 370 Ftp::Relay::proceedAfterPreliminaryReply);
434a79b0
DK
371
372 CallJobHere1(9, 4, request->clientConnectionManager, ConnStateData,
373 ConnStateData::sendControlMsg, HttpControlMsg(reply, call));
374}
375
376void
5517260a 377Ftp::Relay::proceedAfterPreliminaryReply()
434a79b0 378{
e7ce227f 379 debugs(9, 5, "proceeding after preliminary reply to client");
434a79b0 380
92ae4c86 381 Must(thePreliminaryCb != NULL);
434a79b0
DK
382 const PreliminaryCb cb = thePreliminaryCb;
383 thePreliminaryCb = NULL;
384 (this->*cb)();
385}
386
387void
5517260a 388Ftp::Relay::forwardError(err_type error, int xerrno)
434a79b0 389{
434a79b0
DK
390 failed(error, xerrno);
391}
392
393HttpReply *
43446566
AR
394Ftp::Relay::createHttpReply(const Http::StatusCode httpStatus, const int64_t clen)
395{
396 HttpReply *const reply = Ftp::HttpReplyWrapper(ctrl.replycode, ctrl.last_reply, httpStatus, clen);
a2c7f09a
AR
397 if (ctrl.message) {
398 for (wordlist *W = ctrl.message; W && W->next; W = W->next)
43446566
AR
399 reply->header.putStr(HDR_FTP_PRE, httpHeaderQuoteString(W->key).c_str());
400 // no hdrCacheInit() is needed for after HDR_FTP_PRE addition
a2c7f09a 401 }
434a79b0
DK
402 return reply;
403}
404
405void
5517260a 406Ftp::Relay::handleDataRequest()
434a79b0 407{
92ae4c86 408 data.addr(master().clientDataAddr);
434a79b0
DK
409 connectDataChannel();
410}
411
412void
5517260a 413Ftp::Relay::startDataDownload()
434a79b0
DK
414{
415 assert(Comm::IsConnOpen(data.conn));
416
e7ce227f 417 debugs(9, 3, "begin data transfer from " << data.conn->remote <<
434a79b0
DK
418 " (" << data.conn->local << ")");
419
420 HttpReply *const reply = createHttpReply(Http::scOkay, -1);
421 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
422 setVirginReply(reply);
423 adaptOrFinalizeReply();
424
434a79b0
DK
425 maybeReadVirginBody();
426 state = READING_DATA;
427}
428
a5d444a5 429void
5517260a 430Ftp::Relay::startDataUpload()
a5d444a5
DK
431{
432 assert(Comm::IsConnOpen(data.conn));
433
e7ce227f 434 debugs(9, 3, "begin data transfer to " << data.conn->remote <<
a5d444a5
DK
435 " (" << data.conn->local << ")");
436
437 if (!startRequestBodyFlow()) { // register to receive body data
438 failed();
439 return;
440 }
441
000e664b 442 state = WRITING_DATA;
a5d444a5
DK
443}
444
434a79b0 445void
5517260a 446Ftp::Relay::readGreeting()
434a79b0 447{
92ae4c86 448 assert(!master().clientReadGreeting);
434a79b0
DK
449
450 switch (ctrl.replycode) {
451 case 220:
92ae4c86 452 updateMaster().clientReadGreeting = true;
e7ce227f
AR
453 if (serverState() == fssBegin)
454 serverState(fssConnected);
29268829 455
43446566
AR
456 // Do not forward server greeting to the user because our FTP Server
457 // has greeted the user already. Also, an original origin greeting may
458 // confuse a user that has changed the origin mid-air.
29268829
DK
459
460 start();
434a79b0
DK
461 break;
462 case 120:
463 if (NULL != ctrl.message)
464 debugs(9, DBG_IMPORTANT, "FTP server is busy: " << ctrl.message->key);
5517260a 465 forwardPreliminaryReply(&Ftp::Relay::scheduleReadControlReply);
434a79b0
DK
466 break;
467 default:
468 failed();
469 break;
470 }
471}
472
473void
5517260a 474Ftp::Relay::sendCommand()
434a79b0
DK
475{
476 if (!fwd->request->header.has(HDR_FTP_COMMAND)) {
43446566 477 abortTransaction("Internal error: FTP relay request with no command");
434a79b0
DK
478 return;
479 }
480
c8059ea9
DK
481 HttpHeader &header = fwd->request->header;
482 assert(header.has(HDR_FTP_COMMAND));
483 const String &cmd = header.findEntry(HDR_FTP_COMMAND)->value;
484 assert(header.has(HDR_FTP_ARGUMENTS));
485 const String &params = header.findEntry(HDR_FTP_ARGUMENTS)->value;
486
487 if (params.size() > 0)
e7ce227f 488 debugs(9, 5, "command: " << cmd << ", parameters: " << params);
434a79b0 489 else
e7ce227f 490 debugs(9, 5, "command: " << cmd << ", no parameters");
434a79b0 491
e7ce227f 492 if (serverState() == fssHandlePasv ||
27c841f6
AR
493 serverState() == fssHandleEpsv ||
494 serverState() == fssHandleEprt ||
495 serverState() == fssHandlePort) {
000e664b
AR
496 sendPassive();
497 return;
498 }
499
1ab04517 500 SBuf buf;
c8059ea9 501 if (params.size() > 0)
1ab04517 502 buf.Printf("%s %s%s", cmd.termedBuf(), params.termedBuf(), Ftp::crlf);
434a79b0 503 else
1ab04517 504 buf.Printf("%s%s", cmd.termedBuf(), Ftp::crlf);
434a79b0 505
1ab04517 506 writeCommand(buf.c_str());
434a79b0 507
cff221ee 508 state =
e7ce227f
AR
509 serverState() == fssHandleCdup ? SENT_CDUP :
510 serverState() == fssHandleCwd ? SENT_CWD :
511 serverState() == fssHandleFeat ? SENT_FEAT :
512 serverState() == fssHandleDataRequest ? SENT_DATA_REQUEST :
513 serverState() == fssHandleUploadRequest ? SENT_DATA_REQUEST :
514 serverState() == fssConnected ? SENT_USER :
515 serverState() == fssHandlePass ? SENT_PASS :
434a79b0
DK
516 SENT_COMMAND;
517}
518
519void
5517260a 520Ftp::Relay::readReply()
434a79b0 521{
e7ce227f
AR
522 assert(serverState() == fssConnected ||
523 serverState() == fssHandleUploadRequest);
434a79b0
DK
524
525 if (100 <= ctrl.replycode && ctrl.replycode < 200)
5517260a 526 forwardPreliminaryReply(&Ftp::Relay::scheduleReadControlReply);
434a79b0
DK
527 else
528 forwardReply();
529}
530
5f3898e2 531void
5517260a 532Ftp::Relay::readFeatReply()
5f3898e2 533{
e7ce227f 534 assert(serverState() == fssHandleFeat);
5f3898e2
AR
535
536 if (100 <= ctrl.replycode && ctrl.replycode < 200)
537 return; // ignore preliminary replies
538
539 forwardReply();
540}
541
434a79b0 542void
5517260a 543Ftp::Relay::readPasvReply()
434a79b0 544{
e7ce227f 545 assert(serverState() == fssHandlePasv || serverState() == fssHandleEpsv || serverState() == fssHandlePort || serverState() == fssHandleEprt);
434a79b0
DK
546
547 if (100 <= ctrl.replycode && ctrl.replycode < 200)
548 return; // ignore preliminary replies
549
92ae4c86 550 if (handlePasvReply(updateMaster().clientDataAddr))
434a79b0 551 forwardReply();
73950ceb 552 else
434a79b0
DK
553 forwardError();
554}
555
cff221ee 556void
5517260a 557Ftp::Relay::readEpsvReply()
cff221ee 558{
cff221ee
AR
559 if (100 <= ctrl.replycode && ctrl.replycode < 200)
560 return; // ignore preliminary replies
561
92ae4c86 562 if (handleEpsvReply(updateMaster().clientDataAddr)) {
000e664b
AR
563 if (ctrl.message == NULL)
564 return; // didn't get complete reply yet
565
cff221ee 566 forwardReply();
000e664b 567 } else
cff221ee
AR
568 forwardError();
569}
570
434a79b0 571void
5517260a 572Ftp::Relay::readDataReply()
434a79b0 573{
e7ce227f
AR
574 assert(serverState() == fssHandleDataRequest ||
575 serverState() == fssHandleUploadRequest);
a5d444a5 576
6af7d28d 577 if (ctrl.replycode == 125 || ctrl.replycode == 150) {
e7ce227f 578 if (serverState() == fssHandleDataRequest)
5517260a 579 forwardPreliminaryReply(&Ftp::Relay::startDataDownload);
e7ce227f 580 else // serverState() == fssHandleUploadRequest
5517260a 581 forwardPreliminaryReply(&Ftp::Relay::startDataUpload);
a5d444a5 582 } else
434a79b0 583 forwardReply();
54a6c0cd
CT
584}
585
586bool
5517260a 587Ftp::Relay::startDirTracking()
54a6c0cd
CT
588{
589 if (!fwd->request->clientConnectionManager->port->ftp_track_dirs)
590 return false;
591
e7ce227f 592 debugs(9, 5, "start directory tracking");
54a6c0cd
CT
593 savedReply.message = ctrl.message;
594 savedReply.lastCommand = ctrl.last_command;
595 savedReply.lastReply = ctrl.last_reply;
596 savedReply.replyCode = ctrl.replycode;
597
598 ctrl.last_command = NULL;
599 ctrl.last_reply = NULL;
600 ctrl.message = NULL;
601 ctrl.offset = 0;
602 writeCommand("PWD\r\n");
603 return true;
604}
605
606void
5517260a 607Ftp::Relay::stopDirTracking()
54a6c0cd 608{
e7ce227f 609 debugs(9, 5, "got code from pwd: " << ctrl.replycode << ", msg: " << ctrl.last_reply);
54a6c0cd 610
92ae4c86
AR
611 if (ctrl.replycode == 257)
612 updateMaster().workingDir = Ftp::UnescapeDoubleQuoted(ctrl.last_reply);
54a6c0cd
CT
613
614 wordlistDestroy(&ctrl.message);
615 safe_free(ctrl.last_command);
616 safe_free(ctrl.last_reply);
617
618 ctrl.message = savedReply.message;
619 ctrl.last_command = savedReply.lastCommand;
620 ctrl.last_reply = savedReply.lastReply;
621 ctrl.replycode = savedReply.replyCode;
622
623 savedReply.message = NULL;
624 savedReply.lastReply = NULL;
625 savedReply.lastCommand = NULL;
626}
627
628void
5517260a 629Ftp::Relay::readCwdOrCdupReply()
54a6c0cd 630{
e7ce227f
AR
631 assert(serverState() == fssHandleCwd ||
632 serverState() == fssHandleCdup);
54a6c0cd 633
e7ce227f 634 debugs(9, 5, "got code " << ctrl.replycode << ", msg: " << ctrl.last_reply);
54a6c0cd
CT
635
636 if (100 <= ctrl.replycode && ctrl.replycode < 200)
637 return;
638
639 if (weAreTrackingDir()) { // we are tracking
640 stopDirTracking(); // and forward the delayed response below
641 } else if (startDirTracking())
642 return;
643
644 forwardReply();
645}
646
647void
5517260a 648Ftp::Relay::readUserOrPassReply()
54a6c0cd
CT
649{
650 if (100 <= ctrl.replycode && ctrl.replycode < 200)
651 return; //Just ignore
652
653 if (weAreTrackingDir()) { // we are tracking
654 stopDirTracking(); // and forward the delayed response below
655 } else if (ctrl.replycode == 230) { // successful login
656 if (startDirTracking())
657 return;
658 }
659
660 forwardReply();
434a79b0
DK
661}
662
663void
5517260a 664Ftp::Relay::readTransferDoneReply()
434a79b0 665{
e7ce227f 666 debugs(9, 3, status());
434a79b0
DK
667
668 if (ctrl.replycode != 226 && ctrl.replycode != 250) {
e7ce227f
AR
669 debugs(9, DBG_IMPORTANT, "got FTP code " << ctrl.replycode <<
670 " after reading response data");
434a79b0
DK
671 }
672
434a79b0
DK
673 serverComplete();
674}
675
676void
43446566 677Ftp::Relay::dataChannelConnected(const CommConnectCbParams &io)
434a79b0 678{
e7ce227f 679 debugs(9, 3, status());
434a79b0
DK
680 data.opener = NULL;
681
43446566 682 if (io.flag != Comm::OK) {
e7ce227f 683 debugs(9, 2, "failed to connect FTP server data channel");
43446566 684 forwardError(ERR_CONNECT_FAIL, io.xerrno);
434a79b0
DK
685 return;
686 }
687
43446566 688 debugs(9, 2, "connected FTP server data channel: " << io.conn);
434a79b0 689
43446566 690 data.opened(io.conn, dataCloser());
434a79b0
DK
691
692 sendCommand();
693}
694
695void
5517260a 696Ftp::Relay::scheduleReadControlReply()
434a79b0 697{
5517260a 698 Ftp::Client::scheduleReadControlReply(0);
434a79b0
DK
699}
700
5517260a
AR
701AsyncJob::Pointer
702Ftp::StartRelay(FwdState *const fwdState)
434a79b0 703{
5517260a 704 return AsyncJob::Start(new Ftp::Relay(fwdState));
434a79b0 705}