50c4c42001-08-23Martin Nilsson // This is a roxen protocol module. // Copyright © 1997 - 2001, Roxen IS.
ea50e61999-05-01Henrik Grubbström (Grubba) /* * FTP protocol mk 2
63d8551997-06-10Henrik Grubbström (Grubba)  *
d90ab72002-10-24Martin Nilsson  * $Id: ftp.pike,v 2.83 2002/10/24 00:11:28 nilsson Exp $
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)
3163752001-03-13Henrik Grubbström (Grubba)  * RFC 2428 FTP Extensions for IPv6 and NATs
ea50e61999-05-01Henrik Grubbström (Grubba)  *
7a8c5b2001-03-03Henrik Grubbström (Grubba)  * IETF draft 12 Extended Directory Listing, TVFS, * and Restart Mechanism for FTP
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 * * RFC's describing gateways and proxies: * * RFC 1415 FTP-FTAM Gateway Specification * * More or less obsolete RFC's: * * RFC 412 User FTP documentation * *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 * *RFC 475 FTP and network mail system * *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 * RFC 542 File Transfer Protocol for the ARPA Network * RFC 561 Standardizing Network Mail Headers * *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)  * * (RFC's marked with * are not available from http://www.roxen.com/rfc/)
c175b91998-03-21Henrik Grubbström (Grubba)  */
b1fca01996-11-12Per Hedbor #include <config.h> #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) 
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 
ea50e61999-05-01Henrik Grubbström (Grubba) #if constant(thread_create) #define BACKEND_CLOSE(FD) do { DWRITE("close\n"); FD->set_blocking(); call_out(FD->close, 0); FD = 0; } while(0) #else /* !constant(thread_create) */ #define BACKEND_CLOSE(FD) do { DWRITE("close\n"); FD->set_blocking(); FD->close(); FD = 0; } while(0) #endif /* constant(thread_create) */ 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 static void trace_enter(mixed a, mixed b) {
17e2b22001-06-15Johan Sundström  write("FTP: TRACE_ENTER(%O, %O)\n", a, b);
ea50e61999-05-01Henrik Grubbström (Grubba)  } static void trace_leave(mixed a) {
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() { }
1fd3172002-02-26Anders Johansson  constant __num = ({ 0 }); 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]; report_debug("REQUESTID: New request id #%d.\n", _num); #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) {
f72bc92000-02-04Per Hedbor  if (!(< "create", "connection", "configuration", "__INIT", "clone_me", "end", "ready_to_receive",
bfb6062001-08-03Mattias Wingstedt  "send", "scan_for_query", "send_result", "misc",
5ad1cd2002-02-04Jonas Wallden  "url_base", "set_response_header",
d90ab72002-10-24Martin Nilsson  "add_response_header", "set_output_charset", "adjust_for_config_path",
1fd3172002-02-26Anders Johansson  "destroy", "_num", "__num">)[var]) {
e81b182000-03-20Henrik Grubbström (Grubba) #ifdef FTP2_DEBUG if (catch { #endif /* FTP2_DEBUG */ o[var] = m_rid[var]; #ifdef FTP2_DEBUG }) {
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)  } #endif /* FTP2_DEBUG */
ea50e61999-05-01Henrik Grubbström (Grubba)  }
3e61d11997-03-09Henrik Grubbström (Grubba)  }
7cd6882001-03-05Stefan Wallström  o["misc"] = m_rid["misc"] + ([ ]);
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { // Defaults... client = ({ "ftp" }); prot = "FTP"; clientprot = "FTP";
1cabe52001-02-05Martin Stjernholm  real_variables = ([]);
b6802c2002-07-04Per Hedbor  variables = FakedVariables( real_variables );
ea50e61999-05-01Henrik Grubbström (Grubba)  misc = ([]); cookies = ([]);
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 = "";
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) { static string convert(string s);
b1fca01996-11-12Per Hedbor 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private function read_cb; static private function close_cb; static private mixed id;
280ffa1999-10-29Henrik Grubbström (Grubba) 
e01e952001-09-22Henrik Grubbström (Grubba)  static private object f; static private string data; static private object ftpsession; int is_file; static void create(object f_, string data_, object ftpsession_) { f = f_; data = data_; ftpsession = ftpsession_; is_file = f_->is_file; }
ea50e61999-05-01Henrik Grubbström (Grubba)  static private void read_callback(mixed i, string s) { read_cb(id, convert(s)); ftpsession->touch_me(); } static private void close_callback(mixed i) { close_cb(id); if (f) { BACKEND_CLOSE(f); } ftpsession->touch_me(); }
280ffa1999-10-29Henrik Grubbström (Grubba)  static private void delayed_nonblocking(function w_cb) { 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() { ftpsession->touch_me(); if (f) { f->set_blocking(); BACKEND_CLOSE(f); } }
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; static 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 
ea50e61999-05-01Henrik Grubbström (Grubba)  static string convert(string s) { 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; static string convert(string s) { 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; static object converter = Locale.Charset.encoder("EBCDIC-US", ""); static string convert(string s) { converted += sizeof(s); return(converter->feed(s)->drain()); } } class FromEBCDICWrapper { inherit FileWrapper; int converted; static object converter = Locale.Charset.decoder("EBCDIC-US"); static string convert(string s) { 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) {
ea50e61999-05-01Henrik Grubbström (Grubba)  static int response_code = 226; static string response = "Stored."; static string gotdata = "";
80d2e61999-05-06Henrik Grubbström (Grubba)  static int closed, recvd;
ea50e61999-05-01Henrik Grubbström (Grubba)  static function other_read_callback;
e01e952001-09-22Henrik Grubbström (Grubba)  static object from_fd; static object session; static object ftpsession; int is_file; static void create(object from_fd_, object session_, object ftpsession_) { from_fd = from_fd_; session = session_; ftpsession = ftpsession_; is_file = from_fd->is_file; }
80d2e61999-05-06Henrik Grubbström (Grubba) #include <variables.h>
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) {
ea50e61999-05-01Henrik Grubbström (Grubba)  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); session->file = 0; session->my_fd = from_fd; } 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 
ea50e61999-05-01Henrik Grubbström (Grubba)  static mixed my_read_callback(mixed id, string data) {
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; response = msg; } 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. response = ((result->rettext || errors[result->error])/" ")[1..] * " "; gotdata = result->data || ""; close(); }
ea50e61999-05-01Henrik Grubbström (Grubba)  string query_address(int|void loc) { return from_fd->query_address(loc);
031f321997-08-31Henrik Grubbström (Grubba)  } }
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
8025352001-01-22Henrik Grubbström (Grubba) class LS_L(static RequestID master_session,
f352562000-09-24Henrik Grubbström (Grubba)  static int|void flags)
ea50e61999-05-01Henrik Grubbström (Grubba) { static constant decode_mode = ({ ({ 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" }) }); static constant months = ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" });
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  static string name_from_uid(int uid)
031f321997-08-31Henrik Grubbström (Grubba)  {
8f8ac62001-08-05Henrik Grubbström (Grubba)  User user; foreach(master_session->conf->user_databases(), UserDB user_db) { if (user = user_db->find_user_from_uid(uid)) { return user->name();
031f321997-08-31Henrik Grubbström (Grubba)  } }
ea50e61999-05-01Henrik Grubbström (Grubba)  return (uid?((string)uid):"root");
031f321997-08-31Henrik Grubbström (Grubba)  }
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])) { user = name_from_uid(st[5]); } // FIXME: Convert st[6] to symbolic group name. } 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 { static inherit LS_L; static string cwd; static array(string) argv; static object ftpsession; static array(string) output_queue = ({}); static int output_pos; static string output_mode = "A";
97b9102000-09-13Henrik Grubbström (Grubba)  static mapping(string:array|object) stat_cache = ([]);
ea50e61999-05-01Henrik Grubbström (Grubba) 
b1ca391999-10-29Henrik Grubbström (Grubba)  static object conv;
8025352001-01-22Henrik Grubbström (Grubba)  static 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)) { if (!session) { session = RequestID2(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; } return st; } // FIXME: Should convert output somewhere below. static void output(string s) { 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 }); } static string quote_non_print(string s) { 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", }))); } static string list_files(array(string) files, string|void dir) { 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" })) + "\""; }
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) static ADT.Stack dir_stack = ADT.Stack(); #else
ea50e61999-05-01Henrik Grubbström (Grubba)  static object(Stack.stack) dir_stack = Stack.stack();
c39ebd2000-12-28Martin Stjernholm #endif
ea50e61999-05-01Henrik Grubbström (Grubba)  static int name_directories;
031f321997-08-31Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  static string fix_path(string s) { return(combine_path(cwd, s)); } static void list_next_directory() { 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); }; 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]; } if ((flags & LS_FLAG_a) && (long != "/")) { if (dir) { dir[".."] = stat_file(combine_path(long,"../")); } else { dir = ([ "..":stat_file(combine_path(long,"../")) ]);
031f321997-08-31Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } string listing = ""; if (dir && sizeof(dir)) { 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. } else { name_directories = 1; } } void set_blocking() { } int query_fd() { return -1; } static mixed id; void set_id(mixed i) { id = i; } 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 conv = Locale.Charset.encoder("EBCDIC-US", ""); }
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)  }
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); } if (dir_stack->ptr) { name_directories = dir_stack->ptr && ((dir_stack->ptr > 1) || n_files); list_next_directory(); } else { output(0); } } } class TelnetSession { static object fd; static object conf; static private mapping cb; static private mixed id; static private function(mixed|void:string) write_cb; static private function(mixed, string:void) read_cb; static private function(mixed|void:void) close_cb; static private constant TelnetCodes = ([ 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 static private void got_data(mixed ignored, string s); static private void send_data(); static private void got_oob(mixed ignored, string s); 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); } } static private string to_send = ""; static private void send(string s) { to_send += s; } static private void send_data() { 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(); } } static private mapping(string:function) default_cb = ([ "BRK":lambda() { 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 }, ]); static private int sync = 0; static private void got_oob(mixed ignored, string s) {
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 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private string rest = ""; static 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];
98ac722002-10-02Martin Nilsson  if (has_value(line, "\377")) {
ea50e61999-05-01Henrik Grubbström (Grubba)  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"; static private constant cmd_help = ([ // FTP commands in reverse RFC order. // The following is a command suggested by the author of ncftp. "CLNT":"<sp> <client-name> <sp> <client-version> " "[<sp> <optional platform info>] (Set client name)", // The following are in // "Extended Directory Listing, TVFS, and Restart Mechanism for FTP" // IETF draft 4. "FEAT":"(Feature list)", "MDTM":"<sp> path-name (Modification time)", "SIZE":"<sp> path-name (Size)", "MLST":"<sp> path-name (Machine Processing List File)", "MLSD":"<sp> path-name (Machine Processing List Directory)", "OPTS":"<sp> command <sp> options (Set Command-specific Options)",
3163752001-03-13Henrik Grubbström (Grubba)  // These are from RFC 2428 "EPRT":"<sp> <d>net-prt<d>net-addr<d>tcp-port<d> (Extended Address Port)", "EPSV":"[<sp> net-prt|ALL] (Extended Address Passive Mode)",
ea50e61999-05-01Henrik Grubbström (Grubba)  // These are in RFC 1639
3163752001-03-13Henrik Grubbström (Grubba)  "LPRT":"<sp> <long-host-port> (Long Port)", "LPSV":"(Long Passive)",
ea50e61999-05-01Henrik Grubbström (Grubba)  // Commands in the order from RFC 959 // Login "USER":"<sp> username (Change user)", "PASS":"<sp> password (Change password)", "ACCT":"<sp> <account-information> (Account)", "CWD":"[ <sp> directory-name ] (Change working directory)", "CDUP":"(Change to parent directory)", "SMNT":"<sp> <pathname> (Structure mount)", // Logout "REIN":"(Reinitialize)", "QUIT":"(Terminate service)", // Transfer parameters "PORT":"<sp> b0, b1, b2, b3, b4 (Set port IP and number)", "PASV":"(Set server in passive mode)", "TYPE":"<sp> [ A | E | I | L ] (Ascii, Ebcdic, Image, Local)", "STRU":"<sp> <structure-code> (File structure)", "MODE":"<sp> <mode-code> (Transfer mode)", // File action commands "ALLO":"<sp> <decimal-integer> [<sp> R <sp> <decimal-integer>]" " (Allocate space for file)", "REST":"<sp> marker (Set restart marker)", "STOR":"<sp> file-name (Store file)", "STOU":"(Store file with unique name)", "RETR":"<sp> file-name (Retreive file)",
0900e22001-08-22Henrik Grubbström (Grubba)  "LIST":"[ <sp> <pathname> ] (List directory)", "NLST":"[ <sp> <pathname> ] (List directory)",
ea50e61999-05-01Henrik Grubbström (Grubba)  "APPE":"<sp> <pathname> (Append file)", "RNFR":"<sp> <pathname> (Rename from)", "RNTO":"<sp> <pathname> (Rename to)", "DELE":"<sp> file-name (Delete file)", "RMD":"<sp> <pathname> (Remove directory)", "MKD":"<sp> <pathname> (Make directory)", "PWD":"(Return current directory)", "ABOR":"(Abort current transmission)", // Informational commands "SYST":"(Get type of operating system)",
0900e22001-08-22Henrik Grubbström (Grubba)  "STAT":"[ <sp> <pathname> ] (Status for server/file)",
ea50e61999-05-01Henrik Grubbström (Grubba)  "HELP":"[ <sp> <string> ] (Give help)", // Miscellaneous commands "SITE":"<sp> <string> (Site parameters)", // Has separate help "NOOP":"(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 "XMKD":"<sp> path-name (Make directory)", "XRMD":"<sp> path-name (Remove directory)", "XPWD":"(Return current directory)", "XCWD":"[ <sp> directory-name ] (Change working directory)", "XCUP":"(Change to parent directory)", // These are in RFC 765 but not in RFC 959 "MAIL":"[<sp> <recipient name>] (Mail to user)", "MSND":"[<sp> <recipient name>] (Mail send to terminal)", "MSOM":"[<sp> <recipient name>] (Mail send to terminal or mailbox)", "MSAM":"[<sp> <recipient name>] (Mail send to terminal and mailbox)", "MRSQ":"[<sp> <scheme>] (Mail recipient scheme question)", "MRCP":"<sp> <recipient name> (Mail recipient)", // These are in RFC 743 "XRSQ":"[<sp> <scheme>] (Scheme selection)", "XRCP":"<sp> <recipient name> (Recipient specification)", // These are in RFC 737 "XSEN":"[<sp> <recipient name>] (Send to terminal)", "XSEM":"[<sp> <recipient name>] (Send, mail if can\'t)", "XMAS":"[<sp> <recipient name>] (Mail and send)", // These are in RFC 542 "BYE":"(Logout)", "BYTE":"<sp> <bits> (Byte size)", "SOCK":"<sp> host-socket (Data socket)", // This one is referenced in a lot of old RFCs "MLFL":"(Mail file)", ]); static private constant site_help = ([ "CHMOD":"<sp> mode <sp> file", "UMASK":"<sp> mode", "PRESTATE":"<sp> prestate", ]);
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private constant modes = ([ "A":"ASCII", "E":"EBCDIC", "I":"BINARY", "L":"LOCAL", ]);
2d3ff51997-04-10Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private int time_touch = time();
b1fca01996-11-12Per Hedbor 
8ca1112000-05-29Henrik Grubbström (Grubba)  static private object(ADT.Queue) to_send = ADT.Queue();
2408161997-05-07Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  static 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)  static 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 { string s = to_send->get();
d16b3e1997-03-27Henrik Grubbström (Grubba) 
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: write_cb(): Sending \"%s\"\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); } else { ::set_write_callback(write_cb); } return(s); } }
a36f051997-11-27Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  void send(int code, array(string) data, int|void enumerate_all)
d16b3e1997-03-27Henrik Grubbström (Grubba)  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: 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()) { to_send->put(s); ::set_write_callback(write_cb); } else { to_send->put(s); } } else {
17e2b22001-06-15Johan Sundström  DWRITE("FTP2: send(): Nothing to send!\n");
ea50e61999-05-01Henrik Grubbström (Grubba)  }
d16b3e1997-03-27Henrik Grubbström (Grubba)  }
8025352001-01-22Henrik Grubbström (Grubba)  static private RequestID master_session;
d16b3e1997-03-27Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private string dataport_addr; static private int dataport_port;
d16b3e1997-03-27Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private string mode = "A";
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private string cwd = "/";
ad72bd1997-04-09Marcus Comstedt 
8025352001-01-22Henrik Grubbström (Grubba)  static private User auth_user;
d90ab72002-10-24Martin Nilsson  // Authenticated user.
8025352001-01-22Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private string user; static private string password; static private int logged_in;
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private object curr_pipe; static private int restart_point;
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private multiset|int allowed_shells = 0; // 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;
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)  /* * Misc */ static 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 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private string fix_path(string s)
ad72bd1997-04-09Marcus Comstedt  {
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 */ static private object pasv_port; static private function(object, mixed:void) pasv_callback; static private mixed pasv_args; static private array(object) pasv_accepted = ({}); void pasv_accept_callback(mixed id)
94d84c1997-08-29Marcus Comstedt  {
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  }
ea50e61999-05-01Henrik Grubbström (Grubba)  static 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 
280ffa1999-10-29Henrik Grubbström (Grubba)  static private void ftp_async_connect(function(object,string,mixed ...:void) fun,
ea50e61999-05-01Henrik Grubbström (Grubba)  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; }
ea50e61999-05-01Henrik Grubbström (Grubba)  object(Stdio.File) f = Stdio.File();
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;
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 
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  } }
ea50e61999-05-01Henrik Grubbström (Grubba)  privs = 0;
280ffa1999-10-29Henrik Grubbström (Grubba)  f->set_nonblocking(lambda(mixed ignored, string data) { DWRITE("FTP: async_connect ok. Got data.\n"); f->set_nonblocking(0,0,0); fun(f, data, @args); }, lambda(mixed ignored) { DWRITE("FTP: async_connect ok.\n"); f->set_nonblocking(0,0,0); fun(f, "", @args); }, lambda(mixed ignored) { DWRITE("FTP: connect_and_send failed\n"); destruct(f); fun(0, 0, @args); });
ea50e61999-05-01Henrik Grubbström (Grubba)  #ifdef FD_DEBUG
280ffa1999-10-29Henrik Grubbström (Grubba)  mark_fd(f->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
280ffa1999-10-29Henrik Grubbström (Grubba)  if(catch(f->connect(dataport_addr, dataport_port))) { DWRITE("FTP: Illegal internet address in connect in async comm.\n");
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 */
b6802c2002-07-04Per Hedbor  static private void send_done_callback( object fd, object session, int amount )
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)  if(fd) { if (fd->set_blocking) { fd->set_blocking(); // Force close() to flush any buffers. } BACKEND_CLOSE(fd); } curr_pipe = 0;
ad72bd1997-04-09Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (session && session->file) {
b6802c2002-07-04Per Hedbor  session->file->len = amount;
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(session->file, session); session->file = 0; } send(226, ({ "Transfer complete." }));
ad72bd1997-04-09Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba) 
97b9102000-09-13Henrik Grubbström (Grubba)  static private mapping|array|object stat_file(string fname, object|void session)
ad72bd1997-04-09Marcus Comstedt  {
ea50e61999-05-01Henrik Grubbström (Grubba)  mapping file; if (!session) { session = RequestID2(master_session); session->method = "STAT"; }
ad72bd1997-04-09Marcus Comstedt 
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, "//", "/");
ea50e61999-05-01Henrik Grubbström (Grubba)  return(conf->stat_file(fname, session));
b134ed1997-06-12Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba)  return(file); }
2408161997-05-07Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private int expect_argument(string cmd, string args)
b1fca01996-11-12Per Hedbor  {
ea50e61999-05-01Henrik Grubbström (Grubba)  if ((< "", 0 >)[args]) { send(504, ({ sprintf("Syntax: %s %s", cmd, cmd_help[cmd]) })); 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) 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private void send_error(string cmd, string f, mapping file, object session) { switch(file && file->error) { case 301: case 302: if (file->extra_heads && file->extra_heads->Location) { send(504, ({ sprintf("'%s': %s: Redirect to %O.", cmd, f, file->extra_heads->Location) })); } else { send(504, ({ sprintf("'%s': %s: Redirect.", cmd, f) })); } break;
8b094e1997-05-26Henrik Grubbström (Grubba)  case 401: case 403:
ea50e61999-05-01Henrik Grubbström (Grubba)  send(530, ({ sprintf("'%s': %s: Access denied.", cmd, f) }));
8b094e1997-05-26Henrik Grubbström (Grubba)  break;
ad72bd1997-04-09Marcus Comstedt  case 405:
ea50e61999-05-01Henrik Grubbström (Grubba)  send(530, ({ sprintf("'%s': %s: Method not allowed.", cmd, f) })); break; case 500: send(451, ({ sprintf("'%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 ]); } send(550, ({ sprintf("'%s': %s: No such file or directory.", 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  }
ea50e61999-05-01Henrik Grubbström (Grubba)  static private int open_file(string fname, object session, string cmd) {
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 
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])) {
ea50e61999-05-01Henrik Grubbström (Grubba)  send(550, ({ sprintf("%s: not a plain file.", fname) })); 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));
ea50e61999-05-01Henrik Grubbström (Grubba)  send(550, ({ sprintf("%s: Error, can't open file.", fname) })); 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));
ea50e61999-05-01Henrik Grubbström (Grubba)  send(550, ({ sprintf("%s: Error, can't open file.", fname) })); return 0; } }
7a87c71997-09-03Henrik Grubbström (Grubba) 
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) 
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)  } }
ea50e61999-05-01Henrik Grubbström (Grubba)  return 1;
7c8fd51998-03-23Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba) 
280ffa1999-10-29Henrik Grubbström (Grubba)  static private void connected_to_send(object fd, string ignored, mapping file, object session)
b1fca01996-11-12Per Hedbor  {
17e2b22001-06-15Johan Sundström  DWRITE("FTP: connected_to_send(X, %O, %O, X)\n", ignored, file);
ea50e61999-05-01Henrik Grubbström (Grubba)  touch_me(); 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) { send(150, ({ sprintf("Opening %s data connection for %s (%d bytes).", modes[file->mode], file->full_path, file->len) })); } else { send(150, ({ sprintf("Opening %s mode data connection for %s", modes[file->mode], file->full_path) }));
879c1a1998-05-08Henrik Grubbström (Grubba)  } }
b1fca01996-11-12Per Hedbor  else
ea50e61999-05-01Henrik Grubbström (Grubba)  {
abdff21999-12-27Martin Nilsson  send(425, ({ "Can't build data connect: Connection refused." }));
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) { object conv = Locale.Charset.encoder("EBCDIC-US", ""); 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) 
b6802c2002-07-04Per Hedbor  object throttler=session->throttler;
df4fbf1999-10-10Francesco Chemolli  object pipe;
b6802c2002-07-04Per Hedbor  pipe = roxen.get_shuffler( fd ); if( conf ) conf->connection_add( this_object(), pipe ); if( throttler || conf->throttler ) pipe->set_throttler( throttler || conf->throttler );
abdff21999-12-27Martin Nilsson 
b6802c2002-07-04Per Hedbor  pipe->set_done_callback( lambda( Shuffler.Shuffle r, int reason ) { send_done_callback( fd, session, r->sent_data() ); } );
ea50e61999-05-01Henrik Grubbström (Grubba)  master_session->file = session->file = file; if(stringp(file->data)) {
b6802c2002-07-04Per Hedbor  pipe->add_source(file->data,0,strlen(file->data));
ea50e61999-05-01Henrik Grubbström (Grubba)  } if(file->file) {
b6802c2002-07-04Per Hedbor  int off;
ea50e61999-05-01Henrik Grubbström (Grubba)  file->file->set_blocking();
b6802c2002-07-04Per Hedbor  catch( off = file->tell() ); pipe->add_source( file->file, max(off,0), (file->len||-1) );
ea50e61999-05-01Henrik Grubbström (Grubba)  }
b6802c2002-07-04Per Hedbor  pipe->start();
ea50e61999-05-01Henrik Grubbström (Grubba)  curr_pipe = pipe; }
b1fca01996-11-12Per Hedbor 
280ffa1999-10-29Henrik Grubbström (Grubba)  static 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) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (fd) { send(150, ({ sprintf("Opening %s mode data connection for %s.", modes[mode], args) })); } else { send(425, ({ "Can't build data connect: Connection refused." })); 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) 
b6802c2002-07-04Per Hedbor  object 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) 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (open_file(args, session, "STOR")) { if (!(session->file->pipe)) { if (fd) { BACKEND_CLOSE(fd);
b1fca01996-11-12Per Hedbor  }
ea50e61999-05-01Henrik Grubbström (Grubba)  switch(session->file->error) { case 401: send(530, ({ sprintf("%s: Need account for storing files.", args)})); break; case 413: send(550, ({ sprintf("%s: Quota exceeded.", args) })); break; case 501: send(502, ({ sprintf("%s: Command not implemented.", args) })); break; default: send(550, ({ sprintf("%s: Error opening file.", args) }));
c175b91998-03-21Henrik Grubbström (Grubba)  break; }
ea50e61999-05-01Henrik Grubbström (Grubba)  session->conf->log(session->file, session); return;
9c83eb1997-08-25Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  master_session->file = session->file; } else { // Error message has already been sent. if (fd) { BACKEND_CLOSE(fd);
b1fca01996-11-12Per Hedbor  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } }
b1fca01996-11-12Per Hedbor 
7cd6882001-03-05Stefan Wallström  static private void discard_data_connection() { if(pasv_port && sizeof(pasv_accepted)) pasv_accepted = pasv_accepted[1..]; }
ea50e61999-05-01Henrik Grubbström (Grubba)  static private void connect_and_send(mapping file, object session) {
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 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private void connect_and_receive(string arg) {
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 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private string my_combine_path(string base, string part) { 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) 
c467861999-08-08Henrik Grubbström (Grubba)  static private constant IFS = (<" ", "\t">); static private constant Quote = (< "\'", "\"", "\`", "\\" >); static private constant Specials = IFS|Quote; static private array(string) split_command_line(string cmdline) { // 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; }
ea50e61999-05-01Henrik Grubbström (Grubba)  static private array(string) glob_expand_command_line(string cmdline) {
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) 
ea50e61999-05-01Henrik Grubbström (Grubba)  array (int) st;
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 }); } } } } 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); return(id->conf->stat_file(id->not_query, id)); }, 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 */ static private constant ls_options = ({ ({ ({ "-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) 
ea50e61999-05-01Henrik Grubbström (Grubba)  static private array(array(string)|string|int) ls_getopt_args = Array.map(ls_options, lambda(array(array(string)|int|string) entry) { return({ entry[1], Getopt.NO_ARG, entry[0] }); }); static private string ls_help(string ls) {
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()); }
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) 
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) 
ea50e61999-05-01Henrik Grubbström (Grubba)  string make_MLSD_fact(string f, mapping(string:array) dir, object session) { 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)  facts["UNIX.mode"] = st[0];
b134ed1997-06-12Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (st[1] >= 0) { facts->size = (string)st[1]; facts->type = "File"; facts["media-type"] = session->conf->type_from_filename(f) || "application/octet-stream"; } else { facts->type = ([ "..":"pdir", ".":"cdir" ])[f] || "dir"; }
b134ed1997-06-12Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  facts->modify = make_MDTM(st[3]);
b134ed1997-06-12Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  facts->charset = "8bit";
b134ed1997-06-12Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  // Construct and return the answer.
c175b91998-03-21Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  return(Array.map(indices(facts), lambda(string s, mapping f) { return s + "=" + f[s]; }, facts) * ";" + " " + f); }
2d3ff51997-04-10Marcus Comstedt 
ea50e61999-05-01Henrik Grubbström (Grubba)  void send_MLSD_response(mapping(string:array) dir, object session) { dir = dir || ([]); array f = indices(dir);
7cd6882001-03-05Stefan Wallström  session->file->data = sizeof(f) ? (Array.map(f, make_MLSD_fact, dir, session) * "\r\n") + "\r\n" : "" ;
ea50e61999-05-01Henrik Grubbström (Grubba)  session->file->mode = "I"; connect_and_send(session->file, session); }
7a8c5b2001-03-03Henrik Grubbström (Grubba)  void send_MLST_response(mapping(string:array) dir, object session) { dir = dir || ([]); send(250,({ "OK" }) + Array.map(indices(dir), make_MLSD_fact, dir, session) + ({ "OK" }) ); }
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; if (pasv_port) { destruct(pasv_port); pasv_port = 0; } if (args != 1) { // Not called by QUIT. send(220, ({ "Server ready for new user." })); } } 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"; if ((< 0, "ftp", "anonymous" >)[user]) { 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
c8a3ca2000-09-18Henrik Grubbström (Grubba)  send(200, ({ "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! send(331, ({ "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, ({ sprintf("Too many anonymous users (%d).", port_obj->query_option("ftp_user_session_limit")) })); conf->log(([ "error":403 ]), master_session); }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { send(530, ({ "Anonymous ftp disabled" })); conf->log(([ "error":403 ]), master_session);
abfd7d1997-06-12Marcus Comstedt  }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else {
c8a3ca2000-09-18Henrik Grubbström (Grubba)  if (check_login()) { send(331, ({ sprintf("Password required for %s.", user) })); master_session->not_query = user; conf->log(([ "error":407 ]), master_session); } else { // Session limit exceeded. send(530, ({ sprintf("Concurrent session limit (%d) exceeded for user \"%s\".", 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()) { send(230, ({ "Guest login ok, access restrictions apply." })); master_session->method = "LOGIN"; master_session->not_query = "Anonymous User:"+args; conf->log(([ "error":200 ]), master_session); logged_in = -1; } else { send(530, ({ sprintf("Too many anonymous users (%d).", port_obj->query_option("ftp_user_session_limit")) })); conf->log(([ "error":403 ]), master_session); }
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { send(503, ({ "Login with USER first." })); } return; }
3bcd3b1997-05-07Henrik Grubbström (Grubba) 
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 
8025352001-01-22Henrik Grubbström (Grubba)  auth_user = master_session->conf->authenticate(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba) 
8025352001-01-22Henrik Grubbström (Grubba)  if (!auth_user) {
b0132d1999-10-08Henrik Grubbström (Grubba)  if (!port_obj->query_option("guest_ftp")) {
ea50e61999-05-01Henrik Grubbström (Grubba)  send(530, ({ sprintf("User %s access denied.", user) })); conf->log(([ "error":401 ]), master_session); } else {
c8a3ca2000-09-18Henrik Grubbström (Grubba)  // Guest user. string u = user; user = 0; if (login()) { send(230, ({ sprintf("Guest user %s logged in.", u) })); logged_in = -1; conf->log(([ "error":200 ]), master_session);
17e2b22001-06-15Johan Sundström  DWRITE("FTP: Guest-user: %O\n", master_session->realauth);
c8a3ca2000-09-18Henrik Grubbström (Grubba)  } else { send(530, ({ sprintf("Too many anonymous/guest users (%d).", port_obj->query_option("ftp_user_session_limit")) })); conf->log(([ "error":403 ]), master_session); }
3bcd3b1997-05-07Henrik Grubbström (Grubba)  }
ea50e61999-05-01Henrik Grubbström (Grubba)  return; }
0026281999-06-07Martin Stjernholm  // Authentication successful
ea50e61999-05-01Henrik Grubbström (Grubba) 
b0132d1999-10-08Henrik Grubbström (Grubba)  if (!port_obj->query_option("named_ftp") ||
cc83812001-09-10Henrik Grubbström (Grubba)  !check_shell(auth_user->shell())) {
ea50e61999-05-01Henrik Grubbström (Grubba)  send(530, ({ "You are not allowed to use named-ftp.", "Try using anonymous, or check /etc/shells" })); conf->log(([ "error":402 ]), master_session);
8025352001-01-22Henrik Grubbström (Grubba)  auth_user = 0;
ea50e61999-05-01Henrik Grubbström (Grubba)  return; }
c8a3ca2000-09-18Henrik Grubbström (Grubba)  if (!login()) { send(530, ({ sprintf("Too many concurrent sessions (limit is %d).", port_obj->query_option("ftp_user_session_limit")) })); conf->log(([ "error":403 ]), master_session); 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; array(int)|object st = conf->stat_file(home, master_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  }
ea50e61999-05-01Henrik Grubbström (Grubba)  logged_in = 1;
abdff21999-12-27Martin Nilsson  send(230, ({ sprintf("User %s logged in.", user) }));
ea50e61999-05-01Henrik Grubbström (Grubba)  conf->log(([ "error":202 ]), master_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) { send(550, ({ sprintf("%s: No such file or directory, or access denied.", ncwd) })); session->conf->log(session->file || ([ "error":404 ]), session); return; } if (!(< -2, -3 >)[st[1]]) { send(504, ({ sprintf("%s: Not a directory.", ncwd) })); session->conf->log(([ "error":400 ]), session); return; } // CWD Successfull cwd = ncwd; array(string) reply = ({ sprintf("Current directory is now %s.", cwd) }); // 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)) { reply = ({ sprintf("Please read the file %s.", f), sprintf("It was last modified %s - %d days ago.", 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); } 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) { send(221, ({ "Bye! It was nice talking to you!" })); 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); } void ftp_PORT(string args) {
3163752001-03-13Henrik Grubbström (Grubba)  if (epsv_only) { send(530, ({ "'PORT': Method not allowed in EPSV ALL mode." })); 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)
3163752001-03-13Henrik Grubbström (Grubba)  send(501, ({ "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); } send(200, ({ "PORT command ok ("+dataport_addr+ " port "+dataport_port+")" })); } }
3163752001-03-13Henrik Grubbström (Grubba)  void ftp_EPRT(string args) { if (epsv_only) { send(530, ({ "'EPRT': Method not allowed in EPSV ALL mode." })); return; } if (sizeof(args) < 3) { send(501, ({ "I don't understand your parameters." })); return; } string delimiter = args[0..0];
b820052001-03-13Martin Stjernholm  if ((delimiter[0] <= 32) || (delimiter[0] >= 127)) {
3163752001-03-13Henrik Grubbström (Grubba)  send(501, ({ "Invalid delimiter." })); } array(string) segments = args/delimiter; if (sizeof(args) != 4) { send(501, ({ "I don't understand your parameters." })); return; } if (segments[1] != "1") {
ac291c2001-03-13Henrik Grubbström (Grubba)  // FIXME: No support for IPv6 yet.
3163752001-03-13Henrik Grubbström (Grubba)  send(522, ({ "Network protocol not supported, use (1)" })); return; } if ((sizeof(segments[2]/".") != 4) || sizeof(replace(segments[2], ".0123456789"/"", allocate(11, "")))) { send(501, ({ sprintf("Bad IPv4 address: '%s'", segments[2]) })); return; }
8191832001-03-13Martin Stjernholm  if (!((int)segments[3])) {
3163752001-03-13Henrik Grubbström (Grubba)  send(501, ({ sprintf("Bad port number: '%s'", segments[3]) })); 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); } send(200, ({ "EPRT command ok ("+dataport_addr+ " port "+dataport_port+")" })); }
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) { send(530, ({ "'PASV': Method not allowed in EPSV ALL mode." })); 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; send(452, ({ "Requested action aborted: Out of ports." })); return; } }
11d3452002-02-12Henrik Grubbström (Grubba)  send(227, ({ sprintf("Entering Passive Mode. (%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)  if (args && args != "1") { if (lower_case(args) == "all") { epsv_only = 1; send(200, ({ "Entering EPSV ALL mode." })); } else {
b0f1a92001-03-13Henrik Grubbström (Grubba)  // FIXME: No support for IPv6 yet.
3163752001-03-13Henrik Grubbström (Grubba)  send(522, ({ "Network protocol not supported, use (1)" })); } return; } if (pasv_port) destruct(pasv_port); 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; send(452, ({ "Requested action aborted: Out of ports." })); return; } }
6e2db72001-06-28Marcus Wellhardh  send(229, ({ sprintf("Entering Extended Passive Mode (|||%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: send(504, ({ "'TYPE': Unknown type:"+args })); return;
031f321997-08-31Henrik Grubbström (Grubba)  }
0a34b11997-03-13Henrik Grubbström (Grubba) 
ea50e61999-05-01Henrik Grubbström (Grubba)  send(200, ({ sprintf("Using %s mode for transferring files.", 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 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (open_file(args, session, "RETR")) { if (restart_point) { if (session->file->data) { if (sizeof(session->file->data) >= restart_point) { session->file->data = session->file->data[restart_point..]; restart_point = 0; } else { restart_point -= sizeof(session->file->data); m_delete(session->file, "data"); } } if (restart_point) { if (!(session->file->file && session->file->file->seek && (session->file->file->seek(restart_point) != -1))) { restart_point = 0; send(550, ({ "'RETR': Error restoring restart point." }));
7cd6882001-03-05Stefan Wallström  discard_data_connection();
ea50e61999-05-01Henrik Grubbström (Grubba)  return; } restart_point = 0; } } connect_and_send(session->file, session); }
7cd6882001-03-05Stefan Wallström  else discard_data_connection();
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; send(350, ({ "'REST' ok" })); }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_ABOR(string args) { if (curr_pipe) { catch {
b6802c2002-07-04Per Hedbor  curr_pipe->stop();
ea50e61999-05-01Henrik Grubbström (Grubba)  }; curr_pipe = 0; send(426, ({ "Data transmission terminated." })); } send(226, ({ "'ABOR' Completed." })); } void ftp_PWD(string args) { send(257, ({ sprintf("\"%s\" is current directory.", cwd) })); } void ftp_XPWD(string args) { ftp_PWD(args); } /* * Handling of file moving */
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  static 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 
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "STAT"; if (stat_file(args, session)) { send(350, ({ sprintf("%s ok, waiting for destination name.", args) }) ); rename_from = args; } else { send(550, ({ sprintf("%s: no such file or permission denied.",args) }) ); } } void ftp_RNTO(string args) { if(!rename_from) { send(503, ({ "RNFR needed before RNTO." })); 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")) { send(250, ({ sprintf("%s moved to %s.", rename_from, args) })); session->conf->log(([ "error":200 ]), session); } rename_from = 0; }
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) { // ftp_MLSD(args); return; ftp_NLST("-l " + (args||"")); } void ftp_MLST(string args) { args = fix_path(args || ".");
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) { session->file = ([]); session->file->full_path = args;
7a8c5b2001-03-03Henrik Grubbström (Grubba)  send_MLST_response(([ args:st ]), session);
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { send_error("MLST", args, session->file, session); } } 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; send_MLSD_response(session->conf->find_dir_stat(args, session), session); } else { if (st) { session->file->error = 405; } send_error("MLSD", args, session->file, session);
7cd6882001-03-05Stefan Wallström  discard_data_connection();
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)  session->data = 0; session->misc->len = 0; session->method = "DELETE"; if (open_file(args, session, "DELE")) { send(250, ({ sprintf("%s deleted.", args) })); session->conf->log(([ "error":200 ]), session); return; } } 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)  session->data = 0; 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); return; } else if (st[1] != -2) { if (st[1] == -3) { send(504, ({ sprintf("%s is a module mountpoint.", args) })); session->conf->log(([ "error":405 ]), session); } else { send(504, ({ sprintf("%s is not a directory.", args) })); session->conf->log(([ "error":405 ]), session); } return; } if (open_file(args, session, "RMD")) { send(250, ({ sprintf("%s deleted.", args) })); session->conf->log(([ "error":200 ]), session); return; } } 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"; session->data = 0; session->misc->len = 0;
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  if (open_file(args, session, "MKD")) { send(257, ({ sprintf("\"%s\" created.", args) })); session->conf->log(([ "error":200 ]), session); return; } } void ftp_XMKD(string args) { ftp_MKD(args); } 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; } send(200, ({ "Ok, gottcha!"})); 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]); })); a = Array.map(a, lambda(string s) { return(([ "REST":"REST STREAM", "MLST":"MLST UNIX.mode;size;type;modify;charset;media-type", "MLSD":"", ])[s] || s); }) - ({ "" }); send(211, ({ "The following features are supported:" }) + a + ({ "END" })); } void ftp_MDTM(string args) { if (!expect_argument("MDTM", 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 = "STAT";
97b9102000-09-13Henrik Grubbström (Grubba)  mapping|array|object st = stat_file(args, session);
ea50e61999-05-01Henrik Grubbström (Grubba) 
97b9102000-09-13Henrik Grubbström (Grubba)  if (!arrayp(st) && !objectp(st)) {
ea50e61999-05-01Henrik Grubbström (Grubba)  send_error("MDTM", args, st, session); return; } send(213, ({ make_MDTM(st[3]) })); } void ftp_SIZE(string args) { if (!expect_argument("SIZE", 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 = "STAT";
97b9102000-09-13Henrik Grubbström (Grubba)  mapping|array|object st = stat_file(args, session);
ea50e61999-05-01Henrik Grubbström (Grubba) 
97b9102000-09-13Henrik Grubbström (Grubba)  if (!arrayp(st) && !objectp(st)) {
ea50e61999-05-01Henrik Grubbström (Grubba)  send_error("SIZE", args, st, session); return; } int size = st[1]; if (size < 0) {
6ef8a41999-06-08Henrik Grubbström (Grubba)  send_error("SIZE", args, ([ "error":405, ]), session); return; // size = 512;
ea50e61999-05-01Henrik Grubbström (Grubba)  } send(213, ({ (string)size })); } 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. */ send(211, sprintf("%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", replace(fd->query_address(1), " ", ":"), roxen.version(), port_obj->sorted_urls * "\nListening on ", replace(fd->query_address(), " ", ":"), 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);
8025352001-01-22Henrik Grubbström (Grubba)  RequestID session = RequestID2(master_session);
ea50e61999-05-01Henrik Grubbström (Grubba)  session->method = "STAT";
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)) {
ea50e61999-05-01Henrik Grubbström (Grubba)  send_error("STAT", long, st, session); return; } string s = LS_L(master_session)->ls_l(args, st); send(213, sprintf("status of \"%s\":\n" "%s" "End of Status", args, s)/"\n"); } void ftp_NOOP(string args) { send(200, ({ "Nothing done ok" })); } void ftp_HELP(string args) { if ((< "", 0 >)[args]) { send(214, ({ "The following commands are recognized (* =>'s unimplemented):", @(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 if ((args/" ")[0] == "SITE") { array(string) a = (upper_case(args)/" ")-({""}); if (sizeof(a) == 1) { send(214, ({ "The following SITE commands are recognized:", @(sprintf(" %#70s", sort(indices(site_help))*"\n")/"\n") })); } else if (site_help[a[1]]) { send(214, ({ sprintf("Syntax: SITE %s %s", a[1], site_help[a[1]]) })); } else { send(504, ({ sprintf("Unknown SITE command %s.", a[1]) })); } } else { args = upper_case(args); if (cmd_help[args]) { send(214, ({ sprintf("Syntax: %s %s%s", args, cmd_help[args], (this_object()["ftp_"+args]? "":"; unimplemented")) })); } else { send(504, ({ sprintf("Unknown command %s.", args) })); } } } 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]]) { send(502, ({ sprintf("Bad SITE command: '%s'", a[0]) })); } else if (this_object()["ftp_SITE_"+a[0]]) { this_object()["ftp_SITE_"+a[0]](a[1..]); } else { send(502, ({ sprintf("SITE command '%s' is not currently supported.", a[0]) })); } } void ftp_SITE_CHMOD(array(string) args) { if (sizeof(args) < 2) { send(501, ({ sprintf("'SITE CHMOD %s': incorrect arguments", 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) { send(501, ({ "SITE CHMOD: mode should be between 0 and 0777" })); 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")) { send(250, ({ sprintf("Changed permissions of %s to 0%o.", fname, mode) })); session->conf->log(([ "error":200 ]), session); } }
abdff21999-12-27Martin Nilsson 
ea50e61999-05-01Henrik Grubbström (Grubba)  void ftp_SITE_UMASK(array(string) args) { if (sizeof(args) < 1) { send(501, ({ sprintf("'SITE UMASK %s': incorrect arguments", 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) { send(501, ({ "SITE UMASK: mode should be between 0 and 0777" })); return; } master_session->misc->umask = mode; send(250, ({ sprintf("Umask set to 0%o.", mode) })); } void ftp_SITE_PRESTATE(array(string) args) { if (!sizeof(args)) { master_session->prestate = (<>); send(200, ({ "Prestate cleared" })); } else { master_session->prestate = aggregate_multiset(@((args*" ")/","-({""}))); send(200, ({ "Prestate set" })); } } static private void timeout() { if (fd) { int t = (time() - time_touch); if (t > FTP2_TIMEOUT) { // Recomended by RFC 1123 4.1.3.2 send(421, ({ "Connection timed out." })); 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)  } } static private void got_command(mixed ignored, string line) {
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"; }
8025352001-01-22Henrik Grubbström (Grubba) #if 0 if (!conf->extra_statistics) { conf->extra_statistics = ([ "ftp": (["commands":([ cmd:1 ])])]); } else if (!conf->extra_statistics->ftp) {
ea50e61999-05-01Henrik Grubbström (Grubba)  conf->extra_statistics->ftp = (["commands":([ cmd:1 ])]); } else if (!conf->extra_statistics->ftp->commands) { conf->extra_statistics->ftp->commands = ([ cmd:1 ]); } else { conf->extra_statistics->ftp->commands[cmd]++; }
8025352001-01-22Henrik Grubbström (Grubba) #endif /* 0 */
ea50e61999-05-01Henrik Grubbström (Grubba)  if (cmd_help[cmd]) { if (!logged_in) { if (!(< "REIN", "USER", "PASS", "SYST", "ACCT", "QUIT", "ABOR", "HELP" >)[cmd]) { send(530, ({ "You need to login first." })); return; } } if (this_object()["ftp_"+cmd]) { conf->requests++;
16675a2002-04-11Anders Johansson #if 1 mixed err; if (err = catch { this_object()["ftp_"+cmd](args); }) { report_error("Internal server error in FTP2\n" "Handling command %O\n%s\n", line, describe_backtrace(err)); } #else
72c2a12002-02-11Henrik Grubbström (Grubba)  roxen->handle(lambda(function f, string args, string line) { mixed err; if (err = catch { f(args); }) { report_error("Internal server error in FTP2\n" "Handling command %O\n%s\n", line, describe_backtrace(err)); }
5e3c102002-02-12Henrik Grubbström (Grubba)  }, this_object()["ftp_"+cmd], args, line);
16675a2002-04-11Anders Johansson #endif
ea50e61999-05-01Henrik Grubbström (Grubba)  } else { send(502, ({ sprintf("'%s' is not currently supported.", cmd) })); } } else { send(502, ({ sprintf("Unknown command '%s'.", cmd) })); } touch_me(); } void con_closed() {
4902702000-09-20Henrik Grubbström (Grubba)  DWRITE("FTP2: con_closed()\n"); logout();
ea50e61999-05-01Henrik Grubbström (Grubba)  master_session->method = "QUIT"; master_session->not_query = user || "Anonymous"; conf->log(([ "error":204, "request_time":(time(1)-master_session->time) ]), master_session);
4902702000-09-20Henrik Grubbström (Grubba)  if (fd) { fd->close(); }
0cc9c52001-08-05Henrik Grubbström (Grubba)  if (pasv_port) { destruct(pasv_port); pasv_port = 0; }
4902702000-09-20Henrik Grubbström (Grubba)  // Make sure we disappear... destruct();
ea50e61999-05-01Henrik Grubbström (Grubba)  } void destroy() {
4902702000-09-20Henrik Grubbström (Grubba)  DWRITE("FTP2: destroy()\n");
c8a3ca2000-09-18Henrik Grubbström (Grubba)  logout();
ea50e61999-05-01Henrik Grubbström (Grubba) 
65f64f2000-12-05Henrik Grubbström (Grubba)  port_obj->sessions--;
ea50e61999-05-01Henrik Grubbström (Grubba)  } void create(object fd, object c) {
b0132d1999-10-08Henrik Grubbström (Grubba)  port_obj = c; // FIXME: Only supports one configuration! conf = port_obj->urls[port_obj->sorted_urls[0]]->conf;
8653f62000-03-10Henrik Grubbström (Grubba)  // Support delayed loading. if (!conf->inited) { conf->enable_all_modules(); }
c8a3ca2000-09-18Henrik Grubbström (Grubba) 
7da2b01999-10-08Henrik Grubbström (Grubba) #if 0
b0132d1999-10-08Henrik Grubbström (Grubba)  werror("FTP: conf:%O\n" "FTP:urls:%O\n", mkmapping(indices(conf), values(conf)), port_obj->urls);
7da2b01999-10-08Henrik Grubbström (Grubba) #endif /* 0 */
ea50e61999-05-01Henrik Grubbström (Grubba)  master_session = RequestID2(); master_session->remoteaddr = (fd->query_address()/" ")[0];
b0132d1999-10-08Henrik Grubbström (Grubba)  master_session->conf = conf;
6abda82000-08-04Martin Stjernholm  master_session->port_obj = c;
ea50e61999-05-01Henrik Grubbström (Grubba)  master_session->my_fd = fd;
66090c2000-03-10Henrik Grubbström (Grubba)  master_session->misc->defaulted = 1;
ea50e61999-05-01Henrik Grubbström (Grubba)  ::create(fd, got_command, 0, con_closed, ([])); array a = fd->query_address(1)/" "; local_addr = a[0]; local_port = (int)a[1]; call_out(timeout, FTP2_TIMEOUT);
b0132d1999-10-08Henrik Grubbström (Grubba)  string s = c->query_option("FTPWelcome"); s = replace(s, ({ "$roxen_version", "$roxen_build", "$full_version", "$pike_version", "$ident", }), ({ roxen->__roxen_version__, roxen->__roxen_build__, roxen->real_version, version(), roxen->version() }));
ea50e61999-05-01Henrik Grubbström (Grubba)  send(220, s/"\n", 1); } }; void create(object f, object c) {
f72bc92000-02-04Per Hedbor  if (f) {
b0132d1999-10-08Henrik Grubbström (Grubba)  c->sessions++; c->ftp_users++;
ea50e61999-05-01Henrik Grubbström (Grubba)  FTPSession(f, c); } }