50c4c42001-08-23Martin Nilsson // This is a roxen protocol module.
f41b982009-05-07Martin Stjernholm // Copyright © 1997 - 2009, Roxen IS.
50c4c42001-08-23Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba) /* * FTP protocol mk 2
63d8551997-06-10Henrik Grubbström (Grubba)  *
0917d32013-03-04Anders Johansson  * $Id$
63d8551997-06-10Henrik Grubbström (Grubba)  *
0eb9d72000-03-16Martin Nilsson  * Henrik Grubbström <grubba@roxen.com>
63d8551997-06-10Henrik Grubbström (Grubba)  */
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba) /* * TODO:
6866751997-08-29Henrik Grubbström (Grubba)  *
ea50e61999-05-01Henrik Grubbström (Grubba)  * How much is supposed to be logged?
6866751997-08-29Henrik Grubbström (Grubba)  */
c175b91998-03-21Henrik Grubbström (Grubba) /* * Relevant RFC's: *
2159231998-03-23Henrik Grubbström (Grubba)  * RFC 764 TELNET PROTOCOL SPECIFICATION
ea50e61999-05-01Henrik Grubbström (Grubba)  * RFC 765 FILE TRANSFER PROTOCOL
c175b91998-03-21Henrik Grubbström (Grubba)  * RFC 959 FILE TRANSFER PROTOCOL (FTP) * RFC 1123 Requirements for Internet Hosts -- Application and Support
ea50e61999-05-01Henrik Grubbström (Grubba)  * RFC 1579 Firewall-Friendly FTP * RFC 1635 How to Use Anonymous FTP
c175b91998-03-21Henrik Grubbström (Grubba)  *
ea50e61999-05-01Henrik Grubbström (Grubba)  * RFC's describing extra commands: * * RFC 683 FTPSRV - Tenex extension for paged files * RFC 737 FTP Extension: XSEN * RFC 743 FTP extension: XRSQ/XRCP * RFC 775 DIRECTORY ORIENTED FTP COMMANDS * RFC 949 FTP unique-named store command * RFC 1639 FTP Operation Over Big Address Records (FOOBAR)
f2591f2003-10-21Henrik Grubbström (Grubba)  * RFC 2228 FTP Security Extensions
25e99d2017-12-04Henrik Grubbström (Grubba)  * RFC 2389 Feature negotiation mechanism for the FTP
3163752001-03-13Henrik Grubbström (Grubba)  * RFC 2428 FTP Extensions for IPv6 and NATs
25e99d2017-12-04Henrik Grubbström (Grubba)  * RFC 2640 Internationalization of the FTP
3385cd2017-11-21Henrik Grubbström (Grubba)  * RFC 3659 Extensions to FTP
25e99d2017-12-04Henrik Grubbström (Grubba)  * RFC 5797 FTP Command and Extension Registry * RFC 7151 FTP HOST Command for Virtual Hosts
ea50e61999-05-01Henrik Grubbström (Grubba)  * * RFC's with recomendations and discussions:
c175b91998-03-21Henrik Grubbström (Grubba)  * * RFC 607 Comments on the File Transfer Protocol * RFC 614 Response to RFC 607, "Comments on the File Transfer Protocol"
ea50e61999-05-01Henrik Grubbström (Grubba)  * RFC 624 Comments on the File Transfer Protocol
c175b91998-03-21Henrik Grubbström (Grubba)  * RFC 640 Revised FTP Reply Codes * RFC 691 One More Try on the FTP * RFC 724 Proposed Official Standard for the
ea50e61999-05-01Henrik Grubbström (Grubba)  * Format of ARPA Network Messages
1b0a302009-04-02Henrik Grubbström (Grubba)  * RFC 4217 Securing FTP with TLS
ea50e61999-05-01Henrik Grubbström (Grubba)  * * RFC's describing gateways and proxies: * * RFC 1415 FTP-FTAM Gateway Specification
25e99d2017-12-04Henrik Grubbström (Grubba)  * RFC 6384 FTP Application Layer Gateway (ALG) for IPv4-to-IPv6 Translation
ea50e61999-05-01Henrik Grubbström (Grubba)  * * More or less obsolete RFC's: * * RFC 412 User FTP documentation
21b2c32009-04-02Henrik Grubbström (Grubba)  * RFC 438 FTP server-server interaction * RFC 448 Print files in FTP * RFC 458 Mail retrieval via FTP * RFC 463 FTP comments and response to RFC 430 * RFC 468 FTP data compression
3385cd2017-11-21Henrik Grubbström (Grubba)  * RFC 475 FTP and network mail system
21b2c32009-04-02Henrik Grubbström (Grubba)  * RFC 478 FTP server-server interaction - II * RFC 479 Use of FTP by the NIC Journal * RFC 480 Host-dependent FTP parameters * RFC 505 Two solutions to a file transfer access problem * RFC 506 FTP command naming problem * RFC 520 Memo to FTP group: Proposal for File Access Protocol * RFC 532 UCSD-CC Server-FTP facility
ea50e61999-05-01Henrik Grubbström (Grubba)  * RFC 542 File Transfer Protocol for the ARPA Network * RFC 561 Standardizing Network Mail Headers
21b2c32009-04-02Henrik Grubbström (Grubba)  * RFC 571 Tenex FTP problem * RFC 630 FTP error code usage for more reliable mail service * RFC 686 Leaving well enough alone * RFC 697 CWD Command of FTP
c175b91998-03-21Henrik Grubbström (Grubba)  * RFC 751 SURVEY OF FTP MAIL AND MLFL * RFC 754 Out-of-Net Host Addresses for Mail
ea50e61999-05-01Henrik Grubbström (Grubba)  *
3385cd2017-11-21Henrik Grubbström (Grubba)  * (RFCs are available from http://pike.lysator.liu.se/docs/ietf/rfc/).
c175b91998-03-21Henrik Grubbström (Grubba)  */
382e652017-12-05Henrik Grubbström (Grubba) #include <roxen.h>
b1fca01996-11-12Per Hedbor #include <module.h> #include <stat.h>
ea50e61999-05-01Henrik Grubbström (Grubba) //#define FTP2_DEBUG
50c4c42001-08-23Martin Nilsson #define FTP2_XTRA_HELP ({ "Report any bugs at http://community.roxen.com/crunch/" })
3e61d11997-03-09Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba) #define FTP2_TIMEOUT (5*60)
3e61d11997-03-09Henrik Grubbström (Grubba) 
e3c15f2014-08-13Henrik Grubbström (Grubba) // Enable the use of handler threads. #define FTP_USE_HANDLER_THREADS
b0132d1999-10-08Henrik Grubbström (Grubba) // #define Query(X) conf->variables[X][VAR_VALUE]
ea50e61999-05-01Henrik Grubbström (Grubba)  #ifdef FTP2_DEBUG
17e2b22001-06-15Johan Sundström # define DWRITE(X ...) werror(X)
6a409d1999-12-27Martin Nilsson #else
17e2b22001-06-15Johan Sundström # define DWRITE(X ...)
6a409d1999-12-27Martin Nilsson #endif
b1fca01996-11-12Per Hedbor 
382e652017-12-05Henrik Grubbström (Grubba) //<locale-token project="prot_ftp">LOCALE</locale-token> #define LOCALE(X,Y) _DEF_LOCALE("prot_ftp",X,Y) // end of the locale related stuff
ea50e61999-05-01Henrik Grubbström (Grubba) #define BACKEND_CLOSE(FD) do { DWRITE("close\n"); FD->set_blocking(); call_out(FD->close, 0); FD = 0; } while(0) class RequestID2
b1fca01996-11-12Per Hedbor {
ea50e61999-05-01Henrik Grubbström (Grubba)  inherit RequestID;
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  mapping file;
3e61d11997-03-09Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba) #ifdef FTP2_DEBUG
fc40392008-08-15Martin Stjernholm  protected void trace_enter(mixed a, mixed b)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  write("FTP: TRACE_ENTER(%O, %O)\n", a, b);
ea50e61999-05-01Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  protected void trace_leave(mixed a)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  write("FTP: TRACE_LEAVE(%O)\n", a);
ea50e61999-05-01Henrik Grubbström (Grubba)  } #endif /* FTP2_DEBUG */ void ready_to_receive() { // FIXME: Should hook the STOR reply to this function. }
84fb682000-02-03Per Hedbor  Configuration configuration() { return conf; } Stdio.File connection( ) { return my_fd; }
ea50e61999-05-01Henrik Grubbström (Grubba)  void send_result(mapping|void result) {
80d2e61999-05-06Henrik Grubbström (Grubba)  if (mappingp(result) && my_fd && my_fd->done) { my_fd->done(result); return; }
8942861999-05-19Henrik Grubbström (Grubba)  error("Async sending with send_result() not supported yet.\n");
ea50e61999-05-01Henrik Grubbström (Grubba)  } object(RequestID2) clone_me() { object(RequestID2) o = this_object(); return(object_program(o)(o)); } void end() { }
fc40392008-08-15Martin Stjernholm  protected constant __num = ({ 0 });
1fd3172002-02-26Anders Johansson  int _num; void destroy() { #ifdef FTP_REQUESTID_DEBUG report_debug("REQUESTID: Destroy request id #%d.\n", _num); #endif }
ea50e61999-05-01Henrik Grubbström (Grubba)  void create(object|void m_rid) {
1fd3172002-02-26Anders Johansson #ifdef FTP_REQUESTID_DEBUG _num = ++__num[0];
d07ce92004-02-24Henrik Grubbström (Grubba)  if (m_rid) { report_debug("REQUESTID: New request id #%d (CHILD to #%d).\n", _num, m_rid->_num); } else { report_debug("REQUESTID: New request id #%d (MASTER).\n", _num); }
1fd3172002-02-26Anders Johansson #else
6a409d1999-12-27Martin Nilsson  DWRITE("REQUESTID: New request id.\n");
1fd3172002-02-26Anders Johansson #endif
ea50e61999-05-01Henrik Grubbström (Grubba)  if (m_rid) { object o = this_object(); foreach(indices(m_rid), string var) {
9fae3c2009-03-24Henrik Grubbström (Grubba)  if (object_variablep(o, var)) {
e58efa2004-05-26Martin Stjernholm #ifdef DEBUG
e81b182000-03-20Henrik Grubbström (Grubba)  if (catch {
e58efa2004-05-26Martin Stjernholm #endif /* DEBUG */
e81b182000-03-20Henrik Grubbström (Grubba)  o[var] = m_rid[var];
e58efa2004-05-26Martin Stjernholm #ifdef DEBUG
e81b182000-03-20Henrik Grubbström (Grubba)  }) {
17e2b22001-06-15Johan Sundström  report_error("FTP2: " "Failed to copy variable %s (value:%O)\n", var, m_rid[var]);
e81b182000-03-20Henrik Grubbström (Grubba)  }
e58efa2004-05-26Martin Stjernholm #endif /* DEBUG */
ea50e61999-05-01Henrik Grubbström (Grubba)  }
3e61d11997-03-09Henrik Grubbström (Grubba)  }
7831fa2006-12-20Martin Stjernholm  o->misc = m_rid->misc + ([]);
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { // Defaults... client = ({ "ftp" }); prot = "FTP"; clientprot = "FTP";
41b69e2004-06-08Henrik Grubbström (Grubba)  variables = FakedVariables(real_variables = ([]));
e9bf332006-12-04Martin Stjernholm  misc = (["pref_languages": PrefLanguages()]);
dbd94b2005-12-07Henrik Grubbström (Grubba)  cookies = CookieJar();
a58dd12000-03-29Henrik Grubbström (Grubba)  throttle = ([]);
db4ca92000-09-18Henrik Grubbström (Grubba)  client_var = ([]);
a58dd12000-03-29Henrik Grubbström (Grubba)  request_headers = ([]);
ea50e61999-05-01Henrik Grubbström (Grubba)  prestate = (<>); config = (<>); supports = (< "ftp", "images", "tables", >); pragma = (<>); rest_query = ""; extra_extension = "";
4af8e52008-06-19Marcus Agehall  root_id = this_object();
3e61d11997-03-09Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  time = predef::time(1); #ifdef FTP2_DEBUG misc->trace_enter = trace_enter; misc->trace_leave = trace_leave; #endif /* FTP2_DEBUG */
3e61d11997-03-09Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba) };
3e61d11997-03-09Henrik Grubbström (Grubba) 
e01e952001-09-22Henrik Grubbström (Grubba) class FileWrapper
ea50e61999-05-01Henrik Grubbström (Grubba) {
fc40392008-08-15Martin Stjernholm  protected string convert(string s);
b1fca01996-11-12Per Hedbor 
fc40392008-08-15Martin Stjernholm  private function read_cb; private function close_cb; private mixed id;
280ffa1999-10-29Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private object f; private string data; private object ftpsession;
e01e952001-09-22Henrik Grubbström (Grubba)  int is_file;
fc40392008-08-15Martin Stjernholm  protected void create(object f_, string data_, object ftpsession_)
e01e952001-09-22Henrik Grubbström (Grubba)  { f = f_; data = data_; ftpsession = ftpsession_; is_file = f_->is_file; }
fc40392008-08-15Martin Stjernholm  private void read_callback(mixed i, string s)
ea50e61999-05-01Henrik Grubbström (Grubba)  { read_cb(id, convert(s)); ftpsession->touch_me(); }
fc40392008-08-15Martin Stjernholm  private void close_callback(mixed i)
ea50e61999-05-01Henrik Grubbström (Grubba)  { ftpsession->touch_me();
7999af2009-06-23Henrik Grubbström (Grubba)  close_cb(id);
ea50e61999-05-01Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  private void delayed_nonblocking(function w_cb)
280ffa1999-10-29Henrik Grubbström (Grubba)  { string d = data; data = 0; f->set_nonblocking(read_callback, w_cb, close_callback); if (d) { read_callback(0, d); } }
ea50e61999-05-01Henrik Grubbström (Grubba)  void set_nonblocking(function r_cb, function w_cb, function c_cb) { read_cb = r_cb; close_cb = c_cb;
bb70ce1999-10-29Henrik Grubbström (Grubba)  remove_call_out(delayed_nonblocking);
ea50e61999-05-01Henrik Grubbström (Grubba)  if (r_cb) {
280ffa1999-10-29Henrik Grubbström (Grubba)  if (data) { // We need to call r_cb as soon as possible, but we can't do it here // and we can't enable the read_callback just yet to maintain order. call_out(delayed_nonblocking, 0, w_cb); f->set_nonblocking(0, w_cb, 0); } else { f->set_nonblocking(read_callback, w_cb, close_callback); }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { f->set_nonblocking(0, w_cb, 0); } } void set_blocking() {
280ffa1999-10-29Henrik Grubbström (Grubba)  if (data) {
bb70ce1999-10-29Henrik Grubbström (Grubba)  remove_call_out(delayed_nonblocking);
280ffa1999-10-29Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  f->set_blocking(); } void set_id(mixed i) { id = i; f->set_id(i); } int query_fd() { return -1; } string read(int|void n) { ftpsession->touch_me();
280ffa1999-10-29Henrik Grubbström (Grubba)  if (data) { if (n) { if (n < sizeof(data)) { string d = data[..n-1]; data = data[n..]; return convert(d); } else { string d = data; data = 0; return convert(d + f->read(n - sizeof(d))); } } else { string d = data; data = 0; return convert(d + f->read()); } }
ea50e61999-05-01Henrik Grubbström (Grubba)  return(convert(f->read(n))); } void close() {
f3fa622009-06-23Henrik Grubbström (Grubba)  if (ftpsession) ftpsession->touch_me();
ea50e61999-05-01Henrik Grubbström (Grubba)  if (f) { f->set_blocking(); BACKEND_CLOSE(f); } }
55ab132006-08-17Marcus Wellhardh  string query_address(int|void loc) { if (!f->query_address) { werror("%O->query_address(%O)\n", f, loc); } return f->query_address(loc); }
b1fca01996-11-12Per Hedbor }
ea50e61999-05-01Henrik Grubbström (Grubba) class ToAsciiWrapper
b1fca01996-11-12Per Hedbor {
ea50e61999-05-01Henrik Grubbström (Grubba)  inherit FileWrapper; int converted;
fc40392008-08-15Martin Stjernholm  protected string convert(string s)
b1fca01996-11-12Per Hedbor  {
ea50e61999-05-01Henrik Grubbström (Grubba)  converted += sizeof(s);
e27e511999-05-22Henrik Grubbström (Grubba)  return(replace(s, ({ "\r\n", "\n", "\r" }), ({ "\r\n", "\r\n", "\r\n" })));
b1fca01996-11-12Per Hedbor  } }
ea50e61999-05-01Henrik Grubbström (Grubba) class FromAsciiWrapper { inherit FileWrapper; int converted;
b1fca01996-11-12Per Hedbor 
fc40392008-08-15Martin Stjernholm  protected string convert(string s)
ea50e61999-05-01Henrik Grubbström (Grubba)  { converted += sizeof(s); #ifdef __NT__
e27e511999-05-22Henrik Grubbström (Grubba)  // This replace shouldn't be needed, but we're paranoid. return(replace(s, ({ "\r\n", "\n", "\r" }), ({ "\r\n", "\r\n", "\r\n" })));
ea50e61999-05-01Henrik Grubbström (Grubba) #else /* !__NT__ */
e27e511999-05-22Henrik Grubbström (Grubba) #ifdef __MACOS__ return(replace(s, ({ "\r\n", "\n", "\r" }), ({ "\r", "\r", "\r" }))); #else /* !__MACOS__ */ return(replace(s, ({ "\r\n", "\n", "\r" }), ({ "\n", "\n", "\n" }))); #endif /* __MACOS__ */
ea50e61999-05-01Henrik Grubbström (Grubba) #endif /* __NT__ */ } }
b1fca01996-11-12Per Hedbor 
c3e98f1999-05-01Henrik Grubbström (Grubba) // This one is needed for touch_me() to be called as needed.
ea50e61999-05-01Henrik Grubbström (Grubba) class BinaryWrapper
b1fca01996-11-12Per Hedbor {
ea50e61999-05-01Henrik Grubbström (Grubba)  inherit FileWrapper;
fc40392008-08-15Martin Stjernholm  protected string convert(string s)
ea50e61999-05-01Henrik Grubbström (Grubba)  { return(s); }
b1fca01996-11-12Per Hedbor }
ea50e61999-05-01Henrik Grubbström (Grubba) // EBCDIC Wrappers here.
031f321997-08-31Henrik Grubbström (Grubba) 
ca56381999-05-01Henrik Grubbström (Grubba) class ToEBCDICWrapper { inherit FileWrapper; int converted;
50aa162015-04-28Jonas Walldén  protected Charset.Encoder converter = Charset.encoder("EBCDIC-US", "");
ca56381999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected string convert(string s)
ca56381999-05-01Henrik Grubbström (Grubba)  { converted += sizeof(s); return(converter->feed(s)->drain()); } } class FromEBCDICWrapper { inherit FileWrapper; int converted;
50aa162015-04-28Jonas Walldén  protected Charset.Decoder converter = Charset.decoder("EBCDIC-US");
ca56381999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected string convert(string s)
ca56381999-05-01Henrik Grubbström (Grubba)  { converted += sizeof(s); return(converter->feed(s)->drain()); } }
ea50e61999-05-01Henrik Grubbström (Grubba) 
e01e952001-09-22Henrik Grubbström (Grubba) class PutFileWrapper
031f321997-08-31Henrik Grubbström (Grubba) {
fc40392008-08-15Martin Stjernholm  protected int response_code = 226; protected array(string) response = ({"Stored."}); protected string gotdata = ""; protected int closed, recvd; protected function other_read_callback;
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected object from_fd; protected object session; protected object ftpsession;
e01e952001-09-22Henrik Grubbström (Grubba)  int is_file;
fc40392008-08-15Martin Stjernholm  protected void create(object from_fd_, object session_, object ftpsession_)
e01e952001-09-22Henrik Grubbström (Grubba)  { from_fd = from_fd_; session = session_; ftpsession = ftpsession_; is_file = from_fd->is_file; }
ea50e61999-05-01Henrik Grubbström (Grubba)  int bytes_received() { return recvd;
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  int close(string|void how) { DWRITE("FTP: PUT: close()\n"); ftpsession->touch_me();
80d2e61999-05-06Henrik Grubbström (Grubba)  if(how != "w" && !closed) {
893a9c2004-03-03Martin Stjernholm  ftpsession->send(response_code, response);
80d2e61999-05-06Henrik Grubbström (Grubba)  closed = 1;
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->received += recvd; session->file->len = recvd; session->conf->log(session->file, session);
3dad632006-12-08Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } if (how) { return from_fd->close(how); } else { BACKEND_CLOSE(from_fd); return 0;
031f321997-08-31Henrik Grubbström (Grubba)  } }
ea50e61999-05-01Henrik Grubbström (Grubba)  string read(mixed ... args) { DWRITE("FTP: PUT: read()\n"); ftpsession->touch_me(); string r = from_fd->read(@args); if(stringp(r)) recvd += sizeof(r); return r; }
41ddda1997-04-05Marcus Comstedt 
fc40392008-08-15Martin Stjernholm  protected mixed my_read_callback(mixed id, string data)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: PUT: my_read_callback(X, \"%s\")\n", data||"");
ea50e61999-05-01Henrik Grubbström (Grubba)  ftpsession->touch_me(); if(stringp(data)) recvd += sizeof(data); return other_read_callback(id, data);
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  void set_read_callback(function read_callback) { DWRITE("FTP: PUT: set_read_callback()\n"); ftpsession->touch_me(); if(read_callback) { other_read_callback = read_callback; from_fd->set_read_callback(my_read_callback); } else from_fd->set_read_callback(read_callback); } void set_nonblocking(function ... args) { DWRITE("FTP: PUT: set_nonblocking()\n"); if(sizeof(args) && args[0]) { other_read_callback = args[0]; from_fd->set_nonblocking(my_read_callback, @args[1..]); } else from_fd->set_nonblocking(@args); }
80d2e61999-05-06Henrik Grubbström (Grubba)  void set_blocking() { from_fd->set_blocking(); }
ea50e61999-05-01Henrik Grubbström (Grubba)  void set_id(mixed id) { from_fd->set_id(id); } int write(string data) {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: PUT: write(\"%s\")\n", data||"");
ea50e61999-05-01Henrik Grubbström (Grubba)  ftpsession->touch_me(); int n, code; string msg; gotdata += data; while((n=search(gotdata, "\n"))>=0) { if(3==sscanf(gotdata[..n], "HTTP/%*s %d %[^\r\n]", code, msg) && code>199) { if(code < 300) code = 226; else code = 550; response_code = code;
893a9c2004-03-03Martin Stjernholm  response = ({msg});
ea50e61999-05-01Henrik Grubbström (Grubba)  } gotdata = gotdata[n+1..];
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  return strlen(data); }
80d2e61999-05-06Henrik Grubbström (Grubba)  void done(mapping result) { if (result->error < 300) { response_code = 226; } else { response_code = 550; } // Cut away the code.
893a9c2004-03-03Martin Stjernholm  if (result->rettext) response = result->rettext / "\n"; else
1f57322008-05-07Martin Stjernholm  response = ({Roxen.http_status_messages[result->error] || ""});
80d2e61999-05-06Henrik Grubbström (Grubba)  gotdata = result->data || ""; close(); }
ea50e61999-05-01Henrik Grubbström (Grubba)  string query_address(int|void loc) {
55ab132006-08-17Marcus Wellhardh  if (!from_fd->query_address) { werror("%O->query_address(%O)\n", from_fd, loc); }
ea50e61999-05-01Henrik Grubbström (Grubba)  return from_fd->query_address(loc);
031f321997-08-31Henrik Grubbström (Grubba)  } }
c52b5e2017-11-22Henrik Grubbström (Grubba) protected string name_from_uid(RequestID master_session, int uid) { string res; // NB: find_user_from_uid() can be quite slow(!), so we // cache the result for the duration of the connection. if (!master_session->misc->username_from_uid) { master_session->misc->username_from_uid = ([]); } else if (res = master_session->misc->username_from_uid[uid]) { return res; } User user; foreach(master_session->conf->user_databases(), UserDB user_db) { if (user = user_db->find_user_from_uid(uid)) { master_session->misc->username_from_uid[uid] = res = user->name(); return res; } } master_session->misc->username_from_uid[uid] = res = (uid?((string)uid):"root"); return res; }
ea50e61999-05-01Henrik Grubbström (Grubba) // Simulated /usr/bin/ls pipe #define LS_FLAG_A 0x00001 #define LS_FLAG_a 0x00002 #define LS_FLAG_b 0x00004 #define LS_FLAG_C 0x00008 #define LS_FLAG_d 0x00010 #define LS_FLAG_F 0x00020 #define LS_FLAG_f 0x00040 #define LS_FLAG_G 0x00080 #define LS_FLAG_h 0x00100 #define LS_FLAG_l 0x00200 #define LS_FLAG_m 0x00400 #define LS_FLAG_n 0x00800 #define LS_FLAG_r 0x01000 #define LS_FLAG_Q 0x02000 #define LS_FLAG_R 0x04000 #define LS_FLAG_S 0x08000 #define LS_FLAG_s 0x10000 #define LS_FLAG_t 0x20000 #define LS_FLAG_U 0x40000 #define LS_FLAG_v 0x80000
fc40392008-08-15Martin Stjernholm class LS_L(protected RequestID master_session, protected int|void flags)
ea50e61999-05-01Henrik Grubbström (Grubba) {
fc40392008-08-15Martin Stjernholm  protected constant decode_mode = ({
ea50e61999-05-01Henrik Grubbström (Grubba)  ({ S_IRUSR, S_IRUSR, 1, "r" }), ({ S_IWUSR, S_IWUSR, 2, "w" }), ({ S_IXUSR|S_ISUID, S_IXUSR, 3, "x" }), ({ S_IXUSR|S_ISUID, S_ISUID, 3, "S" }), ({ S_IXUSR|S_ISUID, S_IXUSR|S_ISUID, 3, "s" }), ({ S_IRGRP, S_IRGRP, 4, "r" }), ({ S_IWGRP, S_IWGRP, 5, "w" }), ({ S_IXGRP|S_ISGID, S_IXGRP, 6, "x" }), ({ S_IXGRP|S_ISGID, S_ISGID, 6, "S" }), ({ S_IXGRP|S_ISGID, S_IXGRP|S_ISGID, 6, "s" }), ({ S_IROTH, S_IROTH, 7, "r" }), ({ S_IWOTH, S_IWOTH, 8, "w" }), ({ S_IXOTH|S_ISVTX, S_IXOTH, 9, "x" }), ({ S_IXOTH|S_ISVTX, S_ISVTX, 9, "T" }), ({ S_IXOTH|S_ISVTX, S_IXOTH|S_ISVTX, 9, "t" }) });
fc40392008-08-15Martin Stjernholm  protected constant months = ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
ea50e61999-05-01Henrik Grubbström (Grubba)  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" });
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  string ls_l(string file, array st)
b1fca01996-11-12Per Hedbor  {
17e2b22001-06-15Johan Sundström  DWRITE("ls_l(\"%s\")\n", file);
ea50e61999-05-01Henrik Grubbström (Grubba)  int mode = st[0] & 007777; array(string) perm = "----------"/"";
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (st[1] < 0) { perm[0] = "d";
031f321997-08-31Henrik Grubbström (Grubba)  }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  foreach(decode_mode, array(string|int) info) { if ((mode & info[0]) == info[1]) { perm[info[2]] = info[3];
031f321997-08-31Henrik Grubbström (Grubba)  } }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  mapping lt = localtime(st[3]);
25f6541999-05-19Henrik Grubbström (Grubba)  // NOTE: SiteBuilder may set st[5] and st[6] to strings.
ea50e61999-05-01Henrik Grubbström (Grubba)  string user = (string)st[5]; string group = (string)st[6]; if (!(flags & LS_FLAG_n)) { // Use symbolic names for uid and gid. if (!stringp(st[5])) {
c52b5e2017-11-22Henrik Grubbström (Grubba)  user = name_from_uid(master_session, st[5]);
ea50e61999-05-01Henrik Grubbström (Grubba)  }
07b65b2004-06-09Henrik Grubbström (Grubba)  if (!stringp(st[6])) { // FIXME: Convert st[6] to symbolic group name. if (!st[6]) group = "wheel"; }
ea50e61999-05-01Henrik Grubbström (Grubba)  } string ts; int now = time(1); // Half a year: // 365.25*24*60*60/2 = 15778800 if ((st[3] <= now - 15778800) || (st[3] > now)) { // Month Day Year ts = sprintf("%s %02d %04d", months[lt->mon], lt->mday, 1900+lt->year);
031f321997-08-31Henrik Grubbström (Grubba)  } else {
ea50e61999-05-01Henrik Grubbström (Grubba)  // Month Day Hour:minute ts = sprintf("%s %02d %02d:%02d", months[lt->mon], lt->mday, lt->hour, lt->min); } if (flags & LS_FLAG_G) { // No group. return sprintf("%s 1 %-10s %12d %s %s\n", perm*"", user, (st[1]<0? 512:st[1]), ts, file); } else { return sprintf("%s 1 %-10s %-6s %12d %s %s\n", perm*"", user, group, (st[1]<0? 512:st[1]), ts, file);
031f321997-08-31Henrik Grubbström (Grubba)  } }
ea50e61999-05-01Henrik Grubbström (Grubba) } class LSFile {
fc40392008-08-15Martin Stjernholm  protected inherit LS_L;
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected string cwd; protected array(string) argv; protected object ftpsession;
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected array(string) output_queue = ({}); protected int output_pos; protected string output_mode = "A";
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected mapping(string:array|object) stat_cache = ([]);
ea50e61999-05-01Henrik Grubbström (Grubba) 
50aa162015-04-28Jonas Walldén  protected Charset.Encoder conv;
b1ca391999-10-29Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected array|object stat_file(string long, RequestID|void session)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
97b9102000-09-13Henrik Grubbström (Grubba)  array|object st = stat_cache[long];
ea50e61999-05-01Henrik Grubbström (Grubba)  if (zero_type(st)) {
d07ce92004-02-24Henrik Grubbström (Grubba)  session = RequestID2(session || master_session); session->method = "DIR";
c0f4542001-02-19Jonas Wallden  long = replace(long, "//", "/");
ea50e61999-05-01Henrik Grubbström (Grubba)  st = session->conf->stat_file(long, session); stat_cache[long] = st;
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } return st; } // FIXME: Should convert output somewhere below.
fc40392008-08-15Martin Stjernholm  protected void output(string s)
ea50e61999-05-01Henrik Grubbström (Grubba)  { if(stringp(s)) { // ls is always ASCII-mode... s = replace(s, "\n", "\r\n");
b1ca391999-10-29Henrik Grubbström (Grubba)  if (conv) { // EBCDIC or potentially other charsets. s = conv->feed(s)->drain(); }
ea50e61999-05-01Henrik Grubbström (Grubba)  } output_queue += ({ s }); }
fc40392008-08-15Martin Stjernholm  protected string quote_non_print(string s)
ea50e61999-05-01Henrik Grubbström (Grubba)  { return(replace(s, ({ "\000", "\001", "\002", "\003", "\004", "\005", "\006", "\007", "\010", "\011", "\012", "\013", "\014", "\015", "\016", "\017", "\200", "\201", "\202", "\203", "\204", "\205", "\206", "\207", "\210", "\211", "\212", "\213", "\214", "\215", "\216", "\217", "\177", }), ({ "\\000", "\\001", "\\002", "\\003", "\\004", "\\005", "\\006", "\\007", "\\010", "\\011", "\\012", "\\013", "\\014", "\\015", "\\016", "\\017", "\\200", "\\201", "\\202", "\\203", "\\204", "\\205", "\\206", "\\207", "\\210", "\\211", "\\212", "\\213", "\\214", "\\215", "\\216", "\\217", "\\177", }))); }
fc40392008-08-15Martin Stjernholm  protected string list_files(array(string) files, string|void dir)
ea50e61999-05-01Henrik Grubbström (Grubba)  { dir = dir || cwd;
17e2b22001-06-15Johan Sundström  DWRITE("FTP: LSFile->list_files(%O, \"%s\"\n", files, dir);
a476711997-10-20Henrik Grubbström (Grubba) 
adc97b1997-10-09Henrik Grubbström (Grubba)  if (!(flags & LS_FLAG_U)) {
ea50e61999-05-01Henrik Grubbström (Grubba)  if (flags & LS_FLAG_S) { array(int) sizes = allocate(sizeof(files)); int i; for (i=0; i < sizeof(files); i++) {
97b9102000-09-13Henrik Grubbström (Grubba)  array|object st = stat_file(combine_path(dir, files[i]));
3405901997-10-04Henrik Grubbström (Grubba)  if (st) {
ea50e61999-05-01Henrik Grubbström (Grubba)  sizes[i] = st[1];
3405901997-10-04Henrik Grubbström (Grubba)  } else {
ea50e61999-05-01Henrik Grubbström (Grubba)  // Should not happen, but... files -= ({ files[i] });
3405901997-10-04Henrik Grubbström (Grubba)  } }
ea50e61999-05-01Henrik Grubbström (Grubba)  sort(sizes, files); } else if (flags & LS_FLAG_t) { array(int) times = allocate(sizeof(files)); int i; for (i=0; i < sizeof(files); i++) {
97b9102000-09-13Henrik Grubbström (Grubba)  array|object st = stat_file(combine_path(dir, files[i]));
ea50e61999-05-01Henrik Grubbström (Grubba)  if (st) { times[i] = -st[-4]; // Note: Negative time. } else { // Should not happen, but... files -= ({ files[i] }); }
3405901997-10-04Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  sort(times, files);
3405901997-10-04Henrik Grubbström (Grubba)  } else {
ea50e61999-05-01Henrik Grubbström (Grubba)  sort(files); } if (flags & LS_FLAG_r) { files = reverse(files);
031f321997-08-31Henrik Grubbström (Grubba)  } }
ea50e61999-05-01Henrik Grubbström (Grubba) 
031f321997-08-31Henrik Grubbström (Grubba)  string res = "";
ea50e61999-05-01Henrik Grubbström (Grubba)  int total; foreach(files, string short) { string long = combine_path(dir, short);
97b9102000-09-13Henrik Grubbström (Grubba)  array|object st = stat_file(long);
9ad0c61997-09-03Henrik Grubbström (Grubba)  if (st) {
ea50e61999-05-01Henrik Grubbström (Grubba)  if (flags & LS_FLAG_Q) { // Enclose in quotes. // Space needs to be quoted to be compatible with -m short = "\"" + replace(short, ({ "\n", "\r", "\\", "\"", "\'", " " }), ({ "\\n", "\\r", "\\\\", "\\\"", "\\\'", "\\020" })) + "\""; }
a994a92017-12-04Henrik Grubbström (Grubba)  short = string_to_utf8(short);
9ad0c61997-09-03Henrik Grubbström (Grubba)  if (flags & LS_FLAG_F) { if (st[1] < 0) {
ea50e61999-05-01Henrik Grubbström (Grubba)  // Directory
9ad0c61997-09-03Henrik Grubbström (Grubba)  short += "/"; } else if (st[0] & 0111) {
ea50e61999-05-01Henrik Grubbström (Grubba)  // Executable
9ad0c61997-09-03Henrik Grubbström (Grubba)  short += "*"; } }
ea50e61999-05-01Henrik Grubbström (Grubba)  int blocks = 1; if (st[1] >= 0) { blocks = (st[1] + 1023)/1024; // Blocks are 1KB. } total += blocks; if (flags & LS_FLAG_s) { res += sprintf("%7d ", blocks);
84fb682000-02-03Per Hedbor  }
ea50e61999-05-01Henrik Grubbström (Grubba)  if (flags & LS_FLAG_b) { short = quote_non_print(short); }
9ad0c61997-09-03Henrik Grubbström (Grubba)  if (flags & LS_FLAG_l) {
ea50e61999-05-01Henrik Grubbström (Grubba)  res += ls_l(short, st);
9ad0c61997-09-03Henrik Grubbström (Grubba)  } else { res += short + "\n";
031f321997-08-31Henrik Grubbström (Grubba)  } } }
ea50e61999-05-01Henrik Grubbström (Grubba)  switch (flags & (LS_FLAG_l|LS_FLAG_C|LS_FLAG_m)) { case LS_FLAG_C: res = sprintf("%#-79s\n", res); break; case LS_FLAG_m: res = sprintf("%=-79s\n", (res/"\n")*", "); break; case LS_FLAG_l: res = "total " + total + "\n" + res; break; default: break;
031f321997-08-31Henrik Grubbström (Grubba)  } return(res); }
c39ebd2000-12-28Martin Stjernholm #if constant (ADT.Stack)
fc40392008-08-15Martin Stjernholm  protected ADT.Stack dir_stack = ADT.Stack();
c39ebd2000-12-28Martin Stjernholm #else
fc40392008-08-15Martin Stjernholm  protected object(Stack.stack) dir_stack = Stack.stack();
c39ebd2000-12-28Martin Stjernholm #endif
fc40392008-08-15Martin Stjernholm  protected int name_directories;
031f321997-08-31Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected string fix_path(string s)
ea50e61999-05-01Henrik Grubbström (Grubba)  { return(combine_path(cwd, s)); }
23bfc52014-08-07Henrik Grubbström (Grubba)  protected int(0..1) list_next_directory()
ea50e61999-05-01Henrik Grubbström (Grubba)  { if (dir_stack->ptr) { string short = dir_stack->pop(); string long = fix_path(short); if ((!sizeof(long)) || (long[-1] != '/')) { long += "/";
39c8111997-11-06Henrik Grubbström (Grubba)  }
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "DIR";
7af2912000-02-02Henrik Grubbström (Grubba)  mixed err; mapping(string:array) dir; err = catch { dir = session->conf->find_dir_stat(long, session); };
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
7af2912000-02-02Henrik Grubbström (Grubba)  if (err) {
17e2b22001-06-15Johan Sundström  report_error("FTP: LSFile->list_next_directory(): " "find_dir_stat(\"%s\") failed:\n" "%s\n", long, describe_backtrace(err));
7af2912000-02-02Henrik Grubbström (Grubba)  }
031f321997-08-31Henrik Grubbström (Grubba) 
17e2b22001-06-15Johan Sundström  DWRITE("FTP: LSFile->list_next_directory(): " "find_dir_stat(\"%s\") => %O\n", long, dir);
031f321997-08-31Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  // Put them in the stat cache. foreach(indices(dir||({})), string f) { stat_cache[combine_path(long, f)] = dir[f]; }
b5bd312017-11-21Henrik Grubbström (Grubba)  dir = dir || ([]); if (flags & LS_FLAG_a) { if (long != "/") {
ea50e61999-05-01Henrik Grubbström (Grubba)  dir[".."] = stat_file(combine_path(long,"../"));
031f321997-08-31Henrik Grubbström (Grubba)  }
b5bd312017-11-21Henrik Grubbström (Grubba)  dir["."] = stat_file(combine_path(long));
ea50e61999-05-01Henrik Grubbström (Grubba)  }
b5bd312017-11-21Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  string listing = "";
b5bd312017-11-21Henrik Grubbström (Grubba)  if (sizeof(dir)) {
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!(flags & LS_FLAG_A)) { foreach(indices(dir), string f) { if (sizeof(f) && (f[0] == '.')) { m_delete(dir, f);
a476711997-10-20Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } } else if (!(flags & LS_FLAG_a)) { foreach(indices(dir), string f) { if ((< ".", ".." >)[f]) { m_delete(dir, f);
a476711997-10-20Henrik Grubbström (Grubba)  }
c250571997-09-07Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } if (flags & LS_FLAG_R) { foreach(indices(dir), string f) { if (!((<".","..">)[f])) { array(mixed) st = dir[f]; if (st && (st[1] < 0)) { if (short[-1] == '/') { dir_stack->push(short + f); } else { dir_stack->push(short + "/" + f);
031f321997-08-31Henrik Grubbström (Grubba)  } } } } }
ea50e61999-05-01Henrik Grubbström (Grubba)  if (sizeof(dir)) { listing = list_files(indices(dir), long); } else if (flags & LS_FLAG_l) { listing = "total 0\n";
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { DWRITE("FTP: LSFile->list_next_directory(): NO FILES!\n"); if (flags & LS_FLAG_l) { listing = "total 0\n";
35e1bf1997-10-25Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } if (name_directories) { listing = "\n" + short + ":\n" + listing; } if (listing != "") { output(listing); } session = RequestID2(master_session); session->method = "LIST"; session->not_query = long; session->conf->log(([ "error":200, "len":sizeof(listing) ]), session); } if (!dir_stack->ptr) { output(0); // End marker.
23bfc52014-08-07Henrik Grubbström (Grubba)  return 0;
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { name_directories = 1;
23bfc52014-08-07Henrik Grubbström (Grubba)  return 1;
ea50e61999-05-01Henrik Grubbström (Grubba)  } } void set_blocking() { } int query_fd() { return -1; }
fc40392008-08-15Martin Stjernholm  protected mixed id;
ea50e61999-05-01Henrik Grubbström (Grubba)  void set_id(mixed i) { id = i; }
23bfc52014-08-07Henrik Grubbström (Grubba)  void fill_output_queue() { if (!sizeof(output_queue) || output_queue[-1]) { while (list_next_directory()) ; } }
ea50e61999-05-01Henrik Grubbström (Grubba)  string read(int|void n, int|void not_all) {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: LSFile->read(%d, %d)\n", n, not_all);
031f321997-08-31Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  ftpsession->touch_me(); while(sizeof(output_queue) <= output_pos) { // Clean anything pending in the queue. output_queue = ({}); output_pos = 0; // Generate some more output... list_next_directory(); } string s = output_queue[output_pos]; if (s) { if (n && (n < sizeof(s))) { output_queue[output_pos] = s[n..]; s = s[..n-1];
c250571997-09-07Henrik Grubbström (Grubba)  } else {
ea50e61999-05-01Henrik Grubbström (Grubba)  output_queue[output_pos++] = 0;
c250571997-09-07Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  return s; } else { // EOF master_session->file = 0; // Avoid extra log-entry. return "";
031f321997-08-31Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor  }
ea50e61999-05-01Henrik Grubbström (Grubba)  void create(string cwd_, array(string) argv_, int flags_, object session_, string output_mode_, object ftpsession_)
031f321997-08-31Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: LSFile(\"%s\", %O, %08x, X, \"%s\")\n", cwd_, argv_, flags_, output_mode_);
ea50e61999-05-01Henrik Grubbström (Grubba)  ::create(session_, flags_); cwd = cwd_; argv = argv_; output_mode = output_mode_; ftpsession = ftpsession_;
b1ca391999-10-29Henrik Grubbström (Grubba)  if (output_mode == "E") { // EBCDIC
50aa162015-04-28Jonas Walldén  conv = Charset.encoder("EBCDIC-US", "");
b1ca391999-10-29Henrik Grubbström (Grubba)  }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  array(string) files = allocate(sizeof(argv)); int n_files; foreach(argv, string short) {
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "LIST"; string long = fix_path(short);
97b9102000-09-13Henrik Grubbström (Grubba)  array|object st = stat_file(long, session);
e1da692000-01-05Martin Stjernholm  if (st) {
abdff21999-12-27Martin Nilsson  if ((< -2, -3 >)[st[1]] &&
ea50e61999-05-01Henrik Grubbström (Grubba)  (!(flags & LS_FLAG_d))) {
abdff21999-12-27Martin Nilsson  // Directory
ea50e61999-05-01Henrik Grubbström (Grubba)  dir_stack->push(short); } else { files[n_files++] = short;
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { output(short + ": not found\n"); session->conf->log(([ "error":404 ]), session);
031f321997-08-31Henrik Grubbström (Grubba)  }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor 
17e2b22001-06-15Johan Sundström  DWRITE("FTP: LSFile: %d files, %d directories\n", n_files, dir_stack->ptr);
4fc7fe1998-04-21Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (n_files) { if (n_files < sizeof(files)) { files -= ({ 0 });
c250571997-09-07Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  string s = list_files(files, cwd); // May modify dir_stack (-R) output(s);
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->not_query = Array.map(files, fix_path) * " "; session->method = "LIST"; session->conf->log(([ "error":200, "len":sizeof(s) ]), session);
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } if (dir_stack->ptr) { name_directories = dir_stack->ptr && ((dir_stack->ptr > 1) || n_files); list_next_directory(); } else { output(0); } } } class TelnetSession {
fc40392008-08-15Martin Stjernholm  protected object fd; protected object conf;
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private mapping cb; private mixed id;
9fa1de2017-11-20Henrik Grubbström (Grubba)  protected function(mixed|void:string) write_cb; protected function(mixed, string:void) read_cb; protected function(mixed|void:void) close_cb;
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private constant TelnetCodes = ([
ea50e61999-05-01Henrik Grubbström (Grubba)  236:"EOF", // End Of File 237:"SUSP", // Suspend Process 238:"ABORT", // Abort Process 239:"EOR", // End Of Record // The following ones are specified in RFC 959: 240:"SE", // Subnegotiation End 241:"NOP", // No Operation 242:"DM", // Data Mark 243:"BRK", // Break 244:"IP", // Interrupt Process 245:"AO", // Abort Output 246:"AYT", // Are You There 247:"EC", // Erase Character 248:"EL", // Erase Line 249:"GA", // Go Ahead 250:"SB", // Subnegotiation 251:"WILL", // Desire to begin/confirmation of option 252:"WON'T", // Refusal to begin/continue option 253:"DO", // Request to begin/confirmation of option 254:"DON'T", // Demand/confirmation of stop of option 255:"IAC", // Interpret As Command ]); // Some prototypes needed by Pike 0.5
fc40392008-08-15Martin Stjernholm  private void got_data(mixed ignored, string s); private void send_data(); private void got_oob(mixed ignored, string s);
ea50e61999-05-01Henrik Grubbström (Grubba)  void set_write_callback(function(mixed|void:string) w_cb) { if (fd) { write_cb = w_cb; fd->set_nonblocking(got_data, w_cb && send_data, close_cb, got_oob); } }
fc40392008-08-15Martin Stjernholm  private string to_send = ""; private void send(string s)
ea50e61999-05-01Henrik Grubbström (Grubba)  { to_send += s; }
fc40392008-08-15Martin Stjernholm  private void send_data()
ea50e61999-05-01Henrik Grubbström (Grubba)  { if (!sizeof(to_send)) { to_send = write_cb(id); } if (fd) { if (!to_send) { // Support for delayed close. BACKEND_CLOSE(fd); } else if (sizeof(to_send)) { int n = fd->write(to_send); if (n >= 0) { conf->hsent += n;
84fb682000-02-03Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  to_send = to_send[n..]; if (sizeof(to_send)) { fd->set_write_callback(send_data); } } else { // Error.
17e2b22001-06-15Johan Sundström  DWRITE("TELNET: write failed: errno:%d\n", fd->errno());
ea50e61999-05-01Henrik Grubbström (Grubba)  BACKEND_CLOSE(fd); } } else { // Nothing to send for the moment. // FIXME: Is this the correct use? fd->set_write_callback(0);
41c7fe1998-04-13Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  report_warning("FTP2: Write callback with nothing to send.\n");
c250571997-09-07Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { report_error("FTP2: Write callback with no fd.\n"); destruct(); } }
fc40392008-08-15Martin Stjernholm  private mapping(string:function) default_cb = ([
ea50e61999-05-01Henrik Grubbström (Grubba)  "BRK":lambda() {
90970a2003-05-19Henrik Grubbström (Grubba)  if (fd) { fd->close(); fd = 0; }
ea50e61999-05-01Henrik Grubbström (Grubba)  destruct(); throw(0); }, "AYT":lambda() { send("\377\361"); // NOP }, "WILL":lambda(int code) { send(sprintf("\377\376%c", code)); // DON'T xxx }, "DO":lambda(int code) { send(sprintf("\377\374%c", code)); // WON'T xxx }, ]);
fc40392008-08-15Martin Stjernholm  private int sync = 0;
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private void got_oob(mixed ignored, string s)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("TELNET: got_oob(\"%s\")\n", s);
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  sync = sync || (s == "\377"); if (cb["URG"]) { cb["URG"](id, s);
031f321997-08-31Henrik Grubbström (Grubba)  } }
b1fca01996-11-12Per Hedbor 
fc40392008-08-15Martin Stjernholm  private string rest = ""; private void got_data(mixed ignored, string s)
031f321997-08-31Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("TELNET: got_data(\"%s\")\n", s);
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (sizeof(s) && (s[0] == 242)) { DWRITE("TELNET: Data Mark\n"); // Data Mark handing. s = s[1..]; sync = 0; }
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  // A single read() can contain multiple or partial commands // RFC 1123 4.1.2.10
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  array lines = s/"\r\n";
031f321997-08-31Henrik Grubbström (Grubba) 
338ab72001-09-11Henrik Grubbström (Grubba)  // Censor the raw string. s = sprintf("string(%d bytes)", sizeof(s));
ea50e61999-05-01Henrik Grubbström (Grubba)  int lineno; for(lineno = 0; lineno < sizeof(lines); lineno++) { string line = lines[lineno]; if (search(line, "\377") != -1) { array a = line / "\377";
031f321997-08-31Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  string parsed_line = a[0];
031f321997-08-31Henrik Grubbström (Grubba)  int i;
ea50e61999-05-01Henrik Grubbström (Grubba)  for (i=1; i < sizeof(a); i++) { string part = a[i]; if (sizeof(part)) { string name = TelnetCodes[part[0]];
17e2b22001-06-15Johan Sundström  DWRITE("TELNET: Code %s\n", name || "Unknown");
ea50e61999-05-01Henrik Grubbström (Grubba)  int j; function fun; switch (name) { case 0: // FIXME: Should probably have a warning here. break; default: if (fun = (cb[name] || default_cb[name])) { mixed err = catch { fun(); }; if (err) { throw(err); } else if (!zero_type(err)) { // We were just destructed. return;
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } a[i] = a[i][1..]; break; case "EC": // Erase Character for (j=i; j--;) { if (sizeof(a[j])) { a[j] = a[j][..sizeof(a[j])-2]; break;
031f321997-08-31Henrik Grubbström (Grubba)  } }
ea50e61999-05-01Henrik Grubbström (Grubba)  a[i] = a[i][1..]; break; case "EL": // Erase Line for (j=0; j < i; j++) { a[j] = ""; } a[i] = a[i][1..]; break; case "WILL": case "WON'T": case "DO": case "DON'T": if (fun = (cb[name] || default_cb[name])) { fun(a[i][1]);
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  a[i] = a[i][2..]; break; case "DM": // Data Mark if (sync) { for (j=0; j < i; j++) { a[j] = ""; } } a[i] = a[i][1..]; sync = 0; break;
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { a[i] = "\377"; i++;
031f321997-08-31Henrik Grubbström (Grubba)  } }
ea50e61999-05-01Henrik Grubbström (Grubba)  line = a * "";
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!lineno) { line = rest + line; } if (lineno < (sizeof(lines)-1)) { if ((!sync) && read_cb) {
17e2b22001-06-15Johan Sundström  DWRITE("TELNET: Calling read_callback(X, \"%s\")\n", line);
ea50e61999-05-01Henrik Grubbström (Grubba)  read_cb(id, line); } } else {
17e2b22001-06-15Johan Sundström  DWRITE("TELNET: Partial line is \"%s\"\n", line);
ea50e61999-05-01Henrik Grubbström (Grubba)  rest = line;
031f321997-08-31Henrik Grubbström (Grubba)  }
5155b51997-09-03Henrik Grubbström (Grubba)  }
7a87c71997-09-03Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  void create(object f, function(mixed,string:void) r_cb, function(mixed|void:string) w_cb, function(mixed|void:void) c_cb, mapping callbacks, mixed|void new_id)
031f321997-08-31Henrik Grubbström (Grubba)  {
ea50e61999-05-01Henrik Grubbström (Grubba)  fd = f; cb = callbacks;
031f321997-08-31Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  read_cb = r_cb; write_cb = w_cb; close_cb = c_cb; id = new_id;
031f321997-08-31Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  fd->set_nonblocking(got_data, w_cb && send_data, close_cb, got_oob);
031f321997-08-31Henrik Grubbström (Grubba)  } };
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba) class FTPSession
2d3ff51997-04-10Marcus Comstedt {
ea50e61999-05-01Henrik Grubbström (Grubba)  // However, a server-FTP MUST be capable of // accepting and refusing Telnet negotiations (i.e., sending // DON'T/WON'T). RFC 1123 4.1.2.12
2d3ff51997-04-10Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  inherit TelnetSession; inherit "roxenlib";
7de2602017-12-05Henrik Grubbström (Grubba)  private mapping(string:string|Locale.DeferredLocale) cmd_help = ([
ea50e61999-05-01Henrik Grubbström (Grubba)  // FTP commands in reverse RFC order. // The following is a command suggested by the author of ncftp.
7de2602017-12-05Henrik Grubbström (Grubba)  "CLNT":LOCALE(1, "<sp> <client-name> <sp> <client-version> " "[<sp> <optional platform info>] (Set client name)"),
ea50e61999-05-01Henrik Grubbström (Grubba) 
25e99d2017-12-04Henrik Grubbström (Grubba)  // These are from RFC 7151
7de2602017-12-05Henrik Grubbström (Grubba)  "HOST":LOCALE(2, "<sp> hostname (Host name)"),
25e99d2017-12-04Henrik Grubbström (Grubba)  // These are from RFC 3659
7de2602017-12-05Henrik Grubbström (Grubba)  "MDTM":LOCALE(3, "<sp> path-name (Modification time)"), "SIZE":LOCALE(4, "<sp> path-name (Size)"), "MLST":LOCALE(5, "<sp> path-name (Machine Processing List File)"), "MLSD":LOCALE(6, "<sp> path-name (Machine Processing List Directory)"),
25e99d2017-12-04Henrik Grubbström (Grubba)  // These are from RFC 2640
7de2602017-12-05Henrik Grubbström (Grubba)  "LANG":LOCALE(7, "<sp> lang-tag (Change Interface Language)"),
ea50e61999-05-01Henrik Grubbström (Grubba) 
3163752001-03-13Henrik Grubbström (Grubba)  // These are from RFC 2428
7de2602017-12-05Henrik Grubbström (Grubba)  "EPRT":LOCALE(8, "<sp> <d>net-prt<d>net-addr<d>tcp-port<d> (Extended Address Port)"), "EPSV":LOCALE(9, "[<sp> net-prt|ALL] (Extended Address Passive Mode)"),
3163752001-03-13Henrik Grubbström (Grubba) 
25e99d2017-12-04Henrik Grubbström (Grubba)  // These are from RFC 2389
7de2602017-12-05Henrik Grubbström (Grubba)  "FEAT":LOCALE(10, "(Feature list)"), "OPTS":LOCALE(11, "<sp> command <sp> options (Set Command-specific Options)"),
25e99d2017-12-04Henrik Grubbström (Grubba) 
f2591f2003-10-21Henrik Grubbström (Grubba)  // These are from RFC 2228 (FTP Security Extensions)
7de2602017-12-05Henrik Grubbström (Grubba)  "AUTH":LOCALE(12, "security-mechanism (Authentication/Security Mechanism)"), "ADAT":LOCALE(13, "security-data (Authentication/Security Data)"), "PBSZ":LOCALE(14, "<sp> size (Protection Buffer SiZe)"), "PROT":LOCALE(15, "<sp> [ C | S | E | P ] (Data Channel Protection Level)"), "CCC":LOCALE(16, "(Clear Command Channel)"), "MIC":LOCALE(17, "command (Integrity Protected Command)"), "CONF":LOCALE(18, "command (Confidentiality Protected Command)"), "ENC":LOCALE(19, "command (Privacy Protected Command)"),
f2591f2003-10-21Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  // These are in RFC 1639
7de2602017-12-05Henrik Grubbström (Grubba)  "LPRT":LOCALE(20, "<sp> <long-host-port> (Long Port)"), "LPSV":LOCALE(21, "(Long Passive)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // Commands in the order from RFC 959 // Login
7de2602017-12-05Henrik Grubbström (Grubba)  "USER":LOCALE(22, "<sp> username (Change user)"), "PASS":LOCALE(23, "<sp> password (Change password)"), "ACCT":LOCALE(24, "<sp> <account-information> (Account)"), "CWD":LOCALE(25, "[ <sp> directory-name ] (Change working directory)"), "CDUP":LOCALE(26, "(Change to parent directory)"), "SMNT":LOCALE(27, "<sp> <pathname> (Structure mount)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // Logout
7de2602017-12-05Henrik Grubbström (Grubba)  "REIN":LOCALE(28, "(Reinitialize)"), "QUIT":LOCALE(29, "(Terminate service)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // Transfer parameters
7de2602017-12-05Henrik Grubbström (Grubba)  "PORT":LOCALE(30, "<sp> b0, b1, b2, b3, b4 (Set port IP and number)"), "PASV":LOCALE(31, "(Set server in passive mode)"), "TYPE":LOCALE(32, "<sp> [ A | E | I | L ] (Ascii, Ebcdic, Image, Local)"), "STRU":LOCALE(33, "<sp> <structure-code> (File structure)"), "MODE":LOCALE(34, "<sp> <mode-code> (Transfer mode)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // File action commands
7de2602017-12-05Henrik Grubbström (Grubba)  "ALLO":LOCALE(35, "<sp> <decimal-integer> [<sp> R <sp> <decimal-integer>]" " (Allocate space for file)"), "REST":LOCALE(36, "<sp> marker (Set restart marker)"), "STOR":LOCALE(37, "<sp> file-name (Store file)"), "STOU":LOCALE(38, "(Store file with unique name)"), "RETR":LOCALE(39, "<sp> file-name (Retreive file)"), "LIST":LOCALE(40, "[ <sp> <pathname> ] (List directory)"), "NLST":LOCALE(40, "[ <sp> <pathname> ] (List directory)"), "APPE":LOCALE(41, "<sp> <pathname> (Append file)"), "RNFR":LOCALE(42, "<sp> <pathname> (Rename from)"), "RNTO":LOCALE(43, "<sp> <pathname> (Rename to)"), "DELE":LOCALE(44, "<sp> file-name (Delete file)"), "RMD":LOCALE(45, "<sp> <pathname> (Remove directory)"), "MKD":LOCALE(46, "<sp> <pathname> (Make directory)"), "PWD":LOCALE(47, "(Return current directory)"), "ABOR":LOCALE(48, "(Abort current transmission)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // Informational commands
7de2602017-12-05Henrik Grubbström (Grubba)  "SYST":LOCALE(49, "(Get type of operating system)"), "STAT":LOCALE(50, "[ <sp> <pathname> ] (Status for server/file)"), "HELP":LOCALE(51, "[ <sp> <string> ] (Give help)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // Miscellaneous commands
7de2602017-12-05Henrik Grubbström (Grubba)  "SITE":LOCALE(52, "<sp> <string> (Site parameters)"),// Has separate help "NOOP":LOCALE(53, "(No operation)"),
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  // Old "Experimental commands" // These are in RFC 775 // Required by RFC 1123 4.1.3.1
7de2602017-12-05Henrik Grubbström (Grubba)  "XMKD":LOCALE(54, "<sp> path-name (Make directory)"), "XRMD":LOCALE(55, "<sp> path-name (Remove directory)"), "XPWD":LOCALE(47, "(Return current directory)"), "XCWD":LOCALE(25, "[ <sp> directory-name ] (Change working directory)"), "XCUP":LOCALE(26, "(Change to parent directory)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // These are in RFC 765 but not in RFC 959
7de2602017-12-05Henrik Grubbström (Grubba)  "MAIL":LOCALE(56, "[<sp> <recipient name>] (Mail to user)"), "MSND":LOCALE(57, "[<sp> <recipient name>] (Mail send to terminal)"), "MSOM":LOCALE(58, "[<sp> <recipient name>] (Mail send to terminal or mailbox)"), "MSAM":LOCALE(59, "[<sp> <recipient name>] (Mail send to terminal and mailbox)"), "MRSQ":LOCALE(60, "[<sp> <scheme>] (Mail recipient scheme question)"), "MRCP":LOCALE(61, "<sp> <recipient name> (Mail recipient)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // These are in RFC 743
7de2602017-12-05Henrik Grubbström (Grubba)  "XRSQ":LOCALE(62, "[<sp> <scheme>] (Scheme selection)"), "XRCP":LOCALE(63, "<sp> <recipient name> (Recipient specification)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // These are in RFC 737
7de2602017-12-05Henrik Grubbström (Grubba)  "XSEN":LOCALE(64, "[<sp> <recipient name>] (Send to terminal)"), "XSEM":LOCALE(65, "[<sp> <recipient name>] (Send, mail if can\'t)"), "XMAS":LOCALE(66, "[<sp> <recipient name>] (Mail and send)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  // These are in RFC 542
7de2602017-12-05Henrik Grubbström (Grubba)  "BYE":LOCALE(67, "(Logout)"), "BYTE":LOCALE(68, "<sp> <bits> (Byte size)"), "SOCK":LOCALE(69, "<sp> host-socket (Data socket)"),
ea50e61999-05-01Henrik Grubbström (Grubba) 
3385cd2017-11-21Henrik Grubbström (Grubba) #if 0 // These are in RFC 475
7de2602017-12-05Henrik Grubbström (Grubba)  "MLTO":LOCALE(70, "<sp> <recipient name> (Initiate mail to user)"), "FROM":LOCALE(71, "<sp> <sender name> (Mail from)"), "MTYP":LOCALE(72, "<sp> [ U | O | L ] (Mail type)"), "RECO":LOCALE(73, "[<sp> <mail unique id>] (Mail record)"),
3385cd2017-11-21Henrik Grubbström (Grubba) #if 0 // NB: Conflicts with AUTH from RFC 2228 above.
7de2602017-12-05Henrik Grubbström (Grubba)  "AUTH":LOCALE(74, "<sp> <author id> (Mail author)"),
3385cd2017-11-21Henrik Grubbström (Grubba) #endif
7de2602017-12-05Henrik Grubbström (Grubba)  "TITL":LOCALE(75, "<sp> <title> (Mail title/subject)"), "ACKN":LOCALE(76, "(Mail acknowledge)"), "TEXT":LOCALE(77, "(Mail text)"), "FILE":LOCALE(78, "<sp> <filename> (Mail file)"), "CITA":LOCALE(79, "<sp> <file name> (Mail citation)"),
3385cd2017-11-21Henrik Grubbström (Grubba) #endif
ea50e61999-05-01Henrik Grubbström (Grubba)  // This one is referenced in a lot of old RFCs
7de2602017-12-05Henrik Grubbström (Grubba)  "MLFL":LOCALE(80, "(Mail file)"),
ea50e61999-05-01Henrik Grubbström (Grubba)  ]);
7de2602017-12-05Henrik Grubbström (Grubba)  private mapping(string:string|Locale.DeferredLocale) site_help = ([ "CHMOD":LOCALE(81, "<sp> mode <sp> file"), "UMASK":LOCALE(82, "<sp> mode"), "PRESTATE":LOCALE(83, "<sp> prestate"),
ea50e61999-05-01Henrik Grubbström (Grubba)  ]);
abdff21999-12-27Martin Nilsson 
7de2602017-12-05Henrik Grubbström (Grubba)  private mapping(string:string|Locale.DeferredLocale) opts_help = ([ "MLST":LOCALE(84, "<sp> <fact-list>"),
f4a4f02017-11-20Henrik Grubbström (Grubba)  ]);
fc40392008-08-15Martin Stjernholm  private constant modes = ([
ea50e61999-05-01Henrik Grubbström (Grubba)  "A":"ASCII", "E":"EBCDIC", "I":"BINARY", "L":"LOCAL", ]);
2d3ff51997-04-10Marcus Comstedt 
fc40392008-08-15Martin Stjernholm  private int time_touch = time();
b1fca01996-11-12Per Hedbor 
fc40392008-08-15Martin Stjernholm  private object(ADT.Queue) to_send = ADT.Queue();
2408161997-05-07Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private int end_marker = 0;
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  void touch_me()
b1fca01996-11-12Per Hedbor  {
ea50e61999-05-01Henrik Grubbström (Grubba)  time_touch = time();
b1fca01996-11-12Per Hedbor  }
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private string write_cb()
53e8681997-10-05Per Hedbor  {
ea50e61999-05-01Henrik Grubbström (Grubba)  touch_me();
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (to_send->is_empty()) {
0a34b11997-03-13Henrik Grubbström (Grubba) 
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: write_cb(): Empty send queue.\n");
ea50e61999-05-01Henrik Grubbström (Grubba)  ::set_write_callback(0); if (end_marker) {
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: write_cb(): Sending EOF.\n");
ea50e61999-05-01Henrik Grubbström (Grubba)  return(0); // Mark EOF }
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: write_cb(): Sending \"\"\n");
ea50e61999-05-01Henrik Grubbström (Grubba)  return(""); // Shouldn't happen, but... } else {
6baab42014-08-08Henrik Grubbström (Grubba)  string|int s = to_send->get();
d16b3e1997-03-27Henrik Grubbström (Grubba) 
42f81a2015-06-17Henrik Grubbström (Grubba) #if constant(SSL.File)
6baab42014-08-08Henrik Grubbström (Grubba)  if (s == 1) { DWRITE("FTP2: write_cb(): STARTTLS.\n"); // NB: This callback is only called when the send buffers // are empty, and it is thus safe to switch to TLS. // Switch to TLS. if (!fd->renegotiate) { fd = SSL.File(fd, port_obj->ctx);
17c3522016-02-22Henrik Grubbström (Grubba)  master_session->my_fd = fd;
6baab42014-08-08Henrik Grubbström (Grubba)  fd->accept(); }
d883632015-05-13Henrik Grubbström (Grubba)  // Restore the callbacks in the new SSL connection. ::set_write_callback(write_cb);
6baab42014-08-08Henrik Grubbström (Grubba)  return "";
80e1a72014-08-08Henrik Grubbström (Grubba)  } else if (s == 2) { DWRITE("FTP2: write_cb(): ENDTLS.\n"); if (fd->renegotiate && !has_prefix(sprintf("%O", port_obj), "SSLProtocol")) {
17c3522016-02-22Henrik Grubbström (Grubba)  // Deactive StartTLS connection. master_session->my_fd = fd = fd->shutdown();
80e1a72014-08-08Henrik Grubbström (Grubba)  if (fd) { // Move the callbacks back to the raw connection. ::set_write_callback(write_cb); } } return "";
6baab42014-08-08Henrik Grubbström (Grubba)  }
42f81a2015-06-17Henrik Grubbström (Grubba) #endif
6baab42014-08-08Henrik Grubbström (Grubba)  DWRITE("FTP2: write_cb(): Sending %O.\n", s);
d16b3e1997-03-27Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if ((to_send->is_empty()) && (!end_marker)) { ::set_write_callback(0);
d883632015-05-13Henrik Grubbström (Grubba)  } else if (stringp(to_send->peek())) { // Not about to switch TLS mode.
ea50e61999-05-01Henrik Grubbström (Grubba)  ::set_write_callback(write_cb); } return(s); } }
a36f051997-11-27Henrik Grubbström (Grubba) 
5301f42009-05-12Henrik Grubbström (Grubba)  int(0..1) busy;
1931842014-08-07Henrik Grubbström (Grubba) #ifdef FTP_USE_HANDLER_THREADS #define next_cmd() call_out(low_next_cmd, 0) #else #define low_next_cmd() next_cmd() #endif
e049b32014-08-08Henrik Grubbström (Grubba)  void low_send(int code, array(string) data, int|void enumerate_all)
d16b3e1997-03-27Henrik Grubbström (Grubba)  {
e049b32014-08-08Henrik Grubbström (Grubba)  DWRITE("FTP2: low_send(%d, %O)\n", code, data);
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!data || end_marker) { end_marker = 1; ::set_write_callback(write_cb);
d16b3e1997-03-27Henrik Grubbström (Grubba)  return; }
ea50e61999-05-01Henrik Grubbström (Grubba)  string s; int i; if (sizeof(data) > 1) { data[0] = sprintf("%03d-%s\r\n", code, data[i]); for (i = sizeof(data)-1; --i; ) { if (enumerate_all) { data[i] = sprintf("%03d-%s\r\n", code, data[i]); } else { data[i] = " " + data[i] + "\r\n"; } } } data[sizeof(data)-1] = sprintf("%03d %s\r\n", code, data[sizeof(data)-1]); s = data * ""; if (sizeof(s)) { if (to_send->is_empty()) {
a994a92017-12-04Henrik Grubbström (Grubba)  to_send->put(string_to_utf8(s));
ea50e61999-05-01Henrik Grubbström (Grubba)  ::set_write_callback(write_cb); } else {
a994a92017-12-04Henrik Grubbström (Grubba)  to_send->put(string_to_utf8(s));
ea50e61999-05-01Henrik Grubbström (Grubba)  } } else {
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: send(): Nothing to send!\n");
ea50e61999-05-01Henrik Grubbström (Grubba)  }
e049b32014-08-08Henrik Grubbström (Grubba)  } void send(int code, array(string) data, int|void enumerate_all) { DWRITE("FTP2: send(%d, %O)\n", code, data); low_send(code, data, enumerate_all);
5301f42009-05-12Henrik Grubbström (Grubba)  if (code >= 200) { // Command finished, get the next. busy = 0; next_cmd(); }
d16b3e1997-03-27Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  private RequestID master_session;
d16b3e1997-03-27Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private string dataport_addr; private int dataport_port;
d16b3e1997-03-27Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private string mode = "A";
ad72bd1997-04-09Marcus Comstedt 
fc40392008-08-15Martin Stjernholm  private string cwd = "/";
ad72bd1997-04-09Marcus Comstedt 
fc40392008-08-15Martin Stjernholm  private User auth_user;
d8816e2002-10-22Jonas Wallden  // Authenticated user.
8025352001-01-22Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private string user; private string password; private int logged_in;
ad72bd1997-04-09Marcus Comstedt 
fc40392008-08-15Martin Stjernholm  private object curr_pipe; private int restart_point;
ad72bd1997-04-09Marcus Comstedt 
fc40392008-08-15Martin Stjernholm  private multiset|int allowed_shells = 0;
ea50e61999-05-01Henrik Grubbström (Grubba)  // On a multihomed server host, the default data transfer port // (L-1) MUST be associated with the same local IP address as // the corresponding control connection to port L. // RFC 1123 4.1.2.12 string local_addr; int local_port;
38cb8a2009-03-06Henrik Grubbström (Grubba)  string e_mode = "1"; /* IPv4 */
ea50e61999-05-01Henrik Grubbström (Grubba) 
b0132d1999-10-08Henrik Grubbström (Grubba)  // The listen port object
6abda82000-08-04Martin Stjernholm  roxen.Protocol port_obj;
b0132d1999-10-08Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  /*
382e652017-12-05Henrik Grubbström (Grubba)  * Locale & Language handling */ protected string format_langlist() { array(string) langs = Locale.list_languages("prot_ftp") + ({}); string current = roxen.get_locale(); foreach(langs; int i; string lang) { if (lang == current) { langs[i] = current + "*"; current = 0; break; } } if (current) { langs += ({ current + "*" }); } return sort(langs) * ";"; } protected void restore_locale() { string lang = master_session->misc["accept-language"]; if (lang) { roxen.set_locale(lang); } else { roxen.set_locale(); } } /*
ea50e61999-05-01Henrik Grubbström (Grubba)  * Misc */
fc40392008-08-15Martin Stjernholm  private int check_shell(string shell)
94d84c1997-08-29Marcus Comstedt  {
b0132d1999-10-08Henrik Grubbström (Grubba)  // FIXME: Should the shell database be protocol specific or // virtual-server specific? if (port_obj->query_option("shells") != "") {
ea50e61999-05-01Henrik Grubbström (Grubba)  // FIXME: Hmm, the cache will probably be empty almost always // since it's part of the FTPsession object. // Oh, well, shouldn't matter much unless you have *lots* of // shells. // /grubba 1998-05-21 if (!allowed_shells) { object(Stdio.File) file = Stdio.File();
b0132d1999-10-08Henrik Grubbström (Grubba)  if (file->open(port_obj->query_option("shells"), "r")) {
ea50e61999-05-01Henrik Grubbström (Grubba)  allowed_shells = aggregate_multiset(@(Array.map(file->read(0x7fffffff)/"\n", lambda(string line) { return(((((line/"#")[0])/"") - ({" ", "\t"}))*""); } )-({""})));
17e2b22001-06-15Johan Sundström  DWRITE("ftp.pike: allowed_shells: %O\n", allowed_shells);
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
17e2b22001-06-15Johan Sundström  report_debug("ftp.pike: Failed to open shell database (%O)\n", port_obj->query_option("shells")); return 0;
ea50e61999-05-01Henrik Grubbström (Grubba)  } } return(allowed_shells[shell]); } return 1;
94d84c1997-08-29Marcus Comstedt  }
ad72bd1997-04-09Marcus Comstedt 
fc40392008-08-15Martin Stjernholm  private string fix_path(string s)
ad72bd1997-04-09Marcus Comstedt  {
a994a92017-12-04Henrik Grubbström (Grubba)  mixed err = catch { s = utf8_to_string(s); };
aa2faa2018-03-01Henrik Grubbström (Grubba)  if (String.width(s) > 8) { // Wide, so it might contain combiners. // Combine them if they are there. s = Unicode.normalize(s, "NFC"); }
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!sizeof(s)) { if (cwd[-1] == '/') { return(cwd); } else { return(cwd + "/"); } } else if (s[0] == '~') { return(combine_path("/", s)); } else if (s[0] == '/') { return(simplify_path(s)); } else { return(combine_path(cwd, s));
ad72bd1997-04-09Marcus Comstedt  } }
ea50e61999-05-01Henrik Grubbström (Grubba)  /* * PASV handling */
fc40392008-08-15Martin Stjernholm  private object pasv_port; private function(object, mixed ...:void) pasv_callback; private mixed pasv_args; private array(object) pasv_accepted = ({});
ea50e61999-05-01Henrik Grubbström (Grubba)  void pasv_accept_callback(mixed id)
94d84c1997-08-29Marcus Comstedt  {
f2591f2003-10-21Henrik Grubbström (Grubba)  DWRITE("FTP: pasv_accept_callback(%O)...\n", id);
ea50e61999-05-01Henrik Grubbström (Grubba)  touch_me(); if(pasv_port) { object fd = pasv_port->accept(); if(fd) { // On a multihomed server host, the default data transfer port // (L-1) MUST be associated with the same local IP address as // the corresponding control connection to port L. // RFC 1123 4.1.2.12 array(string) remote = (fd->query_address()||"? ?")/" "; #ifdef FD_DEBUG mark_fd(fd->query_fd(), "ftp communication: -> "+remote[0]+":"+remote[1]); #endif if(pasv_callback) {
280ffa1999-10-29Henrik Grubbström (Grubba)  pasv_callback(fd, "", @pasv_args);
ea50e61999-05-01Henrik Grubbström (Grubba)  pasv_callback = 0; } else { pasv_accepted += ({ fd }); } } }
94d84c1997-08-29Marcus Comstedt  }
fc40392008-08-15Martin Stjernholm  private void ftp_async_accept(function(object,mixed ...:void) fun, mixed ... args)
94d84c1997-08-29Marcus Comstedt  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: async_accept(%O, %@O)...\n", fun, args);
ea50e61999-05-01Henrik Grubbström (Grubba)  touch_me();
94d84c1997-08-29Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (sizeof(pasv_accepted)) {
280ffa1999-10-29Henrik Grubbström (Grubba)  fun(pasv_accepted[0], "", @args);
ea50e61999-05-01Henrik Grubbström (Grubba)  pasv_accepted = pasv_accepted[1..]; } else { pasv_callback = fun; pasv_args = args; }
94d84c1997-08-29Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba)  /* * PORT handling */
94d84c1997-08-29Marcus Comstedt 
fc40392008-08-15Martin Stjernholm  private void ftp_async_connect(function(object,string,mixed ...:void) fun, mixed ... args)
ad72bd1997-04-09Marcus Comstedt  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: async_connect(%O, %@O)...\n", fun, args);
ea50e61999-05-01Henrik Grubbström (Grubba)  // More or less copied from socket.pike
280ffa1999-10-29Henrik Grubbström (Grubba)  if (!dataport_addr) { DWRITE("FTP: No dataport specified.\n");
7a8c5b2001-03-03Henrik Grubbström (Grubba)  fun(0, "", @args);
280ffa1999-10-29Henrik Grubbström (Grubba)  return; }
8468d02015-06-16Martin Karlgren  object(Stdio.File)|object(SSL.File) f = Stdio.File();
ea50e61999-05-01Henrik Grubbström (Grubba) 
23f42f1999-10-28Henrik Grubbström (Grubba)  // FIXME: Race-condition: open_socket() for other connections will fail // until the socket has been connected.
ea50e61999-05-01Henrik Grubbström (Grubba)  object privs;
82c5f42017-11-23Stefan Wallström #ifndef FTP2_USE_ANY_SOURCE_PORT
6cacde2001-10-18Martin Stjernholm  if(local_port-1 < 1024 && geteuid())
ea50e61999-05-01Henrik Grubbström (Grubba)  privs = Privs("FTP: Opening the data connection on " + local_addr + ":" + (local_port-1) + "."); if(!f->open_socket(local_port-1, local_addr)) { privs = 0;
17e2b22001-06-15Johan Sundström  DWRITE("FTP: socket(%d, %O) failed. Trying with any port.\n", local_port-1, local_addr);
abdff21999-12-27Martin Nilsson 
82c5f42017-11-23Stefan Wallström #endif
cff0e11999-10-28Henrik Grubbström (Grubba)  if(!f->open_socket(0, local_addr)) {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: socket(0, %O) failed. " "Trying with any port, any ip.\n", local_addr);
cff0e11999-10-28Henrik Grubbström (Grubba)  if (!f->open_socket()) { DWRITE("FTP: socket() failed. Out of sockets?\n");
280ffa1999-10-29Henrik Grubbström (Grubba)  fun(0, 0, @args);
cff0e11999-10-28Henrik Grubbström (Grubba)  destruct(f); return; }
ad72bd1997-04-09Marcus Comstedt  }
82c5f42017-11-23Stefan Wallström #ifndef FTP2_USE_ANY_SOURCE_PORT
ad72bd1997-04-09Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba)  privs = 0;
82c5f42017-11-23Stefan Wallström #endif
ea50e61999-05-01Henrik Grubbström (Grubba) 
f2591f2003-10-21Henrik Grubbström (Grubba)  Stdio.File raw_connection = f;
280ffa1999-10-29Henrik Grubbström (Grubba)  f->set_nonblocking(lambda(mixed ignored, string data) { DWRITE("FTP: async_connect ok. Got data.\n");
fc41e42004-02-24Martin Stjernholm  f->set_nonblocking(0,0,0,0,0);
280ffa1999-10-29Henrik Grubbström (Grubba)  fun(f, data, @args); }, lambda(mixed ignored) { DWRITE("FTP: async_connect ok.\n");
fc41e42004-02-24Martin Stjernholm  f->set_nonblocking(0,0,0,0,0);
280ffa1999-10-29Henrik Grubbström (Grubba)  fun(f, "", @args); }, lambda(mixed ignored) {
cf7afc2014-10-02Henrik Grubbström (Grubba)  DWRITE("FTP: connect_and_send failed: %s (%d)\n", strerror(f->errno()), f->errno());
280ffa1999-10-29Henrik Grubbström (Grubba)  destruct(f); fun(0, 0, @args);
fc41e42004-02-24Martin Stjernholm  }, lambda(mixed ignored) {
cf7afc2014-10-02Henrik Grubbström (Grubba)  DWRITE("FTP: connect_and_send failed (oob): %s (%d)\n", strerror(f->errno()), f->errno());
fc41e42004-02-24Martin Stjernholm  destruct(f); fun(0, 0, @args);
280ffa1999-10-29Henrik Grubbström (Grubba)  });
ea50e61999-05-01Henrik Grubbström (Grubba)  #ifdef FD_DEBUG
f2591f2003-10-21Henrik Grubbström (Grubba)  mark_fd(raw_connection->query_fd(), sprintf("ftp communication: %s:%d -> %s:%d", local_addr, local_port - 1, dataport_addr, dataport_port));
ea50e61999-05-01Henrik Grubbström (Grubba) #endif
c56b172014-09-30Henrik Grubbström (Grubba)  if(mixed err = catch{
ed0fc42010-05-27Henrik Grubbström (Grubba)  if (!(raw_connection->connect(dataport_addr, dataport_port))) { DWRITE("FTP: connect(%O, %O) failed with: %s!\n" "FTP: local_addr: %O:%O (%O)\n", dataport_addr, dataport_port, strerror(raw_connection->errno()),
73fcb22014-10-01Henrik Grubbström (Grubba)  local_addr, local_port-1, raw_connection->is_open() && raw_connection->query_address(1));
ed0fc42010-05-27Henrik Grubbström (Grubba)  destruct(f); fun(0, 0, @args); return; } }) {
c56b172014-09-30Henrik Grubbström (Grubba)  DWRITE("FTP: Illegal IP address (%s:%d) in async connect.\n", dataport_addr||"", dataport_port); DWRITE("FTP: %s\n", describe_backtrace(err));
ea50e61999-05-01Henrik Grubbström (Grubba)  destruct(f);
280ffa1999-10-29Henrik Grubbström (Grubba)  fun(0, 0, @args);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; }
ad72bd1997-04-09Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba)  /* * Data connection handling */
e44f2a2014-08-11Henrik Grubbström (Grubba)  enum SSLMode { SSL_NONE = 0, SSL_ACTIVE = 1, SSL_PASSIVE = 2, SSL_ALL = 3, }; // Set to SSL_ALL by PROT S,E and P. // Cleared by PROT C. // Set to SSL_ACTIVE by AUTH SSL. SSLMode use_ssl;
fc40392008-08-15Martin Stjernholm  private void send_done_callback(array(object) args)
ad72bd1997-04-09Marcus Comstedt  {
ea50e61999-05-01Henrik Grubbström (Grubba)  DWRITE("FTP: send_done_callback()\n");
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  object fd = args[0]; object session = args[1];
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  if(fd) {
f2591f2003-10-21Henrik Grubbström (Grubba)  //DWRITE("FTP: fd: %O: %O\n", fd, mkmapping(indices(fd), values(fd)));
ea50e61999-05-01Henrik Grubbström (Grubba)  if (fd->set_blocking) { fd->set_blocking(); // Force close() to flush any buffers. }
f2591f2003-10-21Henrik Grubbström (Grubba)  call_out(fd->close, 0); fd = 0; //BACKEND_CLOSE(fd);
ea50e61999-05-01Henrik Grubbström (Grubba)  } curr_pipe = 0;
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (session && session->file) { session->conf->log(session->file, session); session->file = 0; }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
7de2602017-12-05Henrik Grubbström (Grubba)  send(226, ({ LOCALE(85, "Transfer complete.") }));
ad72bd1997-04-09Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private mapping|array|object stat_file(string fname, object|void session)
ad72bd1997-04-09Marcus Comstedt  {
ea50e61999-05-01Henrik Grubbström (Grubba)  mapping file;
d07ce92004-02-24Henrik Grubbström (Grubba)  session = RequestID2(session || master_session); session->method = "STAT";
ea50e61999-05-01Henrik Grubbström (Grubba)  session->not_query = fname;
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  foreach(conf->first_modules(), function funp) { if ((file = funp(session))) {
ad72bd1997-04-09Marcus Comstedt  break; } }
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!file) {
c0f4542001-02-19Jonas Wallden  fname = replace(fname, "//", "/");
d07ce92004-02-24Henrik Grubbström (Grubba)  file = conf->stat_file(fname, session);
b134ed1997-06-12Marcus Comstedt  }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session); return file;
ea50e61999-05-01Henrik Grubbström (Grubba)  }
2408161997-05-07Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private int expect_argument(string cmd, string args)
b1fca01996-11-12Per Hedbor  {
ea50e61999-05-01Henrik Grubbström (Grubba)  if ((< "", 0 >)[args]) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(86, "Syntax: %s %s"), cmd, cmd_help[cmd]) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return 0;
6ae0d91997-03-13Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  return 1;
b1fca01996-11-12Per Hedbor  }
2408161997-05-07Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private void send_error(string cmd, string f, mapping file, object session)
ea50e61999-05-01Henrik Grubbström (Grubba)  { switch(file && file->error) { case 301: case 302: if (file->extra_heads && file->extra_heads->Location) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(87, "'%s': %s: Redirect to %O."),
ea50e61999-05-01Henrik Grubbström (Grubba)  cmd, f, file->extra_heads->Location) })); } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(88, "'%s': %s: Redirect."), cmd, f) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } break;
8b094e1997-05-26Henrik Grubbström (Grubba)  case 401:
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ sprintf(LOCALE(89, "'%s': %s: Access denied."),
ea50e61999-05-01Henrik Grubbström (Grubba)  cmd, f) }));
8b094e1997-05-26Henrik Grubbström (Grubba)  break;
7eeda02002-12-17Henrik Grubbström (Grubba)  case 403:
7de2602017-12-05Henrik Grubbström (Grubba)  send(451, ({ sprintf(LOCALE(90, "'%s': %s: Forbidden."),
7eeda02002-12-17Henrik Grubbström (Grubba)  cmd, f) })); break;
ad72bd1997-04-09Marcus Comstedt  case 405:
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(91, "'%s': %s: Method not allowed."), cmd, f) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  break; case 500:
7de2602017-12-05Henrik Grubbström (Grubba)  send(451, ({ sprintf(LOCALE(93, "'%s': Requested action aborted: " "local error in processing."), cmd) }));
ad72bd1997-04-09Marcus Comstedt  break; default:
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!file) { file = ([ "error":404 ]); }
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(94, "'%s': %s: No such file or directory."),
ea50e61999-05-01Henrik Grubbström (Grubba)  cmd, f) }));
8b094e1997-05-26Henrik Grubbström (Grubba)  break;
ad72bd1997-04-09Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(file, session);
3d7af31998-04-03Johan Schön  }
fc40392008-08-15Martin Stjernholm  private mapping open_file(string fname, object session, string cmd)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
97b9102000-09-13Henrik Grubbström (Grubba)  object|array|mapping file;
47352e1997-08-13Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  file = stat_file(fname, session);
abdff21999-12-27Martin Nilsson 
f92a6e2004-05-17Martin Stjernholm  // The caller is assumed to have made a new session object for us // but not to set not_query in it.. session->not_query = fname;
97b9102000-09-13Henrik Grubbström (Grubba)  if (objectp(file) || arrayp(file)) { array|object st = file;
ea50e61999-05-01Henrik Grubbström (Grubba)  file = 0;
cf1f182001-09-11Henrik Grubbström (Grubba)  if (st && (st[1] < 0) && !((<"RMD", "XRMD", "CHMOD">)[cmd])) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(95, "%s: not a plain file."), fname) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return 0; } mixed err; if ((err = catch(file = conf->get_file(session)))) {
ba093e1999-12-06Martin Stjernholm  report_error("FTP: Error opening file \"%s\"\n" "%s\n", fname, describe_backtrace(err));
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(96, "%s: Error, can't open file."), fname) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return 0; }
cf1f182001-09-11Henrik Grubbström (Grubba)  } else if ((< "STOR", "APPE", "MKD", "XMKD", "MOVE" >)[cmd]) {
ea50e61999-05-01Henrik Grubbström (Grubba)  mixed err; if ((err = catch(file = conf->get_file(session)))) {
ba093e1999-12-06Martin Stjernholm  report_error("FTP: Error opening file \"%s\"\n" "%s\n", fname, describe_backtrace(err));
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(96, "%s: Error, can't open file."), fname) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return 0; } }
7a87c71997-09-03Henrik Grubbström (Grubba) 
3dad632006-12-08Henrik Grubbström (Grubba)  // file is a mapping.
ea50e61999-05-01Henrik Grubbström (Grubba)  session->file = file;
a2a7191997-10-03Henrik Grubbström (Grubba) 
5608a41999-05-19Henrik Grubbström (Grubba)  if (!file || (file->error && (file->error >= 300))) {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: open_file(\"%s\") failed: %O\n", fname, file);
ea50e61999-05-01Henrik Grubbström (Grubba)  send_error(cmd, fname, file, session); return 0; }
980b331998-03-23Henrik Grubbström (Grubba) 
f4ecd82003-02-05Jonas Wallden  // If data is a wide string we flatten it according to the charset // preferences in the current ID object. if (file->data && String.width(file->data) > 8) file->data = session->output_encode(file->data, 0)[1];
ea50e61999-05-01Henrik Grubbström (Grubba)  file->full_path = fname; file->request_start = time(1);
c175b91998-03-21Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!file->len) { if (file->data) { file->len = sizeof(file->data); } if (objectp(file->file)) { file->len += file->file->stat()[1];
2159231998-03-23Henrik Grubbström (Grubba)  } }
3dad632006-12-08Henrik Grubbström (Grubba)  return file;
7c8fd51998-03-23Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private void connected_to_send(object fd, string ignored, mapping file, object session)
b1fca01996-11-12Per Hedbor  {
ed0fc42010-05-27Henrik Grubbström (Grubba)  DWRITE("FTP: connected_to_send(%O, %O, %O, X)\n", fd, ignored, file);
ea50e61999-05-01Henrik Grubbström (Grubba)  touch_me();
382e652017-12-05Henrik Grubbström (Grubba)  restore_locale();
ea50e61999-05-01Henrik Grubbström (Grubba)  if(!file->len) file->len = file->data?(stringp(file->data)?strlen(file->data):0):0;
df67041998-02-15Mattias Wingstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!file->mode) { file->mode = mode; }
2159231998-03-23Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if(fd) { if (file->len) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(150, ({ sprintf(LOCALE(97, "Opening %s data connection for %s (%d bytes)."),
ea50e61999-05-01Henrik Grubbström (Grubba)  modes[file->mode], file->full_path, file->len) })); } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(150, ({ sprintf(LOCALE(98, "Opening %s mode data connection for %s"),
ea50e61999-05-01Henrik Grubbström (Grubba)  modes[file->mode], file->full_path) }));
879c1a1998-05-08Henrik Grubbström (Grubba)  }
e44f2a2014-08-11Henrik Grubbström (Grubba)  SSLMode ssl_mask = SSL_ACTIVE; if (pasv_port) ssl_mask = SSL_PASSIVE;
42f81a2015-06-17Henrik Grubbström (Grubba) #if constant(SSL.File)
e44f2a2014-08-11Henrik Grubbström (Grubba)  if (use_ssl & ssl_mask) { DWRITE("FTP: Initiating SSL/TLS connection.\n"); // RFC 4217 7: // For i) and ii), the FTP client MUST be the TLS client and the FTP // server MUST be the TLS server. // // That is to say, it does not matter which side initiates the // connection with a connect() call or which side reacts to the // connection via the accept() call; the FTP client, as defined in // [RFC-959], is always the TLS client, as defined in [RFC-2246]. fd = SSL.File(fd, port_obj->ctx); fd->accept(); DWRITE("FTP: Created an sslfile: %O\n", fd); }
42f81a2015-06-17Henrik Grubbström (Grubba) #endif
879c1a1998-05-08Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor  else
ea50e61999-05-01Henrik Grubbström (Grubba)  {
7de2602017-12-05Henrik Grubbström (Grubba)  send(425, ({ LOCALE(99, "Can't build data connect: Connection refused.") }));
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return;
74ee781997-08-12Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  switch(file->mode) { case "A": if (file->data) {
889ac41999-06-27Henrik Grubbström (Grubba)  file->data = replace(file->data, ({ "\r\n", "\n", "\r" }), ({ "\r\n", "\r\n", "\r\n" }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } if(objectp(file->file) && file->file->set_nonblocking) { // The list_stream object doesn't support nonblocking I/O, // but converts to ASCII anyway, so we don't have to do // anything about it.
280ffa1999-10-29Henrik Grubbström (Grubba)  file->file = ToAsciiWrapper(file->file, 0, this_object());
b1fca01996-11-12Per Hedbor  } break;
ea50e61999-05-01Henrik Grubbström (Grubba)  case "E": // EBCDIC handling here.
ca56381999-05-01Henrik Grubbström (Grubba)  if (file->data) {
50aa162015-04-28Jonas Walldén  Charset.Encoder conv = Charset.encoder("EBCDIC-US", "");
ca56381999-05-01Henrik Grubbström (Grubba)  file->data = conv->feed(file->data)->drain(); } if(objectp(file->file) && file->file->set_nonblocking) { // The list_stream object doesn't support nonblocking I/O, // but converts to ASCII anyway, so we don't have to do // anything about it.
b1ca391999-10-29Henrik Grubbström (Grubba)  // But EBCDIC doen't work...
280ffa1999-10-29Henrik Grubbström (Grubba)  file->file = ToEBCDICWrapper(file->file, 0, this_object());
ca56381999-05-01Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  break; default: // "I" and "L" // Binary -- no conversion needed. if (objectp(file->file) && file->file->set_nonblocking) {
280ffa1999-10-29Henrik Grubbström (Grubba)  file->file = BinaryWrapper(file->file, 0, this_object());
b1fca01996-11-12Per Hedbor  } break;
ea50e61999-05-01Henrik Grubbström (Grubba)  }
c3e98f1999-05-01Henrik Grubbström (Grubba) 
df4fbf1999-10-10Francesco Chemolli #ifndef DISABLE_FTP_THROTTLING mapping throttle=session->throttle||([]); object pipe;
abdff21999-12-27Martin Nilsson  if ( conf &&
df4fbf1999-10-10Francesco Chemolli  ((throttle->doit && conf->query("req_throttle")) || conf->throttler ) ) {
f72bc92000-02-04Per Hedbor // report_debug("ftp: using slowpipe\n");
df4fbf1999-10-10Francesco Chemolli  pipe=((program)"slowpipe")(); } else {
f72bc92000-02-04Per Hedbor // report_debug("ftp: using fastpipe\n");
df4fbf1999-10-10Francesco Chemolli  pipe=((program)"fastpipe")(); //will use Stdio.sendfile if possible throttle->doit=0; } if (throttle->doit) { throttle->rate=max(throttle->rate, conf->query("req_throttle_min")); pipe->throttle(throttle->rate, (int)(throttle->rate* conf->query("req_throttle_depth_mult")), 0); } if (conf && conf->throttler) { //we are sure to be using slowpipe pipe->assign_throttler(conf->throttler); } #else object pipe=((program)"fastpipe")(); #endif
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  pipe->set_done_callback(send_done_callback, ({ fd, session }) ); master_session->file = session->file = file; if(stringp(file->data)) { pipe->write(file->data); } if(file->file) { file->file->set_blocking();
afc8932000-02-14Jonas Wallden  pipe->input(file->file, file->len);
ea50e61999-05-01Henrik Grubbström (Grubba)  } curr_pipe = pipe; pipe->output(fd); }
b1fca01996-11-12Per Hedbor 
fc40392008-08-15Martin Stjernholm  private void connected_to_receive(object fd, string data, string args)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: connected_to_receive(X, %O, %O)\n", data, args);
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  touch_me();
2159231998-03-23Henrik Grubbström (Grubba) 
382e652017-12-05Henrik Grubbström (Grubba)  restore_locale();
ea50e61999-05-01Henrik Grubbström (Grubba)  if (fd) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(150, ({ sprintf(LOCALE(100, "Opening %s mode data connection for %s."),
ea50e61999-05-01Henrik Grubbström (Grubba)  modes[mode], args) }));
e44f2a2014-08-11Henrik Grubbström (Grubba)  SSLMode ssl_mask = SSL_ACTIVE; if (pasv_port) ssl_mask = SSL_PASSIVE;
42f81a2015-06-17Henrik Grubbström (Grubba) #if constant(SSL.File)
e44f2a2014-08-11Henrik Grubbström (Grubba)  if (use_ssl & ssl_mask) { DWRITE("FTP: Initiating SSL/TLS connection.\n"); fd = SSL.File(fd, port_obj->ctx); fd->accept(); DWRITE("FTP: Created an sslfile: %O\n", fd); }
42f81a2015-06-17Henrik Grubbström (Grubba) #endif
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(425, ({ LOCALE(99, "Can't build data connect: Connection refused.") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return; }
f352562000-09-24Henrik Grubbström (Grubba)  data = data && sizeof(data) && data;
ea50e61999-05-01Henrik Grubbström (Grubba)  switch(mode) { case "A":
280ffa1999-10-29Henrik Grubbström (Grubba)  fd = FromAsciiWrapper(fd, data, this_object());
74ee781997-08-12Henrik Grubbström (Grubba)  break;
ea50e61999-05-01Henrik Grubbström (Grubba)  case "E":
280ffa1999-10-29Henrik Grubbström (Grubba)  fd = FromEBCDICWrapper(fd, data, this_object());
ea50e61999-05-01Henrik Grubbström (Grubba)  return; default: // "I" and "L" // Binary, no need to do anything.
280ffa1999-10-29Henrik Grubbström (Grubba)  fd = BinaryWrapper(fd, data, this_object());
74ee781997-08-12Henrik Grubbström (Grubba)  break;
ea50e61999-05-01Henrik Grubbström (Grubba)  }
5babd11997-04-29Henrik Grubbström (Grubba) 
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "PUT"; session->my_fd = PutFileWrapper(fd, session, this_object()); session->misc->len = 0x7fffffff;
974ffc1997-03-11Henrik Grubbström (Grubba) 
3dad632006-12-08Henrik Grubbström (Grubba)  mapping file; if (file = open_file(args, session, "STOR")) { if (!(file->pipe)) {
ea50e61999-05-01Henrik Grubbström (Grubba)  if (fd) { BACKEND_CLOSE(fd);
b1fca01996-11-12Per Hedbor  }
3dad632006-12-08Henrik Grubbström (Grubba)  switch(file->error) {
ea50e61999-05-01Henrik Grubbström (Grubba)  case 401:
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ sprintf(LOCALE(101, "%s: Need account for storing files."), args)}));
ea50e61999-05-01Henrik Grubbström (Grubba)  break; case 413:
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(102, "%s: Quota exceeded."), args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  break; case 501:
7de2602017-12-05Henrik Grubbström (Grubba)  send(502, ({ sprintf(LOCALE(103, "%s: Command not implemented."), args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  break; default:
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(104, "%s: Error opening file."), args) }));
c175b91998-03-21Henrik Grubbström (Grubba)  break; }
3dad632006-12-08Henrik Grubbström (Grubba)  session->conf->log(file, session);
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return;
9c83eb1997-08-25Henrik Grubbström (Grubba)  }
3dad632006-12-08Henrik Grubbström (Grubba)  master_session->file = file;
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { // Error message has already been sent. if (fd) { BACKEND_CLOSE(fd);
b1fca01996-11-12Per Hedbor  }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } }
b1fca01996-11-12Per Hedbor 
fc40392008-08-15Martin Stjernholm  private void discard_data_connection() {
7cd6882001-03-05Stefan Wallström  if(pasv_port && sizeof(pasv_accepted)) pasv_accepted = pasv_accepted[1..]; }
fc40392008-08-15Martin Stjernholm  private void connect_and_send(mapping file, object session)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: connect_and_send(%O)\n", file);
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (pasv_port) { ftp_async_accept(connected_to_send, file, session); } else { ftp_async_connect(connected_to_send, file, session); } }
b1fca01996-11-12Per Hedbor 
fc40392008-08-15Martin Stjernholm  private void connect_and_receive(string arg)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: connect_and_receive(\"%s\")\n", arg);
3085aa1997-04-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (pasv_port) { ftp_async_accept(connected_to_receive, arg); } else { ftp_async_connect(connected_to_receive, arg); } }
3085aa1997-04-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  /* * Command-line simulation */
6ae0d91997-03-13Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  // NOTE: base is modified destructably! array(string) my_combine_path_array(array(string) base, string part) { if ((part == ".") || (part == "")) { if ((part == "") && (!sizeof(base))) { return(({""}));
6ae0d91997-03-13Henrik Grubbström (Grubba)  } else {
ea50e61999-05-01Henrik Grubbström (Grubba)  return(base);
b1fca01996-11-12Per Hedbor  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else if ((part == "..") && sizeof(base) && (base[-1] != "..") && (base[-1] != "")) { base[-1] = part; return(base); } else { return(base + ({ part })); } }
4e59e61998-03-20Thomas Weber 
fc40392008-08-15Martin Stjernholm  private string my_combine_path(string base, string part)
ea50e61999-05-01Henrik Grubbström (Grubba)  { if ((sizeof(part) && (part[0] == '/')) || (sizeof(base) && (base[0] == '/'))) { return(combine_path(base, part)); } // Combine two relative paths. int i; array(string) arr = ((base/"/") + (part/"/")) - ({ ".", "" }); foreach(arr, string part) { if ((part == "..") && i && (arr[i-1] != "..")) { i--; } else { arr[i++] = part;
4e59e61998-03-20Thomas Weber  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } if (i) { return(arr[..i-1]*"/"); } else { return(""); } }
74ee781997-08-12Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private constant IFS = (<" ", "\t">); private constant Quote = (< "\'", "\"", "\`", "\\" >); private constant Specials = IFS|Quote;
c467861999-08-08Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private array(string) split_command_line(string cmdline)
c467861999-08-08Henrik Grubbström (Grubba)  { // Check if we need to handle quoting at all... int need_quoting; foreach(indices(Quote), string c) { if (need_quoting = (search(cmdline, c) >= 0)) { break; } } if (!need_quoting) { // The easy case... return ((replace(cmdline, "\t", " ")/" ") - ({ "" })); } array(string) res = ({}); string arg = 0; int argstart = 0; int i; for(i=0; i < sizeof(cmdline); i++) { string c; if (Specials[c = cmdline[i..i]]) { if (argstart < i) { arg = (arg || "") + cmdline[argstart..i-1]; } switch(c) { case "\"": case "\'": case "\`": // NOTE: We handle all of the above as \'. int j = search(cmdline, c, i+1); if (j == -1) { // No endquote! // Simulate one at EOL. j = sizeof(cmdline); } arg = (arg || "") + cmdline[i+1..j-1]; i = j; break; case "\\": i++; arg += cmdline[i..i]; break; case " ": case "\t": // IFS if (arg) { res += ({ arg }); arg = 0; } break; } argstart = i+1; } } if (argstart < i) { arg = (arg || "") + cmdline[argstart..]; } if (arg) { res += ({ arg }); } return res; }
fc40392008-08-15Martin Stjernholm  private array(string) glob_expand_command_line(string cmdline)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("glob_expand_command_line(\"%s\")\n", cmdline);
74ee781997-08-12Henrik Grubbström (Grubba) 
c467861999-08-08Henrik Grubbström (Grubba)  array(string|array(string)) args = split_command_line(cmdline);
5babd11997-04-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  int index;
5babd11997-04-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  for(index = 0; index < sizeof(args); index++) {
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  // Glob-expand args[index]
c175b91998-03-21Henrik Grubbström (Grubba) 
c467861999-08-08Henrik Grubbström (Grubba)  // FIXME: Does not check if "*" or "?" was quoted!
ea50e61999-05-01Henrik Grubbström (Grubba)  if (replace(args[index], ({"*", "?"}), ({ "", "" })) != args[index]) {
74ee781997-08-12Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  // Globs in the file-name. array(string|array(string)) matches = ({ ({ }) }); multiset(string) paths; // Used to filter out duplicates. int i; foreach(my_combine_path("", args[index])/"/", string part) { paths = (<>); if (replace(part, ({"*", "?"}), ({ "", "" })) != part) { // Got a glob. array(array(string)) new_matches = ({}); foreach(matches, array(string) path) { array(string) dir;
8025352001-01-22Henrik Grubbström (Grubba)  RequestID id = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  id->method = "LIST"; dir = id->conf->find_dir(combine_path(cwd, path*"/")+"/", id); if (dir && sizeof(dir)) { dir = glob(part, dir); if ((< '*', '?' >)[part[0]]) { // Glob-expanding does not expand to files starting with '.' dir = Array.filter(dir, lambda(string f) { return (sizeof(f) && (f[0] != '.')); }); } foreach(sort(dir), string f) { array(string) arr = my_combine_path_array(path, f); string p = arr*"/"; if (!paths[p]) { paths[p] = 1; new_matches += ({ arr }); } } }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(id);
ea50e61999-05-01Henrik Grubbström (Grubba)  } matches = new_matches; } else { // No glob // Just add the part. Modify matches in-place. for(i=0; i<sizeof(matches); i++) { matches[i] = my_combine_path_array(matches[i], part); string path = matches[i]*"/"; if (paths[path]) { matches[i] = 0; } else { paths[path] = 1; } } matches -= ({ 0 }); } if (!sizeof(matches)) { break; } } if (sizeof(matches)) { // Array => string for (i=0; i < sizeof(matches); i++) { matches[i] *= "/"; } // Filter out non-existing or forbiden files/directories matches = Array.filter(matches, lambda(string short, string cwd, object m_id) { object id = RequestID2(m_id); id->method = "LIST"; id->not_query = combine_path(cwd, short);
d07ce92004-02-24Henrik Grubbström (Grubba)  mapping res = id->conf->stat_file(id->not_query, id); destruct(id); return res;
ea50e61999-05-01Henrik Grubbström (Grubba)  }, cwd, master_session); if (sizeof(matches)) { args[index] = matches; } } } if (stringp(args[index])) { // No glob args[index] = ({ my_combine_path("", args[index]) });
b1fca01996-11-12Per Hedbor  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } return(args * ({})); }
5babd11997-04-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  /* * LS handling */
fc40392008-08-15Martin Stjernholm  private constant ls_options = ({
ea50e61999-05-01Henrik Grubbström (Grubba)  ({ ({ "-A", "--almost-all" }), LS_FLAG_A, "do not list implied . and .." }), ({ ({ "-a", "--all" }), LS_FLAG_a|LS_FLAG_A, "do not hide entries starting with ." }), ({ ({ "-b", "--escape" }), LS_FLAG_b, "print octal escapes for nongraphic characters" }), ({ ({ "-C" }), LS_FLAG_C, "list entries by columns" }), ({ ({ "-d", "--directory" }), LS_FLAG_d, "list directory entries instead of contents" }), ({ ({ "-F", "--classify" }), LS_FLAG_F, "append a character for typing each entry"}), ({ ({ "-f" }), LS_FLAG_a|LS_FLAG_A|LS_FLAG_U, "do not sort, enable -aU, disable -lst" }), ({ ({ "-G", "--no-group" }), LS_FLAG_G, "inhibit display of group information" }), ({ ({ "-g" }), 0, "(ignored)" }), ({ ({ "-h", "--help" }), LS_FLAG_h, "display this help and exit" }), ({ ({ "-k", "--kilobytes" }), 0, "use 1024 blocks (ignored, always done anyway)" }), ({ ({ "-L", "--dereference" }), 0, "(ignored)" }), ({ ({ "-l" }), LS_FLAG_l, "use a long listing format" }), ({ ({ "-m" }), LS_FLAG_m, "fill width with a comma separated list of entries" }), ({ ({ "-n", "--numeric-uid-gid" }), LS_FLAG_n, "list numeric UIDs and GIDs instead of names" }), ({ ({ "-o" }), LS_FLAG_l|LS_FLAG_G, "use long listing format without group info" }), ({ ({ "-Q", "--quote-name" }), LS_FLAG_Q, "enclose entry names in double quotes" }), ({ ({ "-R", "--recursive" }), LS_FLAG_R, "list subdirectories recursively" }), ({ ({ "-r", "--reverse" }), LS_FLAG_r, "reverse order while sorting" }), ({ ({ "-S" }), LS_FLAG_S, "sort by file size" }), ({ ({ "-s", "--size" }), LS_FLAG_s, "print block size of each file" }), ({ ({ "-t" }), LS_FLAG_t, "sort by modification time; with -l: show mtime" }), ({ ({ "-U" }), LS_FLAG_U, "do not sort; list entries in directory order" }), ({ ({ "-v", "--version" }), LS_FLAG_v, "output version information and exit" }), });
5babd11997-04-29Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private array(array(string)|string|int)
ea50e61999-05-01Henrik Grubbström (Grubba)  ls_getopt_args = Array.map(ls_options, lambda(array(array(string)|int|string) entry) { return({ entry[1], Getopt.NO_ARG, entry[0] }); });
fc40392008-08-15Martin Stjernholm  private string ls_help(string ls)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  return sprintf("Usage: %s [OPTION]... [FILE]...\n"
ea50e61999-05-01Henrik Grubbström (Grubba)  "List information about the FILEs " "(the current directory by default).\n" "Sort entries alphabetically if none " "of -cftuSUX nor --sort.\n" "\n" "%@s\n", ls, Array.map(ls_options, lambda(array entry) { if (sizeof(entry[0]) > 1) { return(sprintf(" %s, %-22s %s\n", @(entry[0]), entry[2])); } return(sprintf(" %s " " %s\n", entry[0][0], entry[2]));
17e2b22001-06-15Johan Sundström  }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } void call_ls(array(string) argv) { /* Parse options */ array options; mixed err; if (err = catch { options = Getopt.find_all_options(argv, ls_getopt_args, 1, 1); }) { send(550, (argv[0]+": "+err[0])/"\n");
7cd6882001-03-05Stefan Wallström  discard_data_connection();
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } int flags; foreach(options, array(int) option) { flags |= option[0]; } if (err = catch { argv = Getopt.get_args(argv, 1, 1); }) { send(550, (argv[0] + ": " + err[0])/"\n");
7cd6882001-03-05Stefan Wallström  discard_data_connection();
ea50e61999-05-01Henrik Grubbström (Grubba)  return; }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (sizeof(argv) == 1) { argv += ({ "./" }); }
b610c91997-08-29Henrik Grubbström (Grubba) 
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "LIST"; // For logging purposes... session->not_query = Array.map(argv[1..], fix_path)*" ";
b610c91997-08-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  mapping file = ([]);
b610c91997-08-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  // The listings returned by a LIST or NLST command SHOULD use an // implied TYPE AN, unless the current type is EBCDIC, in which // case an implied TYPE EN SHOULD be used. // RFC 1123 4.1.2.7 if (mode != "E") { file->mode = "A"; } else { file->mode = "E"; }
74ee781997-08-12Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (flags & LS_FLAG_v) { file->data = "ls - builtin_ls 1.1\n"; } else if (flags & LS_FLAG_h) { file->data = ls_help(argv[0]); } else { if (flags & LS_FLAG_d) { flags &= ~LS_FLAG_R;
b1fca01996-11-12Per Hedbor  }
ea50e61999-05-01Henrik Grubbström (Grubba)  if (flags & (LS_FLAG_f|LS_FLAG_C|LS_FLAG_m)) { flags &= ~LS_FLAG_l; } if (flags & LS_FLAG_C) { flags &= ~LS_FLAG_m; } file->file = LSFile(cwd, argv[1..], flags, session, file->mode, this_object());
23bfc52014-08-07Henrik Grubbström (Grubba)  #ifdef FTP_USE_HANDLER_THREADS // Create the listing synchronously when running in // a handler thread. file->file && file->file->fill_output_queue(); #endif
ea50e61999-05-01Henrik Grubbström (Grubba)  }
5babd11997-04-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!file->full_path) { file->full_path = argv[0]; } session->file = file; connect_and_send(file, session); }
5babd11997-04-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  /* * Listings for Machine Processing */
74ee781997-08-12Henrik Grubbström (Grubba) 
72ed252017-11-20Henrik Grubbström (Grubba)  constant supported_mlst_facts = (<
0edb1b2017-11-20Henrik Grubbström (Grubba)  "size", "type", "modify", "charset", "media-type",
16c7f12017-11-20Henrik Grubbström (Grubba)  "unix.mode", "unix.atime", "unix.ctime", "unix.uid", "unix.gid",
062d972017-11-22Henrik Grubbström (Grubba)  "unix.ownername", "unix.groupname",
72ed252017-11-20Henrik Grubbström (Grubba)  >); multiset(string) current_mlst_facts = (<
062d972017-11-22Henrik Grubbström (Grubba)  "size", "type", "modify", "unix.mode", "unix.ownername", "unix.groupname",
72ed252017-11-20Henrik Grubbström (Grubba)  >); protected string format_factlist(multiset(string) all, multiset(string)|void selected) { if (!selected) selected = (<>); string ret = ""; foreach(sort(indices(all)), string fact) { ret += fact; if (selected[fact]) { ret += "*"; } ret += ";"; } return ret; }
ea50e61999-05-01Henrik Grubbström (Grubba)  string make_MDTM(int t) {
c2849d2002-02-13Henrik Grubbström (Grubba)  mapping lt = gmtime(t);
17e2b22001-06-15Johan Sundström  return sprintf("%04d%02d%02d%02d%02d%02d",
ea50e61999-05-01Henrik Grubbström (Grubba)  lt->year + 1900, lt->mon + 1, lt->mday,
17e2b22001-06-15Johan Sundström  lt->hour, lt->min, lt->sec);
ea50e61999-05-01Henrik Grubbström (Grubba)  }
5babd11997-04-29Henrik Grubbström (Grubba) 
78e15b2017-11-23Henrik Grubbström (Grubba)  mapping(string:string) make_MLSD_facts(string f, mapping(string:array) dir, object session)
ea50e61999-05-01Henrik Grubbström (Grubba)  { array st = dir[f];
5babd11997-04-29Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  mapping(string:string) facts = ([]);
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  // Construct the facts here.
74ee781997-08-12Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (st[1] >= 0) { facts->size = (string)st[1];
0643b22017-11-20Henrik Grubbström (Grubba)  facts->type = "file";
7472db2017-11-20Henrik Grubbström (Grubba)  if (current_mlst_facts["media-type"]) { string|array(string) ct = session->conf->type_from_filename(f); if (arrayp(ct)) { ct = (sizeof(ct) > 1) && ct[1]; } facts["media-type"] = ct || "application/octet-stream";
c6f0732014-08-11Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
bdd9362017-11-23Henrik Grubbström (Grubba)  if (!current_mlst_facts->type) { if ((< "..", "." >)[f]) { // RFC 3659 7.5.1.2: // // The type=cdir fact indicates the listed entry contains a // pathname of the directory whose contents are listed. An // entry of this type will only be returned as a part of the // result of an MLSD command when the type fact is included, // and provides a name for the listed directory, and facts // about that directory. In a sense, it can be viewed as // representing the title of the listing, in a machine // friendly format. It may appear at any point of the // listing, it is not restricted to appearing at the start, // though frequently may do so, and may occur multiple // times. It MUST NOT be included if the type fact is not // included, or there would be no way for the user-PI to // distinguish the name of the directory from an entry in // the directory. return 0; } } else { facts->type = ([ "..":"pdir", ".":"cdir" ])[f] || "dir"; }
ea50e61999-05-01Henrik Grubbström (Grubba)  }
b134ed1997-06-12Marcus Comstedt 
95086b2017-11-20Henrik Grubbström (Grubba)  facts->modify = make_MDTM(st[3]); /* mtime */
b134ed1997-06-12Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  facts->charset = "8bit";
b134ed1997-06-12Marcus Comstedt 
95086b2017-11-20Henrik Grubbström (Grubba)  // FIXME: Consider adding support for the "unique" fact. // Typically based on dev-no + inode-no. // FIXME: Consider adding support for the "perm" fact.
c175b91998-03-21Henrik Grubbström (Grubba) 
16c7f12017-11-20Henrik Grubbström (Grubba)  // Facts from // https://www.iana.org/assignments/os-specific-parameters/os-specific-parameters.xml facts["unix.atime"] = make_MDTM(st[2]); /* atime */ facts["unix.ctime"] = make_MDTM(st[4]); /* ctime */
062d972017-11-22Henrik Grubbström (Grubba)  // NOTE: SiteBuilder may set st[5] and st[6] to strings. if (stringp(st[5])) { facts["unix.ownername"] = st[5]; } else { facts["unix.ownername"] = name_from_uid(master_session, st[5]); } if (stringp(st[6])) { facts["unix.groupname"] = st[6]; } else if (!st[6]) { facts["unix.groupname"] = "wheel"; } else { facts["unix.groupname"] = (string)st[6]; }
95086b2017-11-20Henrik Grubbström (Grubba) 
5ef3aa2017-11-20Henrik Grubbström (Grubba)  // Defacto standard facts here. // Cf eg https://github.com/giampaolo/pyftpdlib facts["unix.mode"] = sprintf("0%o", st[0]); /* mode */
41dbdd2017-11-22Henrik Grubbström (Grubba)  if (intp(st[5])) { facts["unix.uid"] = sprintf("%d", st[5]); /* uid */ } if (intp(st[6])) { facts["unix.gid"] = sprintf("%d", st[6]); /* gid */ }
c175b91998-03-21Henrik Grubbström (Grubba) 
78e15b2017-11-23Henrik Grubbström (Grubba)  // filter and return the answer. return facts & current_mlst_facts; } string format_MLSD_facts(mapping(string:string) facts) {
bdd9362017-11-23Henrik Grubbström (Grubba)  if (!facts) return 0;
78e15b2017-11-23Henrik Grubbström (Grubba)  return map(sort(indices(facts)), lambda(string s, mapping f) { return s + "=" + f[s] + ";"; }, facts) * ""; }
eb91412017-11-20Henrik Grubbström (Grubba) 
78e15b2017-11-23Henrik Grubbström (Grubba)  string make_MLSD_fact(string f, mapping(string:array) dir, object session) { string facts = format_MLSD_facts(make_MLSD_facts(f, dir, session));
bdd9362017-11-23Henrik Grubbström (Grubba)  if (!facts) return 0;
78e15b2017-11-23Henrik Grubbström (Grubba)  return facts + " " + f;
ea50e61999-05-01Henrik Grubbström (Grubba)  }
2d3ff51997-04-10Marcus Comstedt 
78e15b2017-11-23Henrik Grubbström (Grubba)  void send_MLSD_response(mapping(string:array) dir, object session)
ea50e61999-05-01Henrik Grubbström (Grubba)  { dir = dir || ([]);
bdd9362017-11-23Henrik Grubbström (Grubba)  array(string) entries = Array.map(indices(dir), make_MLSD_fact, dir, session) - ({ 0 });
ea50e61999-05-01Henrik Grubbström (Grubba) 
bdd9362017-11-23Henrik Grubbström (Grubba)  session->file->data = sizeof(entries) ? entries * "\r\n" + "\r\n" : "" ;
ea50e61999-05-01Henrik Grubbström (Grubba)  session->file->mode = "I"; connect_and_send(session->file, session); }
78e15b2017-11-23Henrik Grubbström (Grubba)  void send_MLST_response(mapping(string:array) dir, object session)
7a8c5b2001-03-03Henrik Grubbström (Grubba)  { dir = dir || ([]);
bdd9362017-11-23Henrik Grubbström (Grubba)  // NB: MLST expands "." and "..", so make_MLSD_fact() won't // return zero here.
7de2602017-12-05Henrik Grubbström (Grubba)  send(250,({ LOCALE(105, "OK") }) +
78e15b2017-11-23Henrik Grubbström (Grubba)  Array.map(indices(dir), make_MLSD_fact, dir, session) +
7de2602017-12-05Henrik Grubbström (Grubba)  ({ LOCALE(105, "OK") }) );
7a8c5b2001-03-03Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  /*
c8a3ca2000-09-18Henrik Grubbström (Grubba)  * Session handling
ea50e61999-05-01Henrik Grubbström (Grubba)  */
c8a3ca2000-09-18Henrik Grubbström (Grubba)  int login()
ea50e61999-05-01Henrik Grubbström (Grubba)  {
c8a3ca2000-09-18Henrik Grubbström (Grubba)  int session_limit = port_obj->query_option("ftp_user_session_limit"); if (session_limit > 0) { if (session_limit <= (port_obj->ftp_sessions[user])) { return 0; // Session limit reached. } if (logged_in) { report_error("Internal error in session-handler."); return 1; }
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: Increasing # of sessions for user %O\n", user);
c8a3ca2000-09-18Henrik Grubbström (Grubba)  port_obj->ftp_sessions[user]++; } logged_in = (user != 0) || -1; return 1; } void logout() { if (!logged_in) return; int session_limit = port_obj->query_option("ftp_user_session_limit"); if (session_limit > 0) {
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: Decreasing # of sessions for user %O\n", user);
c8a3ca2000-09-18Henrik Grubbström (Grubba)  if ((--port_obj->ftp_sessions[user]) < 0) { port_obj->ftp_sessions[user] = 0; } } logged_in = 0; } int check_login() { int session_limit = port_obj->query_option("ftp_user_session_limit"); if (session_limit <= 0) return 1; if (session_limit <= (port_obj->ftp_sessions[user])) { return 0; // Session limit reached.
ea50e61999-05-01Henrik Grubbström (Grubba)  }
c8a3ca2000-09-18Henrik Grubbström (Grubba)  return 1; } /* * FTP commands begin here */
3163752001-03-13Henrik Grubbström (Grubba)  // Set to 1 by EPSV ALL.
ac291c2001-03-13Henrik Grubbström (Grubba)  int epsv_only;
3163752001-03-13Henrik Grubbström (Grubba) 
c8a3ca2000-09-18Henrik Grubbström (Grubba)  void ftp_REIN(string|int args) { logout();
3163752001-03-13Henrik Grubbström (Grubba)  // FIXME: What about EPSV ALL mode? RFC 2428 doesn't say. // I guess that it shouldn't be reset.
41cfa32001-09-10Henrik Grubbström (Grubba)  // Compatibility... m_delete(master_session->misc, "home");
ea50e61999-05-01Henrik Grubbström (Grubba)  dataport_addr = 0; dataport_port = 0; mode = "A"; cwd = "/";
8025352001-01-22Henrik Grubbström (Grubba)  auth_user = 0;
ea50e61999-05-01Henrik Grubbström (Grubba)  user = password = 0; curr_pipe = 0; restart_point = 0; logged_in = 0;
679af82017-12-06Henrik Grubbström (Grubba)  roxen.set_locale(); m_delete(master_session->misc, "accept-language"); master_session->misc->pref_languages->languages = ({});
ea50e61999-05-01Henrik Grubbström (Grubba)  if (pasv_port) { destruct(pasv_port); pasv_port = 0; }
80e1a72014-08-08Henrik Grubbström (Grubba)  if (args != 1) {
6baab42014-08-08Henrik Grubbström (Grubba)  // Not called by QUIT or AUTH.
7de2602017-12-05Henrik Grubbström (Grubba)  low_send(220, ({ LOCALE(106, "Server ready for new user.") }));
80e1a72014-08-08Henrik Grubbström (Grubba)  // RFC 4217 13: // When this command is processed by the server, the TLS // session(s) MUST be cleared and the control and data // connections revert to unprotected, clear communications. to_send->put(2); // End TLS marker.
e44f2a2014-08-11Henrik Grubbström (Grubba)  use_ssl = SSL_NONE;
80e1a72014-08-08Henrik Grubbström (Grubba)  busy = 0; next_cmd();
ea50e61999-05-01Henrik Grubbström (Grubba)  } }
6baab42014-08-08Henrik Grubbström (Grubba)  void ftp_AUTH(string args) { if (!expect_argument("AUTH", args)) return; args = upper_case(replace(args, ({ " ", "\t" }), ({ "", "" }))); // RFC 4217 17: // To request the TLS protocol in accordance with this document, // the client MUST use 'TLS' // // To maintain backward compatibility with older versions of this // document, the server SHOULD accept 'TLS-C' as a synonym for 'TLS'.
e44f2a2014-08-11Henrik Grubbström (Grubba)  if (!(< "TLS", "SSL", "SSL-C", "TLS-C", "SSL-P", "TLS-P" >)[args]) {
6baab42014-08-08Henrik Grubbström (Grubba)  // RFC 2228 AUTH: // If the server does not understand the named security mechanism, it // should respond with reply code 504.
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ LOCALE(107, "Unknown authentication mechanism.") }));
6baab42014-08-08Henrik Grubbström (Grubba)  return; }
15b8a42014-08-11Henrik Grubbström (Grubba)  if ((port_obj->query_option("require_starttls") < 0) || !port_obj->ctx) {
6baab42014-08-08Henrik Grubbström (Grubba)  // RFC 2228 AUTH: // If the server is not willing to accept the named security // mechanism, it should respond with reply code 534.
7de2602017-12-05Henrik Grubbström (Grubba)  send(534, ({ LOCALE(108, "TLS not configured.") }));
6baab42014-08-08Henrik Grubbström (Grubba)  return; } // RFC 2228 AUTH: // The AUTH command, if accepted, removes any state associated with // prior FTP Security commands. The server must also require that the // user reauthorize (that is, reissue some or all of the USER, PASS, // and ACCT commands) in this case (see section 4 for an explanation // of "authorize" in this context). // // RFC 4217 4.2 requires REIN.
80e1a72014-08-08Henrik Grubbström (Grubba)  ftp_REIN(1);
6baab42014-08-08Henrik Grubbström (Grubba) 
d883632015-05-13Henrik Grubbström (Grubba)  // Inform the client that we agree to switch to TLS.
7de2602017-12-05Henrik Grubbström (Grubba)  low_send(234, ({ LOCALE(109, "TLS enabled.") }));
d883632015-05-13Henrik Grubbström (Grubba)  // Make sure not to read any more from the fd before // the TLS handshaking is done. fd->set_read_callback(0); fd->set_close_callback(0); // Switch to TLS marker. to_send->put(1);
6baab42014-08-08Henrik Grubbström (Grubba) 
e44f2a2014-08-11Henrik Grubbström (Grubba)  // Compatibility with early draft-murray-auth-ftp-ssl // (drafts of RFC 4217). if (args == "TLS-P") { // AUTH TLS-P: Enable PROT P by default. use_ssl = SSL_ALL; } else if ((args == "SSL") || (args == "SSL-P")) { // AUTH SSL: Enable PROT P by default in active mode. // // Use SSL/TLS for the data connection in active mode but // not in passive mode. This behaviour probably has to do // with the server initiating the connection in passive mode, // which would imply it to be the SSL/TLS client. // // cf RFC 4217 (AUTH TLS) which solves this by having // the server being the SSL/TLS server in both modes. use_ssl = SSL_ACTIVE; } // NB: AUTH SSL-C is "Don't encrypt data channel".
6baab42014-08-08Henrik Grubbström (Grubba)  busy = 0; next_cmd(); }
48834b2014-08-11Henrik Grubbström (Grubba)  void ftp_CCC(string args) { if (!fd->renegotiate) { // Not AUTH TLS
7de2602017-12-05Henrik Grubbström (Grubba)  send(533, ({ LOCALE(110, "Command connection not protected.") }));
48834b2014-08-11Henrik Grubbström (Grubba)  return; } if (master_session->my_fd->renegotiate) { // ftps
7de2602017-12-05Henrik Grubbström (Grubba)  send(534, ({ LOCALE(111, "Not allowed for ftps.") }));
48834b2014-08-11Henrik Grubbström (Grubba)  return; }
7de2602017-12-05Henrik Grubbström (Grubba)  low_send(200, ({ LOCALE(112, "TLS disabled.") }));
48834b2014-08-11Henrik Grubbström (Grubba)  to_send->put(2); // Disable TLS marker. busy = 0; next_cmd(); }
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_USER(string args) {
c8a3ca2000-09-18Henrik Grubbström (Grubba)  logout();
8025352001-01-22Henrik Grubbström (Grubba)  auth_user = 0;
ea50e61999-05-01Henrik Grubbström (Grubba)  user = args; password = 0; logged_in = 0; cwd = "/"; master_session->method = "LOGIN";
be7a1a2014-08-12Henrik Grubbström (Grubba)  if ((< 0, "", "ftp", "anonymous" >)[user]) {
ea50e61999-05-01Henrik Grubbström (Grubba)  master_session->not_query = "Anonymous"; user = 0;
b0132d1999-10-08Henrik Grubbström (Grubba)  if (port_obj->query_option("anonymous_ftp")) {
c8a3ca2000-09-18Henrik Grubbström (Grubba)  if (check_login()) {
ea50e61999-05-01Henrik Grubbström (Grubba) #if 0
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ LOCALE(113, "Anonymous ftp, at your service") }));
ea50e61999-05-01Henrik Grubbström (Grubba) #else /* !0 */
c8a3ca2000-09-18Henrik Grubbström (Grubba)  // ncftp doesn't like the above answer -- stupid program!
7de2602017-12-05Henrik Grubbström (Grubba)  send(331, ({ LOCALE(114, "Anonymous ftp accepted, send " "your complete e-mail address as password.") }));
ea50e61999-05-01Henrik Grubbström (Grubba) #endif /* 0 */
c8a3ca2000-09-18Henrik Grubbström (Grubba)  conf->log(([ "error":200 ]), master_session); } else { send(530, ({
7de2602017-12-05Henrik Grubbström (Grubba)  sprintf(LOCALE(115, "Too many anonymous users (%d)."), port_obj->query_option("ftp_user_session_limit"))
c8a3ca2000-09-18Henrik Grubbström (Grubba)  })); conf->log(([ "error":403 ]), master_session); }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ LOCALE(116, "Anonymous ftp disabled") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  conf->log(([ "error":403 ]), master_session);
abfd7d1997-06-12Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
be7a1a2014-08-12Henrik Grubbström (Grubba)  if (port_obj->ctx && !fd->renegotiate && (port_obj->query_option("require_starttls") == 1)) { conf->log(([ "error":403 ]), master_session);
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ LOCALE(117, "You need to AUTH TLS first.") }));
be7a1a2014-08-12Henrik Grubbström (Grubba)  return; }
c8a3ca2000-09-18Henrik Grubbström (Grubba)  if (check_login()) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(331, ({ sprintf(LOCALE(118, "Password required for %s."), user) }));
c8a3ca2000-09-18Henrik Grubbström (Grubba)  master_session->not_query = user; conf->log(([ "error":407 ]), master_session); } else { // Session limit exceeded. send(530, ({
7de2602017-12-05Henrik Grubbström (Grubba)  sprintf(LOCALE(119, "Concurrent session limit (%d) exceeded " "for user \"%s\"."),
c8a3ca2000-09-18Henrik Grubbström (Grubba)  port_obj->query_option("ftp_user_session_limit"), user) })); conf->log(([ "error":403 ]), master_session); user = 0; return;
abfd7d1997-06-12Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } }
c175b91998-03-21Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_PASS(string args) { if (!user) {
b0132d1999-10-08Henrik Grubbström (Grubba)  if (port_obj->query_option("anonymous_ftp")) {
c8a3ca2000-09-18Henrik Grubbström (Grubba)  if (login()) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(230, ({ LOCALE(120, "Guest login ok, access restrictions apply.") }));
c8a3ca2000-09-18Henrik Grubbström (Grubba)  master_session->method = "LOGIN"; master_session->not_query = "Anonymous User:"+args; conf->log(([ "error":200 ]), master_session); logged_in = -1; } else { send(530, ({
7de2602017-12-05Henrik Grubbström (Grubba)  sprintf(LOCALE(115, "Too many anonymous users (%d)."), port_obj->query_option("ftp_user_session_limit"))
c8a3ca2000-09-18Henrik Grubbström (Grubba)  })); conf->log(([ "error":403 ]), master_session); }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(503, ({ LOCALE(121, "Login with USER first.") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } return; }
3bcd3b1997-05-07Henrik Grubbström (Grubba) 
be7a1a2014-08-12Henrik Grubbström (Grubba)  if (port_obj->ctx && !fd->renegotiate && (port_obj->query_option("require_starttls") == 1)) { // NB: Reachable through the following exotic command sequence: // // AUTH TLS, USER, PASS, CCC, PASS conf->log(([ "error":403 ]), master_session);
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ LOCALE(117, "You need to AUTH TLS first.") }));
be7a1a2014-08-12Henrik Grubbström (Grubba)  return; }
c8a3ca2000-09-18Henrik Grubbström (Grubba)  logout();
ea50e61999-05-01Henrik Grubbström (Grubba)  password = args||""; args = "CENSORED_PASSWORD"; // Censored in case of backtrace. master_session->method = "LOGIN"; master_session->realauth = user + ":" + password; master_session->not_query = user;
cf4a902001-01-19Per Hedbor  master_session->misc->user = user; // Loophole for new API master_session->misc->password = password; // Otherwise we have to emulate // the Authentication header
cc83812001-09-10Henrik Grubbström (Grubba)  // Compatibility... m_delete(master_session->misc, "home");
cf4a902001-01-19Per Hedbor 
7831fa2006-12-20Martin Stjernholm  RequestID2 session = RequestID2 (master_session);
ea50e61999-05-01Henrik Grubbström (Grubba) 
7831fa2006-12-20Martin Stjernholm  auth_user = session->conf->authenticate(session);
e9bf332006-12-04Martin Stjernholm 
8025352001-01-22Henrik Grubbström (Grubba)  if (!auth_user) {
b0132d1999-10-08Henrik Grubbström (Grubba)  if (!port_obj->query_option("guest_ftp")) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ sprintf(LOCALE(122, "User %s access denied."), user) }));
7831fa2006-12-20Martin Stjernholm  conf->log(([ "error":401 ]), session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
c8a3ca2000-09-18Henrik Grubbström (Grubba)  // Guest user. string u = user; user = 0; if (login()) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(230, ({ sprintf(LOCALE(123, "Guest user %s logged in."), u) }));
c8a3ca2000-09-18Henrik Grubbström (Grubba)  logged_in = -1;
7831fa2006-12-20Martin Stjernholm  conf->log(([ "error":200 ]), session); DWRITE("FTP: Guest-user: %O\n", session->realauth);
c8a3ca2000-09-18Henrik Grubbström (Grubba)  } else { send(530, ({
7de2602017-12-05Henrik Grubbström (Grubba)  sprintf(LOCALE(124, "Too many anonymous/guest users (%d)."), port_obj->query_option("ftp_user_session_limit"))
c8a3ca2000-09-18Henrik Grubbström (Grubba)  }));
7831fa2006-12-20Martin Stjernholm  conf->log(([ "error":403 ]), session);
c8a3ca2000-09-18Henrik Grubbström (Grubba)  }
3bcd3b1997-05-07Henrik Grubbström (Grubba)  }
7831fa2006-12-20Martin Stjernholm  destruct (session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; }
0026281999-06-07Martin Stjernholm  // Authentication successful
ea50e61999-05-01Henrik Grubbström (Grubba) 
7831fa2006-12-20Martin Stjernholm  // Transfer entries traditionally set by auth modules in id->misc
cb7a872006-12-20Martin Stjernholm  // so that they get propagated to id->misc in all subsequent // subrequests.
7831fa2006-12-20Martin Stjernholm  // // We can't copy the whole misc mapping to the master RequestID; // that can cause various stuff set during the auth check to be // around for too long - the lifespan of id->misc must generally
cb7a872006-12-20Martin Stjernholm  // not be longer than a single (http style) request. { mapping ses_misc = session->misc, mses_misc = master_session->misc; foreach (({"authenticated_user", "user", "password", "uid", "gid", "gecos", "home", "shell"}), string field) { mixed val = ses_misc[field]; if (zero_type (val)) m_delete (mses_misc, field); else mses_misc[field] = val; } }
7831fa2006-12-20Martin Stjernholm 
b0132d1999-10-08Henrik Grubbström (Grubba)  if (!port_obj->query_option("named_ftp") ||
cc83812001-09-10Henrik Grubbström (Grubba)  !check_shell(auth_user->shell())) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ LOCALE(125, "You are not allowed to use named-ftp."), LOCALE(126, "Try using anonymous, or check /etc/shells") }));
7831fa2006-12-20Martin Stjernholm  conf->log(([ "error":402 ]), session);
8025352001-01-22Henrik Grubbström (Grubba)  auth_user = 0;
7831fa2006-12-20Martin Stjernholm  destruct (session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; }
c8a3ca2000-09-18Henrik Grubbström (Grubba)  if (!login()) { send(530, ({
7de2602017-12-05Henrik Grubbström (Grubba)  sprintf(LOCALE(127, "Too many concurrent sessions (limit is %d)."),
c8a3ca2000-09-18Henrik Grubbström (Grubba)  port_obj->query_option("ftp_user_session_limit")) }));
7831fa2006-12-20Martin Stjernholm  conf->log(([ "error":403 ]), session); destruct (session);
c8a3ca2000-09-18Henrik Grubbström (Grubba)  return; }
cc83812001-09-10Henrik Grubbström (Grubba)  if (stringp(auth_user->homedir())) {
ea50e61999-05-01Henrik Grubbström (Grubba)  // Check if it is possible to cd to the users home-directory.
cc83812001-09-10Henrik Grubbström (Grubba)  string home = auth_user->homedir(); if ((home == "") || (home[-1] != '/')) { home += "/";
2408161997-05-07Henrik Grubbström (Grubba)  }
cc83812001-09-10Henrik Grubbström (Grubba)  // Compatibility... master_session->misc->home = home;
7831fa2006-12-20Martin Stjernholm  RequestID2 stat_session = RequestID2(master_session); stat_session->method = "STAT"; array(int)|object st = conf->stat_file(home, stat_session); destruct(stat_session);
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (st && (st[1] < 0)) {
cc83812001-09-10Henrik Grubbström (Grubba)  cwd = home;
ea50e61999-05-01Henrik Grubbström (Grubba)  }
b1fca01996-11-12Per Hedbor  }
7831fa2006-12-20Martin Stjernholm 
ea50e61999-05-01Henrik Grubbström (Grubba)  logged_in = 1;
7de2602017-12-05Henrik Grubbström (Grubba)  send(230, ({ sprintf(LOCALE(128, "User %s logged in."), user) }));
7831fa2006-12-20Martin Stjernholm  conf->log(([ "error":202 ]), session); destruct (session);
b1fca01996-11-12Per Hedbor  }
980b331998-03-23Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_CWD(string args) { if (!expect_argument("CWD", args)) { return; }
980b331998-03-23Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  string ncwd = fix_path(args);
a2a7191997-10-03Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if ((ncwd == "") || (ncwd[-1] != '/')) { ncwd += "/"; }
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  object session = RequestID2(master_session); session->method = "CWD"; session->not_query = ncwd;
acd03a1997-08-28Henrik Grubbström (Grubba) 
97b9102000-09-13Henrik Grubbström (Grubba)  array|object st = conf->stat_file(ncwd, session);
ea50e61999-05-01Henrik Grubbström (Grubba)  ncwd = session->not_query; // Makes internal redirects to work. if (!st) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(129, "%s: No such file or directory, or access denied."),
ea50e61999-05-01Henrik Grubbström (Grubba)  ncwd) })); session->conf->log(session->file || ([ "error":404 ]), session);
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } if (!(< -2, -3 >)[st[1]]) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(130, "%s: Not a directory."), ncwd) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(([ "error":400 ]), session);
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } // CWD Successfull cwd = ncwd;
7de2602017-12-05Henrik Grubbström (Grubba)  array(string) reply = ({ sprintf(LOCALE(131, "Current directory is now %s."), cwd) });
ea50e61999-05-01Henrik Grubbström (Grubba)  // Check for .messages etc session->method = "GET"; // Important array(string) files = conf->find_dir(cwd, session); if (files) { files = reverse(sort(Array.filter(files, lambda(string s) { return(s[..5] == "README"); }))); foreach(files, string f) {
c0f4542001-02-19Jonas Wallden  array|object st = conf->stat_file(replace(cwd + f, "//", "/"), session);
ea50e61999-05-01Henrik Grubbström (Grubba)  if (st && (st[1] >= 0)) {
7de2602017-12-05Henrik Grubbström (Grubba)  reply = ({ sprintf(LOCALE(132, "Please read the file %s."), f), sprintf(LOCALE(133, "It was last modified %s - %d days ago."),
ea50e61999-05-01Henrik Grubbström (Grubba)  ctime(st[3]) - "\n", (time(1) - st[3])/86400), "" }) + reply; } } }
7cd6882001-03-05Stefan Wallström  string message; catch { message = conf->try_get_file(cwd + ".message", session); };
ea50e61999-05-01Henrik Grubbström (Grubba)  if (message) { reply = (message/"\n")+({ "" })+reply; } session->method = "CWD"; // Restore it again. send(250, reply); session->conf->log(([ "error":200, "len":sizeof(reply*"\n") ]), session);
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_XCWD(string args) { ftp_CWD(args);
acd03a1997-08-28Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_CDUP(string args) { ftp_CWD("../");
7a87c71997-09-03Henrik Grubbström (Grubba)  }
acd03a1997-08-28Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_XCUP(string args)
14179b1997-01-29Per Hedbor  {
ea50e61999-05-01Henrik Grubbström (Grubba)  ftp_CWD("../"); } void ftp_QUIT(string args) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(221, ({ LOCALE(134, "Bye! It was nice talking to you!") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  send(0, 0); // EOF marker. master_session->method = "QUIT"; master_session->not_query = user || "Anonymous"; conf->log(([ "error":200 ]), master_session); // Reinitialize the connection. ftp_REIN(1); } void ftp_BYE(string args) { ftp_QUIT(args); }
f2591f2003-10-21Henrik Grubbström (Grubba)  void ftp_PBSZ(string args) {
7043862014-08-08Henrik Grubbström (Grubba)  if (!expect_argument("PBSZ", args)) return;
f2591f2003-10-21Henrik Grubbström (Grubba) 
48834b2014-08-11Henrik Grubbström (Grubba)  if (!fd->renegotiate) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(536, ({ LOCALE(135, "Only allowed for authenticated command connections.") }));
48834b2014-08-11Henrik Grubbström (Grubba)  return; }
f2591f2003-10-21Henrik Grubbström (Grubba)  send(200, ({ "PBSZ=0" })); } void ftp_PROT(string args) { if (!expect_argument("PROT", args)) return; args = upper_case(replace(args, ({ " ", "\t" }), ({ "", "" })));
48834b2014-08-11Henrik Grubbström (Grubba)  SSLMode wanted;
f2591f2003-10-21Henrik Grubbström (Grubba)  switch(args) { case "C": // Clear.
48834b2014-08-11Henrik Grubbström (Grubba)  wanted = SSL_NONE;
f2591f2003-10-21Henrik Grubbström (Grubba)  break; case "S": // Safe. case "E": // Confidential. case "P": // Private.
48834b2014-08-11Henrik Grubbström (Grubba)  wanted = SSL_ALL;
f2591f2003-10-21Henrik Grubbström (Grubba)  break; default:
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(136, "Unknown protection level: %s"), args) }));
f2591f2003-10-21Henrik Grubbström (Grubba)  return; }
48834b2014-08-11Henrik Grubbström (Grubba)  if (!fd->renegotiate) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(536, ({ LOCALE(137, "Only supported over TLS.") }));
48834b2014-08-11Henrik Grubbström (Grubba)  return; } use_ssl = wanted;
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ LOCALE(105, "OK") }));
f2591f2003-10-21Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_PORT(string args) {
3163752001-03-13Henrik Grubbström (Grubba)  if (epsv_only) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ LOCALE(138, "'PORT': Method not allowed in EPSV ALL mode.") }));
3163752001-03-13Henrik Grubbström (Grubba)  return; }
ea50e61999-05-01Henrik Grubbström (Grubba)  int a, b, c, d, e, f;
abdff21999-12-27Martin Nilsson  if (sscanf(args||"", "%d,%d,%d,%d,%d,%d", a, b, c, d, e, f)<6)
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ LOCALE(139, "I don't understand your parameters.") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  else { dataport_addr = sprintf("%d.%d.%d.%d", a, b, c, d); dataport_port = e*256 + f; if (pasv_port) { destruct(pasv_port); }
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ sprintf(LOCALE(140, "PORT command ok (%s port %d)"), dataport_addr, dataport_port) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } }
3163752001-03-13Henrik Grubbström (Grubba)  void ftp_EPRT(string args) {
a632c12005-04-26Henrik Grubbström (Grubba)  // Specified by RFC 2428: // Extensions for IPv6 and NATs.
3163752001-03-13Henrik Grubbström (Grubba)  if (epsv_only) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ LOCALE(141, "'EPRT': Method not allowed in EPSV ALL mode.") }));
3163752001-03-13Henrik Grubbström (Grubba)  return; } if (sizeof(args) < 3) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ LOCALE(139, "I don't understand your parameters.") }));
3163752001-03-13Henrik Grubbström (Grubba)  return; } string delimiter = args[0..0];
b820052001-03-13Martin Stjernholm  if ((delimiter[0] <= 32) || (delimiter[0] >= 127)) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ LOCALE(142, "Invalid delimiter.") }));
3163752001-03-13Henrik Grubbström (Grubba)  } array(string) segments = args/delimiter;
fdb9c52009-03-06Henrik Grubbström (Grubba)  if (sizeof(segments) != 5) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ LOCALE(139, "I don't understand your parameters.") }));
3163752001-03-13Henrik Grubbström (Grubba)  return; }
a632c12005-04-26Henrik Grubbström (Grubba)  if (!(<"1","2">)[segments[1]]) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(522, ({ LOCALE(143, "Network protocol not supported, use (1 or 2)") }));
3163752001-03-13Henrik Grubbström (Grubba)  return; }
a632c12005-04-26Henrik Grubbström (Grubba)  if (segments[1] == "1") { // IPv4. if ((sizeof(segments[2]/".") != 4) || sizeof(replace(segments[2], ".0123456789"/"", allocate(11, "")))) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ sprintf(LOCALE(144, "Bad IPv4 address: '%s'"), segments[2]) }));
a632c12005-04-26Henrik Grubbström (Grubba)  return; } } else { // IPv6. // FIXME: Improve the validation? if (sizeof(replace(lower_case(segments[2]), ".:0123456789abcdef"/"", allocate(18, "")))) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ sprintf(LOCALE(145, "Bad IPv6 address: '%s'"), segments[2]) }));
a632c12005-04-26Henrik Grubbström (Grubba)  return; }
3163752001-03-13Henrik Grubbström (Grubba)  }
a632c12005-04-26Henrik Grubbström (Grubba)  if ((((int)segments[3]) <= 0) || (((int)segments[3]) > 65535)) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ sprintf(LOCALE(146, "Bad port number: '%s'"), segments[3]) }));
3163752001-03-13Henrik Grubbström (Grubba)  return; }
8191832001-03-13Martin Stjernholm  dataport_addr = segments[2]; dataport_port = (int)segments[3];
3163752001-03-13Henrik Grubbström (Grubba)  if (pasv_port) { destruct(pasv_port); }
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ sprintf(LOCALE(147, "EPRT command ok (%d port %d)"), dataport_addr, dataport_port) }));
3163752001-03-13Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_PASV(string args) { // Required by RFC 1123 4.1.2.6
65e6f32001-04-14Werner Koch  int min; int max;
ea50e61999-05-01Henrik Grubbström (Grubba) 
3163752001-03-13Henrik Grubbström (Grubba)  if (epsv_only) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ LOCALE(148, "'PASV': Method not allowed in EPSV ALL mode.") }));
3163752001-03-13Henrik Grubbström (Grubba)  return; }
38cb8a2009-03-06Henrik Grubbström (Grubba)  if (e_mode != "1") {
7de2602017-12-05Henrik Grubbström (Grubba)  send(530, ({ LOCALE(149, "'PASV': Method not allowed on IPv6 connections.") }));
38cb8a2009-03-06Henrik Grubbström (Grubba)  return; }
ea50e61999-05-01Henrik Grubbström (Grubba)  if(pasv_port) destruct(pasv_port);
65e6f32001-04-14Werner Koch 
ea50e61999-05-01Henrik Grubbström (Grubba)  pasv_port = Stdio.Port(0, pasv_accept_callback, local_addr); /* FIXME: Hmm, getting the address from an anonymous port seems not * to work on NT... */ int port=(int)((pasv_port->query_address()/" ")[1]);
65e6f32001-04-14Werner Koch  min = port_obj->query_option("passive_port_min"); max = port_obj->query_option("passive_port_max"); if ((port < min) || (port > max)) { if (max > 65535) max = 65535; if (min < 0) min = 0; for (port = min; port <= max; port++) { if (pasv_port->bind(port, pasv_accept_callback, local_addr)) { break; } } if (port > max) { destruct(pasv_port); pasv_port = 0;
7de2602017-12-05Henrik Grubbström (Grubba)  send(452, ({ LOCALE(150, "Requested action aborted: Out of ports.") }));
65e6f32001-04-14Werner Koch  return; } }
7de2602017-12-05Henrik Grubbström (Grubba)  send(227, ({ LOCALE(151, "Entering Passive Mode.") + sprintf(" (%s,%d,%d)",
ea50e61999-05-01Henrik Grubbström (Grubba)  replace(local_addr, ".", ","), (port>>8), (port&0xff)) })); }
3163752001-03-13Henrik Grubbström (Grubba)  void ftp_EPSV(string args) { // Specified by RFC 2428: // Extensions for IPv6 and NATs.
00e0bd2001-04-24Henrik Grubbström (Grubba)  int min; int max;
3163752001-03-13Henrik Grubbström (Grubba) 
38cb8a2009-03-06Henrik Grubbström (Grubba)  if (!(< 0, e_mode >)[args]) {
3163752001-03-13Henrik Grubbström (Grubba)  if (lower_case(args) == "all") { epsv_only = 1;
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ LOCALE(152, "Entering EPSV ALL mode.") }));
3163752001-03-13Henrik Grubbström (Grubba)  } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(522, ({ sprintf(LOCALE(153, "Network protocol not supported, use %s."), e_mode) }));
3163752001-03-13Henrik Grubbström (Grubba)  } return; } if (pasv_port) destruct(pasv_port);
f2591f2003-10-21Henrik Grubbström (Grubba) 
3163752001-03-13Henrik Grubbström (Grubba)  pasv_port = Stdio.Port(0, pasv_accept_callback, local_addr); /* FIXME: Hmm, getting the address from an anonymous port seems not * to work on NT... */ int port=(int)((pasv_port->query_address()/" ")[1]);
65e6f32001-04-14Werner Koch  min = port_obj->query_option("passive_port_min"); max = port_obj->query_option("passive_port_max"); if ((port < min) || (port > max)) { if (max > 65535) max = 65535;
a632c12005-04-26Henrik Grubbström (Grubba)  if (min < 1) min = 1;
65e6f32001-04-14Werner Koch  for (port = min; port <= max; port++) { if (pasv_port->bind(port, pasv_accept_callback, local_addr)) { break; } } if (port > max) { destruct(pasv_port); pasv_port = 0;
7de2602017-12-05Henrik Grubbström (Grubba)  send(452, ({ LOCALE(150, "Requested action aborted: Out of ports.") }));
65e6f32001-04-14Werner Koch  return; } }
7de2602017-12-05Henrik Grubbström (Grubba)  send(229, ({ LOCALE(154, "Entering Extended Passive Mode") + sprintf(" (|||%d|)", /* "1", local_addr,*/ port) }));
3163752001-03-13Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_TYPE(string args) { if (!expect_argument("TYPE", args)) { return; } args = upper_case(replace(args, ({ " ", "\t" }), ({ "", "" }))); // I and L8 are required by RFC 1123 4.1.2.1 switch(args) { case "L8": case "L": case "I": mode = "I"; break; case "A": mode = "A"; break; case "E":
c9f6a31999-05-01Henrik Grubbström (Grubba)  mode = "E"; break;
ea50e61999-05-01Henrik Grubbström (Grubba)  default:
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(155, "'TYPE': Unknown type: %s"), args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return;
031f321997-08-31Henrik Grubbström (Grubba)  }
0a34b11997-03-13Henrik Grubbström (Grubba) 
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ sprintf(LOCALE(156, "Using %s mode for transferring files."),
ea50e61999-05-01Henrik Grubbström (Grubba)  modes[mode]) })); } void ftp_RETR(string args) { if (!expect_argument("RETR", args)) { return; }
0a34b11997-03-13Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  args = fix_path(args);
f7e7501997-04-08Marcus Comstedt 
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
f7e7501997-04-08Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "GET"; session->not_query = args;
2d3ff51997-04-10Marcus Comstedt 
3dad632006-12-08Henrik Grubbström (Grubba)  mapping file; if (file = open_file(args, session, "RETR")) {
ea50e61999-05-01Henrik Grubbström (Grubba)  if (restart_point) {
3dad632006-12-08Henrik Grubbström (Grubba)  if (file->data) { if (sizeof(file->data) >= restart_point) { file->data = file->data[restart_point..];
ea50e61999-05-01Henrik Grubbström (Grubba)  restart_point = 0; } else {
3dad632006-12-08Henrik Grubbström (Grubba)  restart_point -= sizeof(file->data); m_delete(file, "data");
ea50e61999-05-01Henrik Grubbström (Grubba)  } } if (restart_point) {
3dad632006-12-08Henrik Grubbström (Grubba)  if (!(file->file && file->file->seek && (file->file->seek(restart_point) != -1))) {
ea50e61999-05-01Henrik Grubbström (Grubba)  restart_point = 0;
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ LOCALE(157, "'RETR': Error restoring restart point.") }));
7cd6882001-03-05Stefan Wallström  discard_data_connection();
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } restart_point = 0; } }
3dad632006-12-08Henrik Grubbström (Grubba)  connect_and_send(file, session);
ea50e61999-05-01Henrik Grubbström (Grubba)  }
d07ce92004-02-24Henrik Grubbström (Grubba)  else {
7cd6882001-03-05Stefan Wallström  discard_data_connection();
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session); }
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_STOR(string args) { if (!expect_argument("STOR", args)) { return; } args = fix_path(args); connect_and_receive(args);
14179b1997-01-29Per Hedbor  }
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_REST(string args) { if (!expect_argument("REST", args)) { return; } restart_point = (int)args;
7de2602017-12-05Henrik Grubbström (Grubba)  send(350, ({ LOCALE(158, "'REST' ok") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_ABOR(string args) { if (curr_pipe) { catch { destruct(curr_pipe); }; curr_pipe = 0;
7de2602017-12-05Henrik Grubbström (Grubba)  send(426, ({ LOCALE(159, "Data transmission terminated.") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  }
7de2602017-12-05Henrik Grubbström (Grubba)  send(226, ({ LOCALE(160, "'ABOR' Completed.") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_PWD(string args) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(257, ({ sprintf(LOCALE(161, "\"%s\" is current directory."), cwd) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_XPWD(string args) { ftp_PWD(args); } /* * Handling of file moving */
abdff21999-12-27Martin Nilsson 
fc40392008-08-15Martin Stjernholm  private string rename_from; // rename from
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_RNFR(string args) { if (!expect_argument("RNFR", args)) { return; } args = fix_path(args);
abdff21999-12-27Martin Nilsson 
f92a6e2004-05-17Martin Stjernholm  if (stat_file(args)) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(350, ({ sprintf(LOCALE(162, "%s ok, waiting for destination name."), args) }) );
ea50e61999-05-01Henrik Grubbström (Grubba)  rename_from = args; } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(550, ({ sprintf(LOCALE(163, "%s: no such file or permission denied."),args) }) );
ea50e61999-05-01Henrik Grubbström (Grubba)  } } void ftp_RNTO(string args) { if(!rename_from) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(503, ({ LOCALE(164, "RNFR needed before RNTO.") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } if (!expect_argument("RNTO", args)) { return; } args = fix_path(args);
abdff21999-12-27Martin Nilsson 
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "MV"; session->misc->move_from = rename_from; session->not_query = args; if (open_file(args, session, "MOVE")) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(250, ({ sprintf(LOCALE(165, "%s moved to %s."), rename_from, args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(([ "error":200 ]), session); } rename_from = 0;
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_NLST(string args) { // ftp_MLST(args); return; array(string) argv = glob_expand_command_line("/usr/bin/ls " + (args||"")); call_ls(argv); } void ftp_LIST(string args) {
4cd66e2017-11-22Henrik Grubbström (Grubba) #ifdef FTP2_MLSD_KLUDGE ftp_MLSD(args); return; #endif
ea50e61999-05-01Henrik Grubbström (Grubba)  ftp_NLST("-l " + (args||"")); } void ftp_MLST(string args) {
474b052017-11-21Henrik Grubbström (Grubba)  string long = fix_path(args || ".");
ea50e61999-05-01Henrik Grubbström (Grubba) 
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "DIR";
474b052017-11-21Henrik Grubbström (Grubba)  array|object st = stat_file(long, session);
ea50e61999-05-01Henrik Grubbström (Grubba)  if (st) { session->file = ([]);
474b052017-11-21Henrik Grubbström (Grubba)  session->file->full_path = long;
78e15b2017-11-23Henrik Grubbström (Grubba)  send_MLST_response(([ long: st ]), session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { send_error("MLST", args, session->file, session); }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_MLSD(string args) { args = fix_path(args || ".");
abdff21999-12-27Martin Nilsson 
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "DIR";
97b9102000-09-13Henrik Grubbström (Grubba)  array|object st = stat_file(args, session);
ea50e61999-05-01Henrik Grubbström (Grubba)  if (st && (st[1] < 0)) { if (args[-1] != '/') { args += "/"; } session->file = ([]); session->file->full_path = args;
bc86942017-11-21Henrik Grubbström (Grubba)  mapping(string:array(mixed)) dir = session->conf->find_dir_stat(args, session) || ([]); if (args != "/") { dir[".."] = stat_file(combine_path(args,"../")); } dir["."] = stat_file(combine_path(args));
78e15b2017-11-23Henrik Grubbström (Grubba)  send_MLSD_response(dir, session);
9acb652006-08-01Henrik Grubbström (Grubba)  // NOTE: send_MLSD_response is asynchronous!
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { if (st) { session->file->error = 405; } send_error("MLSD", args, session->file, session);
7cd6882001-03-05Stefan Wallström  discard_data_connection();
9acb652006-08-01Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } }
f4a4f02017-11-20Henrik Grubbström (Grubba)  void ftp_OPTS(string args) { if ((< 0, "" >)[args]) { ftp_HELP("OPTS"); return; } array a = (args/" ") - ({ "" }); if (!sizeof(a)) { ftp_HELP("OPTS"); return; } a[0] = upper_case(a[0]); if (!opts_help[a[0]]) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(502, ({ sprintf(LOCALE(166, "Bad OPTS command: '%s'"), a[0]) }));
f4a4f02017-11-20Henrik Grubbström (Grubba)  } else if (this_object()["ftp_OPTS_"+a[0]]) { this_object()["ftp_OPTS_"+a[0]](a[1..]); } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(502, ({ sprintf(LOCALE(167, "OPTS command '%s' is not currently supported."),
f4a4f02017-11-20Henrik Grubbström (Grubba)  a[0]) })); } }
eb91412017-11-20Henrik Grubbström (Grubba)  void ftp_OPTS_MLST(array(string) args) { if (sizeof(args) != 1) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ sprintf(LOCALE(168, "'OPTS MLST %s': incorrect arguments"),
eb91412017-11-20Henrik Grubbström (Grubba)  args*" ") })); return; } multiset(string) new_mlst_facts = (<>); foreach(args[0]/";", string fact) { fact = lower_case(fact); if (!supported_mlst_facts[fact]) continue; new_mlst_facts[fact] = 1; } current_mlst_facts = new_mlst_facts; send(200, ({ sprintf("MLST OPTS %s", format_factlist(new_mlst_facts)) })); }
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_DELE(string args) { if (!expect_argument("DELE", args)) { return; } args = fix_path(args);
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba) 
e885f72010-06-29Henrik Grubbström (Grubba)  session->data = "";
ea50e61999-05-01Henrik Grubbström (Grubba)  session->misc->len = 0; session->method = "DELETE"; if (open_file(args, session, "DELE")) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(250, ({ sprintf(LOCALE(169, "%s deleted."), args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(([ "error":200 ]), session); }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_RMD(string args) { if (!expect_argument("RMD", args)) { return; } args = fix_path(args);
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba) 
e885f72010-06-29Henrik Grubbström (Grubba)  session->data = "";
ea50e61999-05-01Henrik Grubbström (Grubba)  session->misc->len = 0; session->method = "DELETE";
97b9102000-09-13Henrik Grubbström (Grubba)  array|object st = stat_file(args, session);
ea50e61999-05-01Henrik Grubbström (Grubba)  if (!st) { send_error("RMD", args, session->file, session);
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } else if (st[1] != -2) { if (st[1] == -3) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(170, "%s is a module mountpoint."), args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(([ "error":405 ]), session); } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(171, "%s is not a directory."), args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(([ "error":405 ]), session); }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } if (open_file(args, session, "RMD")) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(250, ({ sprintf(LOCALE(169, "%s deleted."), args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(([ "error":200 ]), session); }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_XRMD(string args) { ftp_RMD(args); } void ftp_MKD(string args) { if (!expect_argument("MKD", args)) { return; } args = fix_path(args);
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "MKDIR";
e885f72010-06-29Henrik Grubbström (Grubba)  session->data = "";
ea50e61999-05-01Henrik Grubbström (Grubba)  session->misc->len = 0;
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (open_file(args, session, "MKD")) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(257, ({ sprintf(LOCALE(172, "\"%s\" created."), args) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(([ "error":200 ]), session); }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_XMKD(string args) { ftp_MKD(args); }
382e652017-12-05Henrik Grubbström (Grubba)  void ftp_LANG(string args) { args = lower_case(String.trim_all_whites(args || "")); if (sizeof(args)) { if (!roxen.set_locale(args)) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(173, "Unsupported language: %s"), args) }));
382e652017-12-05Henrik Grubbström (Grubba)  return; } master_session->misc->pref_languages->languages = ({ args }); master_session->misc["accept-language"] = args; } else { roxen.set_locale(); master_session->misc->pref_languages->languages = ({}); m_delete(master_session->misc, "accept-language"); }
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ sprintf(LOCALE(174, "Language set to %s"), roxen.get_locale()) }));
382e652017-12-05Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_SYST(string args) {
e6a1712000-02-02Per Hedbor  send(215, ({ "UNIX Type: L8: Roxen Information Server"}));
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_CLNT(string args) { if (!expect_argument("CLNT", args)) { return; }
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ LOCALE(175, "Ok, gottcha!")}));
ea50e61999-05-01Henrik Grubbström (Grubba)  master_session->client = args/" " - ({ "" }); } void ftp_FEAT(string args) { array a = sort(Array.filter(indices(cmd_help), lambda(string s) { return(this_object()["ftp_"+s]); }));
6baab42014-08-08Henrik Grubbström (Grubba)  if (!port_obj->ctx) { a -= ({ "AUTH" }); }
48834b2014-08-11Henrik Grubbström (Grubba)  if (master_session->my_fd->renegotiate) { // ftps. a -= ({ "CCC" }); }
25e99d2017-12-04Henrik Grubbström (Grubba)  a += ({ "TVFS" }); // RFC 3659
a994a92017-12-04Henrik Grubbström (Grubba)  a += ({ "UTF8" }); // RFC 2640
ea50e61999-05-01Henrik Grubbström (Grubba)  a = Array.map(a, lambda(string s) { return(([ "REST":"REST STREAM",
72ed252017-11-20Henrik Grubbström (Grubba)  "MLST":sprintf("MLST %s", format_factlist(supported_mlst_facts, current_mlst_facts)),
ea50e61999-05-01Henrik Grubbström (Grubba)  "MLSD":"",
6baab42014-08-08Henrik Grubbström (Grubba)  "AUTH":"AUTH TLS",
382e652017-12-05Henrik Grubbström (Grubba)  "LANG":sprintf("LANG %s", format_langlist()),
ea50e61999-05-01Henrik Grubbström (Grubba)  ])[s] || s); }) - ({ "" });
7de2602017-12-05Henrik Grubbström (Grubba)  send(211, ({ LOCALE(176, "The following features are supported:") }) + a +
ea50e61999-05-01Henrik Grubbström (Grubba)  ({ "END" })); } void ftp_MDTM(string args) { if (!expect_argument("MDTM", args)) { return; } args = fix_path(args);
f92a6e2004-05-17Martin Stjernholm  mapping|array|object st = stat_file(args);
ea50e61999-05-01Henrik Grubbström (Grubba) 
97b9102000-09-13Henrik Grubbström (Grubba)  if (!arrayp(st) && !objectp(st)) {
f92a6e2004-05-17Martin Stjernholm  send_error("MDTM", args, st, master_session);
d07ce92004-02-24Henrik Grubbström (Grubba)  } else { send(213, ({ make_MDTM(st[3]) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } } void ftp_SIZE(string args) { if (!expect_argument("SIZE", args)) { return; } args = fix_path(args);
f92a6e2004-05-17Martin Stjernholm  mapping|array|object st = stat_file(args);
ea50e61999-05-01Henrik Grubbström (Grubba) 
97b9102000-09-13Henrik Grubbström (Grubba)  if (!arrayp(st) && !objectp(st)) {
f92a6e2004-05-17Martin Stjernholm  send_error("SIZE", args, st, master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } int size = st[1]; if (size < 0) {
f92a6e2004-05-17Martin Stjernholm  send_error("SIZE", args, ([ "error":405, ]), master_session);
6ef8a41999-06-08Henrik Grubbström (Grubba)  // size = 512;
d07ce92004-02-24Henrik Grubbström (Grubba)  } else { send(213, ({ (string)size }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } } void ftp_STAT(string args) { // According to RFC 1123 4.1.3.3, this command can be sent during // a file-transfer.
0900e22001-08-22Henrik Grubbström (Grubba)  // RFC 959 4.1.3: // The command may be sent during a file transfer (along with the // Telnet IP and Synch signals--see the Section on FTP Commands) // in which case the server will respond with the status of the // operation in progress, [...] // FIXME: This is not supported yet.
ea50e61999-05-01Henrik Grubbström (Grubba) 
0900e22001-08-22Henrik Grubbström (Grubba)  if ((< "", 0 >)[args]) { /* RFC 959 4.1.3: * If no argument is given, the server should return general * status information about the server FTP process. This * should include current values of all transfer parameters and * the status of connections. */
b6a3232010-05-26Henrik Grubbström (Grubba)  string local_addr = fd->query_address(1); if (has_value(local_addr, ":")) { // IPv6. local_addr = "[" + replace(local_addr, " ", "]:"); } else { local_addr = replace(local_addr, " ", ":"); } string remote_addr = fd->query_address(); if (has_value(remote_addr, ":")) { // IPv6. remote_addr = "[" + replace(remote_addr, " ", "]:"); } else { remote_addr = replace(remote_addr, " ", ":"); }
0900e22001-08-22Henrik Grubbström (Grubba)  send(211,
7de2602017-12-05Henrik Grubbström (Grubba)  sprintf(LOCALE(177, "%s FTP server status:\n" "Version %s\n" "Listening on %s\n" "Connected to %s\n" "Logged in %s\n" "TYPE: %s, FORM: %s; STRUcture: %s; transfer MODE: %s\n" "End of status"),
b6a3232010-05-26Henrik Grubbström (Grubba)  local_addr,
0900e22001-08-22Henrik Grubbström (Grubba)  roxen.version(), port_obj->sorted_urls * "\nListening on ",
b6a3232010-05-26Henrik Grubbström (Grubba)  remote_addr,
0900e22001-08-22Henrik Grubbström (Grubba)  user?sprintf("as %s", user):"anonymously", (["A":"ASCII", "E":"EBCDIC", "I":"IMAGE", "L":"LOCAL"]) [mode], "Non-Print", "File", "Stream" )/"\n");
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } string long = fix_path(args);
97b9102000-09-13Henrik Grubbström (Grubba)  mapping|array|object st = stat_file(long);
ea50e61999-05-01Henrik Grubbström (Grubba) 
97b9102000-09-13Henrik Grubbström (Grubba)  if (!arrayp(st) && !objectp(st)) {
f92a6e2004-05-17Martin Stjernholm  send_error("STAT", long, st, master_session);
d07ce92004-02-24Henrik Grubbström (Grubba)  } else { string s = LS_L(master_session)->ls_l(args, st);
ea50e61999-05-01Henrik Grubbström (Grubba) 
7de2602017-12-05Henrik Grubbström (Grubba)  send(213, ({ sprintf(LOCALE(178, "Status of \"%s\"."), args), @((s/"\n") - ({""})), "End of Status", }));
d07ce92004-02-24Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_NOOP(string args) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ LOCALE(179, "Nothing done ok") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_HELP(string args) { if ((< "", 0 >)[args]) { send(214, ({
7de2602017-12-05Henrik Grubbström (Grubba)  LOCALE(180, "The following commands are recognized (* =>'s unimplemented):"),
ea50e61999-05-01Henrik Grubbström (Grubba)  @(sprintf(" %#70s", sort(Array.map(indices(cmd_help), lambda(string s) { return(upper_case(s)+ (this_object()["ftp_"+s]? " ":"* ")); }))*"\n")/"\n"), @(FTP2_XTRA_HELP), })); } else { args = upper_case(args);
f5838a2017-11-20Henrik Grubbström (Grubba)  if ((args/" ")[0] == "SITE") { array(string) a = (args/" ")-({""}); if (sizeof(a) == 1) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(214, ({ LOCALE(181, "The following SITE commands are recognized:"),
f5838a2017-11-20Henrik Grubbström (Grubba)  @(sprintf(" %#70s", sort(indices(site_help))*"\n")/"\n") })); } else if (site_help[a[1]]) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(214, ({ sprintf(LOCALE(182, "Syntax: SITE %s %s"), a[1], site_help[a[1]]) }));
f5838a2017-11-20Henrik Grubbström (Grubba)  } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(183, "Unknown SITE command %s."), a[1]) }));
f5838a2017-11-20Henrik Grubbström (Grubba)  } } else if ((args/" ")[0] == "OPTS") { array(string) a = (args/" ")-({""}); if (sizeof(a) == 1) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(214, ({ LOCALE(184, "The following OPTS commands are recognized:"),
f5838a2017-11-20Henrik Grubbström (Grubba)  @(sprintf(" %#70s", sort(indices(opts_help))*"\n")/"\n") })); } else if (opts_help[a[1]]) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(214, ({ sprintf(LOCALE(185, "Syntax: OPTS %s %s"), a[1], opts_help[a[1]]) }));
f5838a2017-11-20Henrik Grubbström (Grubba)  } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(186, "Unknown OPTS command %s."), a[1]) }));
f5838a2017-11-20Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
f5838a2017-11-20Henrik Grubbström (Grubba)  if (cmd_help[args]) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(214, ({ sprintf(LOCALE(187, "Syntax: %s %s%s"), args,
f5838a2017-11-20Henrik Grubbström (Grubba)  cmd_help[args], (this_object()["ftp_"+args]? "":"; unimplemented")) })); } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(504, ({ sprintf(LOCALE(188, "Unknown command %s."), args) }));
f5838a2017-11-20Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } } } void ftp_SITE(string args) { // Extended commands. // Site specific commands are required to be part of the site command // by RFC 1123 4.1.2.8 if ((< 0, "" >)[args]) { ftp_HELP("SITE"); return; } array a = (args/" ") - ({ "" }); if (!sizeof(a)) { ftp_HELP("SITE"); return; } a[0] = upper_case(a[0]); if (!site_help[a[0]]) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(502, ({ sprintf(LOCALE(189, "Bad SITE command: '%s'"), a[0]) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } else if (this_object()["ftp_SITE_"+a[0]]) { this_object()["ftp_SITE_"+a[0]](a[1..]); } else {
7de2602017-12-05Henrik Grubbström (Grubba)  send(502, ({ sprintf(LOCALE(190, "SITE command '%s' is not currently supported."),
ea50e61999-05-01Henrik Grubbström (Grubba)  a[0]) })); } } void ftp_SITE_CHMOD(array(string) args) { if (sizeof(args) < 2) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ sprintf(LOCALE(191, "'SITE CHMOD %s': incorrect arguments"),
ea50e61999-05-01Henrik Grubbström (Grubba)  args*" ") })); return; }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  int mode; foreach(args[0] / "", string m) // We do this loop, instead of using a sscanf or cast to be able // to catch arguments which aren't an octal number like 0891. { mode *= 010; if(m[0] < '0' || m[0] > '7') { // This is not an octal number... mode = -1; break; } mode += (int)("0"+m); } if(mode == -1 || mode > 0777) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ LOCALE(192, "SITE CHMOD: mode should be between 0 and 0777") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } string fname = fix_path(args[1..]*" ");
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "CHMOD"; session->misc->mode = mode; session->not_query = fname; if (open_file(fname, session, "CHMOD")) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(250, ({ sprintf(LOCALE(193, "Changed permissions of %s to 0%o."),
ea50e61999-05-01Henrik Grubbström (Grubba)  fname, mode) })); session->conf->log(([ "error":200 ]), session); }
d07ce92004-02-24Henrik Grubbström (Grubba)  destruct(session);
ea50e61999-05-01Henrik Grubbström (Grubba)  }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_SITE_UMASK(array(string) args) { if (sizeof(args) < 1) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ sprintf(LOCALE(194, "'SITE UMASK %s': incorrect arguments"),
ea50e61999-05-01Henrik Grubbström (Grubba)  args*" ") })); return; }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  int mode; foreach(args[0] / "", string m) // We do this loop, instead of using a sscanf or cast to be able // to catch arguments which aren't an octal number like 0891. { mode *= 010; if(m[0] < '0' || m[0] > '7') { // This is not an octal number... mode = -1; break; } mode += (int)("0"+m); } if(mode == -1 || mode > 0777) {
7de2602017-12-05Henrik Grubbström (Grubba)  send(501, ({ LOCALE(195, "SITE UMASK: mode should be between 0 and 0777") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } master_session->misc->umask = mode;
7de2602017-12-05Henrik Grubbström (Grubba)  send(250, ({ sprintf(LOCALE(196, "Umask set to 0%o."), mode) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } void ftp_SITE_PRESTATE(array(string) args) { if (!sizeof(args)) { master_session->prestate = (<>);
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ LOCALE(197, "Prestate cleared") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { master_session->prestate = aggregate_multiset(@((args*" ")/","-({""})));
7de2602017-12-05Henrik Grubbström (Grubba)  send(200, ({ LOCALE(198, "Prestate set") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  } }
fc40392008-08-15Martin Stjernholm  private void timeout()
ea50e61999-05-01Henrik Grubbström (Grubba)  { if (fd) { int t = (time() - time_touch); if (t > FTP2_TIMEOUT) { // Recomended by RFC 1123 4.1.3.2
7de2602017-12-05Henrik Grubbström (Grubba)  send(421, ({ LOCALE(199, "Connection timed out.") }));
ea50e61999-05-01Henrik Grubbström (Grubba)  send(0,0); if (master_session->file) { if (objectp(master_session->file->file)) { destruct(master_session->file->file); } if (objectp(master_session->file->pipe)) { destruct(master_session->file->pipe); } } if (objectp(pasv_port)) { destruct(pasv_port); } master_session->method = "QUIT"; master_session->not_query = user || "Anonymous"; master_session->conf->log(([ "error":408 ]), master_session); } else { // Not time yet to sever the connection. call_out(timeout, FTP2_TIMEOUT + 30 - t); }
4902702000-09-20Henrik Grubbström (Grubba)  } else { // We ought to be dead already... DWRITE("FTP2: Timeout on dead connection.\n"); destruct();
ea50e61999-05-01Henrik Grubbström (Grubba)  } }
1931842014-08-07Henrik Grubbström (Grubba) #ifdef FTP_USE_HANDLER_THREADS // Minimal layer for API compatibility with ADT.Queue. protected class CommandQueue { inherit Thread.Queue; protected int _sizeof() { return size(); } void put(mixed value) { write(value); } mixed get() { return try_read(); } } private CommandQueue cmd_queue = CommandQueue(); #else
5301f42009-05-12Henrik Grubbström (Grubba)  private ADT.Queue cmd_queue = ADT.Queue();
1931842014-08-07Henrik Grubbström (Grubba) #endif
5301f42009-05-12Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  private void got_command(mixed ignored, string line)
ea50e61999-05-01Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: got_command(X, \"%s\")\n", line);
ea50e61999-05-01Henrik Grubbström (Grubba)  touch_me(); string cmd = line; string args; int i; if (line == "") { // The empty command. // Some stupid ftp-proxies send this. return; // Even less than a NOOP. } if ((i = search(line, " ")) != -1) { cmd = line[..i-1];
d819242000-07-23Henrik Grubbström (Grubba)  args = line[i+1..] - "\0";
ea50e61999-05-01Henrik Grubbström (Grubba)  } cmd = upper_case(cmd); if ((< "PASS" >)[cmd]) { // Censor line, so that the password doesn't show // in backtraces. line = cmd + " CENSORED_PASSWORD"; }
5301f42009-05-12Henrik Grubbström (Grubba)  cmd_queue->put(({ line, cmd, args })); if (!busy) next_cmd(); }
1931842014-08-07Henrik Grubbström (Grubba)  private void low_next_cmd()
5301f42009-05-12Henrik Grubbström (Grubba)  {
1931842014-08-07Henrik Grubbström (Grubba)  // Protect against multiple call_outs. if (busy || !sizeof(cmd_queue)) return; busy = 1;
7a562a2017-11-28Henrik Grubbström (Grubba)  if (!conf) { // Configuration deleted during session. terminate_connection(); return; }
5301f42009-05-12Henrik Grubbström (Grubba)  array(string|array(string)) cmd_entry = cmd_queue->get();
1931842014-08-07Henrik Grubbström (Grubba)  if (!cmd_entry) { // Race? busy = 0; return; }
5301f42009-05-12Henrik Grubbström (Grubba)  string line = cmd_entry[0]; string cmd = cmd_entry[1]; array(string) args = cmd_entry[2];
abd11b2009-06-23Henrik Grubbström (Grubba) </