00001
00002
00003
00004
00005
00006
00007
00008
00009
#include <ctype.h>
00010
#include <time.h>
00011
#include "wvhttppool.h"
00012
#include "wvbufstream.h"
00013
#include "wvtcp.h"
00014
#include "wvsslstream.h"
00015
#include "strutils.h"
00016
00017 WvFtpStream::WvFtpStream(
const WvIPPortAddr &_remaddr,
WvStringParm _username,
00018
WvStringParm _password)
00019 :
WvUrlStream(_remaddr, _username,
WvString("FTP %s", _remaddr))
00020 {
00021 data = NULL;
00022 logged_in =
false;
00023 password = _password;
00024 uses_continue_select =
true;
00025 last_request_time = time(0);
00026 alarm(60000);
00027 }
00028
00029
00030 WvFtpStream::~WvFtpStream()
00031 {
00032
close();
00033 }
00034
00035
00036
void WvFtpStream::doneurl()
00037 {
00038
log(
"Done URL: %s\n", curl->
url);
00039
00040 curl->
done();
00041 curl = NULL;
00042
if (data)
00043 {
00044
delete data;
00045 data = NULL;
00046 }
00047
urls.unlink_first();
00048 last_request_time = time(0);
00049
alarm(60000);
00050
request_next();
00051 }
00052
00053
00054
void WvFtpStream::request_next()
00055 {
00056
00057
00058
if (
request_count >=
max_requests ||
waiting_urls.isempty())
00059
return;
00060
00061
if (!
urls.isempty())
00062
return;
00063
00064
00065
WvUrlRequest *url =
waiting_urls.first();
00066
00067
waiting_urls.unlink_first();
00068
00069
request_count++;
00070
log(
"Request #%s: %s\n", request_count, url->
url);
00071
urls.append(url,
false,
"request_url");
00072
alarm(0);
00073 }
00074
00075
00076 void WvFtpStream::close()
00077 {
00078
if (
isok())
00079
log(
"Closing.\n");
00080
WvStreamClone::close();
00081
00082
if (
geterr())
00083 {
00084
00085
00086
if (!curl && !urls.isempty())
00087 curl = urls.first();
00088
if (!curl && !waiting_urls.isempty())
00089 curl = waiting_urls.first();
00090
if (curl)
00091
log(
"URL '%s' is FAILED\n", curl->
url);
00092
if (curl)
00093 curl->
done();
00094 }
00095
00096
if (curl)
00097 curl->
done();
00098 }
00099
00100
00101
char *WvFtpStream::get_important_line(
int timeout)
00102 {
00103
char *line;
00104
do
00105 {
00106 line = getline(timeout);
00107
if (!line)
00108
return NULL;
00109 }
00110
while (line[3] ==
'-');
00111
return line;
00112 }
00113
00114
00115 bool WvFtpStream::pre_select(SelectInfo &si)
00116 {
00117 SelectRequest oldwant = si.wants;
00118
00119
if (WvUrlStream::pre_select(si))
00120
return true;
00121
00122
if (data && data->
pre_select(si))
00123
return true;
00124
00125
if (curl && curl->
putstream && curl->
putstream->
pre_select(si))
00126
return true;
00127
00128 si.wants = oldwant;
00129
00130
return false;
00131 }
00132
00133
00134 bool WvFtpStream::post_select(SelectInfo &si)
00135 {
00136 SelectRequest oldwant = si.wants;
00137
00138
if (WvUrlStream::post_select(si))
00139
return true;
00140
00141
if (data && data->
post_select(si))
00142
return true;
00143
00144
if (curl && curl->
putstream && curl->
putstream->
post_select(si))
00145
return true;
00146
00147 si.wants = oldwant;
00148
00149
return false;
00150 }
00151
00152
00153 void WvFtpStream::execute()
00154 {
00155
char buf[1024], *line;
00156
WvStreamClone::execute();
00157
00158
if (alarm_was_ticking && ((last_request_time + 60) <= time(0)))
00159 {
00160
log(WvLog::Debug4,
"urls count: %s\n", urls.count());
00161
if (urls.isempty())
00162
close();
00163
00164
return;
00165 }
00166
00167
if (!logged_in)
00168 {
00169 line = get_important_line(60000);
00170
if (!line)
00171
return;
00172
if (strncmp(line,
"220", 3))
00173 {
00174
log(
"Server rejected connection: %s\n", line);
00175 seterr(
"server rejected connection");
00176
return;
00177 }
00178
00179
log(WvLog::Debug5,
"Got greeting: %s\n", line);
00180 write(
WvString(
"USER %s\r\n",
00181 !target.username ?
"anonymous" :
00182 target.username.cstr()));
00183 line = get_important_line(60000);
00184
if (!line)
00185
return;
00186
00187
if (!strncmp(line,
"230", 3))
00188 {
00189
log(WvLog::Debug5,
"Server doesn't need password.\n");
00190 logged_in =
true;
00191 }
00192
else if (!strncmp(line,
"33", 2))
00193 {
00194 write(
WvString(
"PASS %s\r\n", !password ? DEFAULT_ANON_PW :
00195 password));
00196 line = get_important_line(60000);
00197
if (!line)
00198
return;
00199
00200
if (line[0] ==
'2')
00201 {
00202
log(WvLog::Debug5,
"Authenticated.\n");
00203 logged_in =
true;
00204 }
00205
else
00206 {
00207
log(
"Strange response to PASS command: %s\n", line);
00208 seterr(
"strange response to PASS command");
00209
return;
00210 }
00211 }
00212
else
00213 {
00214
log(
"Strange response to USER command: %s\n", line);
00215 seterr(
"strange response to USER command");
00216
return;
00217 }
00218
00219 write(
"TYPE I\r\n");
00220 line = get_important_line(60000);
00221
if (!line)
00222
return;
00223
00224
if (strncmp(line,
"200", 3))
00225 {
00226
log(
"Strange response to TYPE I command: %s\n", line);
00227 seterr(
"strange response to TYPE I command");
00228
return;
00229 }
00230 }
00231
00232
if (!curl && !urls.isempty())
00233 {
00234 curl = urls.first();
00235
00236 write(
WvString(
"CWD %s\r\n", curl->
url.
getfile()));
00237 line = get_important_line(60000);
00238
if (!line)
00239
return;
00240
00241
if (!strncmp(line,
"250", 3))
00242 {
00243
log(WvLog::Debug5,
"This is a directory.\n");
00244 curl->
is_dir =
true;
00245 }
00246
00247 write(
"PASV\r\n");
00248 line = get_important_line(60000);
00249
if (!line)
00250
return;
00251
00252
WvIPPortAddr *dataip = parse_pasv_response(line);
00253
00254
if (!dataip)
00255
return;
00256
00257
log(WvLog::Debug4,
"Data port is %s.\n", *dataip);
00258
00259 data =
new WvTCPConn(*dataip);
00260
if (!data)
00261 {
00262
log(
"Can't open data connection.\n");
00263 seterr(
"can't open data connection");
00264
return;
00265 }
00266
00267
if (curl->
is_dir)
00268 {
00269
if (!curl->
putstream)
00270 {
00271 write(
WvString(
"LIST %s\r\n", curl->
url.
getfile()));
00272
if (curl->
outstream)
00273 {
00274
WvString url_no_pw(
"ftp://%s%s%s%s", curl->
url.
getuser(),
00275 !!curl->
url.
getuser() ?
"@" :
"",
00276 curl->
url.
gethost(),
00277 curl->
url.
getfile());
00278 curl->
outstream->
write(
00279
WvString(
00280
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML "
00281
"4.01//EN\">\n"
00282
"<html>\n<head>\n<title>%s</title>\n"
00283
"<meta http-equiv=\"Content-Type\" "
00284
"content=\"text/html; "
00285
"charset=ISO-8859-1\">\n"
00286
"<base href=\"%s\"/>\n</head>\n"
00287
"<style type=\"text/css\">\n"
00288
"img { border: 0; padding: 0 2px; vertical-align: "
00289
"text-bottom; }\n"
00290
"td { font-family: monospace; padding: 2px 3px; "
00291
"text-align: right; vertical-align: bottom; }\n"
00292
"td:first-child { text-align: left; padding: "
00293
"2px 10px 2px 3px; }\n"
00294
"table { border: 0; }\n"
00295
"a.symlink { font-style: italic; }\n"
00296
"</style>\n<body>\n"
00297
"<h1>Index of %s</h1>\n"
00298
"<hr/><table>\n", url_no_pw, curl->
url, url_no_pw
00299 ));
00300 }
00301 }
00302
else
00303 {
00304
log(
"Target is a directory.\n");
00305 seterr(
"target is a directory");
00306
doneurl();
00307
return;
00308 }
00309 }
00310
else if (!curl->
putstream)
00311 write(
WvString(
"RETR %s\r\n", curl->
url.
getfile()));
00312
else
00313 {
00314
if (curl->
create_dirs)
00315 {
00316 write(
WvString(
"CWD %s\r\n",
getdirname(curl->
url.
getfile())));
00317 line = get_important_line(60000);
00318
if (!line)
00319
return;
00320
00321
if (strncmp(line,
"250", 3))
00322 {
00323
log(
"Path doesn't exist; creating directories...\n");
00324
00325
WvString current_dir(
"");
00326
WvStringList dirs;
00327 dirs.
split(
getdirname(curl->
url.
getfile()),
"/");
00328 WvStringList::Iter i(dirs);
00329
for (i.rewind(); i.next(); )
00330 {
00331 current_dir.
append(
WvString(
"/%s", i()));
00332 write(
WvString(
"MKD %s\r\n", current_dir));
00333 line = get_important_line(60000);
00334
if (!line)
00335
return;
00336 }
00337 }
00338 }
00339 write(
WvString(
"STOR %s\r\n", curl->
url.
getfile()));
00340 }
00341
00342
log(WvLog::Debug5,
"Waiting for response to STOR/RETR/LIST\n");
00343 line = get_important_line(60000);
00344
log(
"Response: %s\n", line);
00345
if (!line)
00346
doneurl();
00347
else if (strncmp(line,
"150", 3))
00348 {
00349
log(
"Strange response to %s command: %s\n",
00350 curl->
putstream ?
"STOR" :
"RETR", line);
00351 seterr(
WvString(
"strange response to %s command",
00352 curl->
putstream ?
"STOR" :
"RETR"));
00353
doneurl();
00354 }
00355
00356 }
00357
00358
if (curl)
00359 {
00360
if (curl->
is_dir)
00361 {
00362 line = data->
getline(0);
00363
if (line && curl->
outstream)
00364 {
00365
WvString output_line(parse_for_links(line));
00366
if (!!output_line)
00367 curl->
outstream->
write(output_line);
00368
else
00369 curl->
outstream->
write(
"Unknown format of LIST "
00370
"response\n");
00371 }
00372 }
00373
else
00374 {
00375
if (curl->
putstream)
00376 {
00377
int len = curl->
putstream->
read(buf,
sizeof(buf));
00378
log(WvLog::Debug5,
"Read %s bytes.\n", len);
00379
00380
if (len)
00381 data->
write(buf, len);
00382 }
00383
else
00384 {
00385
int len = data->
read(buf,
sizeof(buf));
00386
log(WvLog::Debug5,
"Read %s bytes.\n", len);
00387
00388
if (len && curl->
outstream)
00389 curl->
outstream->
write(buf, len);
00390 }
00391 }
00392
00393
if (!data->
isok() || (curl->
putstream && !curl->
putstream->
isok()))
00394 {
00395
if (curl->
putstream && data->
isok())
00396 data->
close();
00397 line = get_important_line(60000);
00398
if (!line)
00399 {
00400
doneurl();
00401
return;
00402 }
00403
00404
if (strncmp(line,
"226", 3))
00405
log(
"Unexpected message: %s\n", line);
00406
00407
if (curl->
is_dir)
00408 {
00409
if (curl->
outstream)
00410 curl->
outstream->
write(
"</table><hr/></body>\n"
00411
"</html>\n");
00412 write(
"CWD /\r\n");
00413
log(WvLog::Debug5,
"Waiting for response to CWD /\n");
00414 line = get_important_line(60000);
00415
if (!line)
00416
return;
00417
00418
if (strncmp(line,
"250", 3))
00419
log(
"Strange resonse to \"CWD /\": %s\n", line);
00420
00421 }
00422
doneurl();
00423 }
00424 }
00425 }
00426
00427
00428
WvString WvFtpStream::parse_for_links(
char *line)
00429 {
00430
WvString output_line(
"");
00431
trim_string(line);
00432
00433
if (curl->
is_dir && curl->
outstream)
00434 {
00435
struct ftpparse fp;
00436
int res =
ftpparse(&fp, line, strlen(line));
00437
if (res)
00438 {
00439
char *linkname = (
char *) alloca(fp.
namelen+1);
00440
int i;
00441
for (i = 0; i < fp.
namelen; i++)
00442 {
00443
if (fp.
name[i] >= 32)
00444 linkname[i] = fp.
name[i];
00445
else
00446 {
00447 linkname[i] =
'?';
00448 }
00449 }
00450 linkname[i] = 0;
00451
00452
WvString linkurl(
curl->
url);
00453
if (linkurl.
cstr()[linkurl.
len()-1] !=
'/')
00454 linkurl.
append(
"/");
00455 linkurl.
append(linkname);
00456
WvUrlLink *link =
new WvUrlLink(linkname, linkurl);
00457
curl->
outstream->
links.append(link,
true);
00458
00459 output_line.
append(
"<tr>\n");
00460
00461 output_line.
append(
WvString(
" <td>%s%s</td>\n", linkname,
00462 fp.
flagtrycwd ?
"/" :
""));
00463
00464
if (fp.
flagtryretr)
00465 {
00466
if (!fp.
sizetype)
00467 output_line.
append(
" <td>? bytes</td>\n");
00468
else
00469 output_line.
append(
WvString(
" <td>%s bytes</td>\n",
00470 fp.
size));
00471
if (fp.
mtimetype > 0)
00472 output_line.
append(
WvString(
" <td>%s</td>\n", (fp.
mtime)));
00473
else
00474 output_line.
append(
" <td>?</td>\n");
00475 }
00476
else
00477 output_line.
append(
" <td></td>\n");
00478
00479 output_line.
append(
"</tr>\n");
00480 }
00481 }
00482
return output_line;
00483 }
00484
00485
00486
WvIPPortAddr *WvFtpStream::parse_pasv_response(
char *line)
00487 {
00488
if (strncmp(line,
"227 ", 4))
00489 {
00490
log(
"Strange response to PASV command: %s\n", line);
00491
seterr(
"strange response to PASV command");
00492
return NULL;
00493 }
00494
00495
char *p = &line[3];
00496
while (!isdigit(*p))
00497 {
00498
if (*p ==
'\0' || *p ==
'\r' || *p ==
'\n')
00499 {
00500
log(
"Couldn't parse PASV response: %s\n", line);
00501
seterr(
"couldn't parse response to PASV command");
00502
return NULL;
00503 }
00504 p++;
00505 }
00506
char *ipstart = p;
00507
00508
for (
int i = 0; i < 4; i++)
00509 {
00510 p = strchr(p,
',');
00511
if (!p)
00512 {
00513
log(
"Couldn't parse PASV IP: %s\n", line);
00514
seterr(
"couldn't parse PASV IP");
00515
return NULL;
00516 }
00517 *p =
'.';
00518 }
00519 *p =
'\0';
00520
WvString pasvip(ipstart);
00521 p++;
00522
int pasvport;
00523 pasvport = atoi(p)*256;
00524 p = strchr(p,
',');
00525
if (!p)
00526 {
00527
log(
"Couldn't parse PASV IP port: %s\n", line);
00528
seterr(
"couldn't parse PASV IP port");
00529
return NULL;
00530 }
00531 pasvport += atoi(++p);
00532
00533
WvIPPortAddr *res =
new WvIPPortAddr(pasvip.
cstr(), pasvport);
00534
00535
return res;
00536 }