11 # define alloca __builtin_alloca
15 # define alloca _alloca
33 #include "wvhttppool.h"
34 #include "wvbufstream.h"
36 #include "wvsslstream.h"
40 WvFtpStream::WvFtpStream(
const WvIPPortAddr &_remaddr, WvStringParm _username,
41 WvStringParm _password)
43 cont(wv::bind(&
WvFtpStream::real_execute, this, _1))
48 last_request_time = time(0);
53 void WvFtpStream::doneurl()
55 log(
"Done URL: %s\n", curl->url);
61 last_request_time = time(0);
66 if (urls.isempty() && waiting_urls.isempty())
71 void WvFtpStream::request_next()
75 if (request_count >= max_requests || waiting_urls.isempty())
84 waiting_urls.unlink_first();
87 log(
"Request #%s: %s\n", request_count, url->url);
88 urls.append(url,
false,
"request_url");
103 if (!curl && !urls.isempty())
105 if (!curl && !waiting_urls.isempty())
106 curl = waiting_urls.first();
108 log(
"URL '%s' is FAILED\n", curl->url);
118 char *WvFtpStream::get_important_line()
127 while (line[3] ==
'-');
128 log(WvLog::Debug5,
">> %s\n", line);
142 if (curl && curl->putstream)
159 if (curl && curl->putstream && curl->putstream->
post_select(si))
168 void *WvFtpStream::real_execute(
void*)
175 log(WvLog::Debug4,
"urls count: %s\n", urls.count());
184 line = get_important_line();
191 if (strncmp(line,
"220", 3))
193 log(
"Server rejected connection: %s\n", line);
194 seterr(
"server rejected connection");
197 print(
"USER %s\r\n", !target.username ?
WvString(
"anonymous") :
199 line = get_important_line();
203 if (!strncmp(line,
"230", 3))
205 log(WvLog::Info,
"Server doesn't need password.\n");
208 else if (!strncmp(line,
"33", 2))
210 print(
"PASS %s\r\n", !password ? DEFAULT_ANON_PW : password);
212 line = get_important_line();
218 log(WvLog::Info,
"Authenticated.\n");
223 log(
"Strange response to PASS command: %s\n", line);
224 seterr(
"strange response to PASS command");
230 log(
"Strange response to USER command: %s\n", line);
231 seterr(
"strange response to USER command");
236 log(WvLog::Debug5,
"<< TYPE I\n");
237 line = get_important_line();
241 if (strncmp(line,
"200", 3))
243 log(
"Strange response to TYPE I command: %s\n", line);
244 seterr(
"strange response to TYPE I command");
249 if (!curl && !urls.isempty())
253 print(
"CWD %s\r\n", curl->url.getfile());
254 line = get_important_line();
258 if (!strncmp(line,
"250", 3))
260 log(WvLog::Debug5,
"This is a directory.\n");
265 line = get_important_line();
273 log(WvLog::Debug4,
"Data port is %s.\n", *dataip);
278 log(
"Can't open data connection.\n");
279 seterr(
"can't open data connection");
285 if (!curl->putstream)
287 print(
"LIST %s\r\n", curl->url.getfile());
290 WvString url_no_pw(
"ftp://%s%s%s%s", curl->url.getuser(),
291 !!curl->url.getuser() ?
"@" :
"",
293 curl->url.getfile());
294 curl->outstream->print(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML "
296 "<html>\n<head>\n<title>%s</title>\n"
297 "<meta http-equiv=\"Content-Type\" "
298 "content=\"text/html; "
299 "charset=ISO-8859-1\">\n"
300 "<base href=\"%s\"/>\n</head>\n"
301 "<style type=\"text/css\">\n"
302 "img { border: 0; padding: 0 2px; vertical-align: "
304 "td { font-family: monospace; padding: 2px 3px; "
305 "text-align: right; vertical-align: bottom; }\n"
306 "td:first-child { text-align: left; padding: "
307 "2px 10px 2px 3px; }\n"
308 "table { border: 0; }\n"
309 "a.symlink { font-style: italic; }\n"
311 "<h1>Index of %s</h1>\n"
312 "<hr/><table>\n", url_no_pw, curl->url, url_no_pw
318 log(
"Target is a directory.\n");
319 seterr(
"target is a directory");
324 else if (!curl->putstream)
325 print(
"RETR %s\r\n", curl->url.getfile());
328 if (curl->create_dirs)
330 print(
"CWD %s\r\n", getdirname(curl->url.getfile()));
331 line = get_important_line();
334 if (strncmp(line,
"250", 3))
336 log(
"Path doesn't exist; creating directories...\n");
340 dirs.
split(getdirname(curl->url.getfile()),
"/");
341 WvStringList::Iter i(dirs);
342 for (i.rewind(); i.next(); )
344 current_dir.append(
WvString(
"/%s", i()));
345 print(
"MKD %s\r\n", current_dir);
346 line = get_important_line();
352 print(
"STOR %s\r\n", curl->url.getfile());
355 log(WvLog::Debug5,
"Waiting for response to %s\n", curl->putstream ?
"STOR" :
356 curl->is_dir ?
"LIST" :
"RETR");
357 line = get_important_line();
361 else if (strncmp(line,
"150", 3))
363 log(
"Strange response to %s command: %s\n",
364 curl->putstream ?
"STOR" :
"RETR", line);
366 curl->putstream ?
"STOR" :
"RETR"));
377 if (line && curl->outstream)
381 curl->outstream->
write(output_line);
383 curl->outstream->
write(
"Unknown format of LIST "
395 int len = curl->putstream->
read(buf,
sizeof(buf));
396 log(WvLog::Debug5,
"Read %s bytes.\n%s\n", len,
hexdump_buffer(buf, len));
400 int wrote = data->
write(buf, len);
401 log(WvLog::Debug5,
"Wrote %s bytes\n", wrote);
405 curl->putstream->
close();
411 int len = data->
read(buf,
sizeof(buf));
412 log(WvLog::Debug5,
"Read %s bytes from remote.\n", len);
414 if (len && curl->outstream)
416 int wrote = curl->outstream->
write(buf, len);
417 log(WvLog::Debug5,
"Wrote %s bytes to local.\n", wrote);
423 if (!data->
isok() || (curl->putstream && !curl->putstream->
isok()))
425 log(
"OK, we should have finished writing!\n");
426 if (curl->putstream && data->
isok())
428 line = get_important_line();
435 if (strncmp(line,
"226", 3))
436 log(
"Unexpected message: %s\n", line);
441 curl->outstream->
write(
"</table><hr/></body>\n"
444 log(WvLog::Debug5,
"Waiting for response to CWD /\n");
445 line = get_important_line();
449 if (strncmp(line,
"250", 3))
450 log(
"Strange resonse to \"CWD /\": %s\n", line);
457 log(
"Why are we here??\n");
471 WvString WvFtpStream::parse_for_links(
char *line)
476 if (curl->is_dir && curl->outstream)
479 int res =
ftpparse(&fp, line, strlen(line));
482 char *linkname = (
char *)alloca(fp.namelen+1);
484 for (i = 0; i < fp.namelen; i++)
486 if (fp.name[i] >= 32)
487 linkname[i] = fp.name[i];
496 if (linkurl.cstr()[linkurl.len()-1] !=
'/')
498 linkurl.append(linkname);
500 curl->outstream->links.append(link,
true);
502 output_line.append(
"<tr>\n");
504 output_line.append(
WvString(
" <td>%s%s</td>\n", linkname,
505 fp.flagtrycwd ?
"/" :
""));
510 output_line.append(
" <td>? bytes</td>\n");
512 output_line.append(
WvString(
" <td>%s bytes</td>\n",
514 if (fp.mtimetype > 0)
515 output_line.append(
WvString(
" <td>%s</td>\n", (fp.mtime)));
517 output_line.append(
" <td>?</td>\n");
520 output_line.append(
" <td></td>\n");
522 output_line.append(
"</tr>\n");
529 WvIPPortAddr *WvFtpStream::parse_pasv_response(
char *line)
531 if (strncmp(line,
"227 ", 4))
533 log(
"Strange response to PASV command: %s\n", line);
534 seterr(
"strange response to PASV command");
541 if (*p ==
'\0' || *p ==
'\r' || *p ==
'\n')
543 log(
"Couldn't parse PASV response: %s\n", line);
544 seterr(
"couldn't parse response to PASV command");
551 for (
int i = 0; i < 4; i++)
556 log(
"Couldn't parse PASV IP: %s\n", line);
557 seterr(
"couldn't parse PASV IP");
566 pasvport = atoi(p)*256;
570 log(
"Couldn't parse PASV IP port: %s\n", line);
571 seterr(
"couldn't parse PASV IP port");
574 pasvport += atoi(++p);
char * edit()
make the string editable, and return a non-const (char*)
virtual void close()
Close this stream.
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
virtual void execute()
The callback() function calls execute(), and then calls the user- specified callback if one is define...
char * blocking_getline(time_t wait_msec, int separator='\n', int readahead=1024)
This is a version of getline() that allows you to block for more data to arrive.
virtual void execute()
The callback() function calls execute(), and then calls the user- specified callback if one is define...
virtual bool flush(time_t msec_timeout)
flush the output buffer, if we can do it without delaying more than msec_timeout milliseconds at a ti...
virtual void close()
Close this stream.
virtual bool isok() const
Is this connection OK? Note: isok() will always be true if !resolved, even though fd==-1.
static WvString strerror(int errnum)
A replacement for the operating system ::strerror() function that can map more kinds of error strings...
bool alarm_was_ticking
This will be true during callback execution if the callback was triggered by the alarm going off.
char * trim_string(char *string)
Trims whitespace from the beginning and end of the character string, including carriage return / line...
virtual void close()
Close the stream if it is open; isok() becomes false from now on.
virtual void seterr(int _errnum)
Override seterr() from WvError so that it auto-closes the stream.
virtual bool isok() const
return true if the stream is actually usable right now
WvTCPConn tries to make all outgoing connections asynchronously (in the background).
WvString is an implementation of a simple and efficient printable-string class.
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
virtual bool isreadable()
Returns true if the stream is readable.
the data structure used by pre_select()/post_select() and internally by select().
An IP+Port address also includes a port number, with the resulting form www.xxx.yyy....
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
virtual size_t read(void *buf, size_t count)
read a data block on the stream.
void alarm(time_t msec_timeout)
set an alarm, ie.
virtual bool isok() const
return true if the stream is actually usable right now
virtual void pre_select(SelectInfo &si)
override pre_select() to cause select() results when resolving names.
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
virtual int geterr() const
If isok() is false, return the system error number corresponding to the error, -1 for a special error...
A SelectRequest is a convenient way to remember what we want to do to a particular stream: read from ...
virtual void close()
Closes the file descriptors.
virtual bool isok() const
return true if the stream is actually usable right now
WvString hexdump_buffer(const void *buf, size_t len, bool charRep=true)
Produce a hexadecimal dump of the data buffer in 'buf' of length 'len'.
virtual bool post_select(SelectInfo &si)
override post_select() to set the 'connected' variable as soon as we are connected.
This is a WvList of WvStrings, and is a really handy way to parse strings.
void split(WvStringParm s, const char *splitchars=" \t\r\n", int limit=0)
split s and form a list ignoring splitchars (except at beginning and end) ie.
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.