00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "curl_setup.h"
00024
00025 #ifndef CURL_DISABLE_FILE
00026
00027 #ifdef HAVE_NETINET_IN_H
00028 #include <netinet/in.h>
00029 #endif
00030 #ifdef HAVE_NETDB_H
00031 #include <netdb.h>
00032 #endif
00033 #ifdef HAVE_ARPA_INET_H
00034 #include <arpa/inet.h>
00035 #endif
00036 #ifdef HAVE_NET_IF_H
00037 #include <net/if.h>
00038 #endif
00039 #ifdef HAVE_SYS_IOCTL_H
00040 #include <sys/ioctl.h>
00041 #endif
00042
00043 #ifdef HAVE_SYS_PARAM_H
00044 #include <sys/param.h>
00045 #endif
00046
00047 #ifdef HAVE_FCNTL_H
00048 #include <fcntl.h>
00049 #endif
00050
00051 #include "strtoofft.h"
00052 #include "urldata.h"
00053 #include <curl/curl.h>
00054 #include "progress.h"
00055 #include "sendf.h"
00056 #include "escape.h"
00057 #include "file.h"
00058 #include "speedcheck.h"
00059 #include "getinfo.h"
00060 #include "transfer.h"
00061 #include "url.h"
00062 #include "parsedate.h"
00063 #include "warnless.h"
00064
00065 #include "curl_printf.h"
00066 #include "curl_memory.h"
00067 #include "memdebug.h"
00068
00069 #if defined(WIN32) || defined(MSDOS) || defined(__EMX__) || \
00070 defined(__SYMBIAN32__)
00071 #define DOS_FILESYSTEM 1
00072 #endif
00073
00074 #ifdef OPEN_NEEDS_ARG3
00075 # define open_readonly(p,f) open((p),(f),(0))
00076 #else
00077 # define open_readonly(p,f) open((p),(f))
00078 #endif
00079
00080
00081
00082
00083
00084 static CURLcode file_do(struct connectdata *, bool *done);
00085 static CURLcode file_done(struct connectdata *conn,
00086 CURLcode status, bool premature);
00087 static CURLcode file_connect(struct connectdata *conn, bool *done);
00088 static CURLcode file_disconnect(struct connectdata *conn,
00089 bool dead_connection);
00090 static CURLcode file_setup_connection(struct connectdata *conn);
00091
00092
00093
00094
00095
00096 const struct Curl_handler Curl_handler_file = {
00097 "FILE",
00098 file_setup_connection,
00099 file_do,
00100 file_done,
00101 ZERO_NULL,
00102 file_connect,
00103 ZERO_NULL,
00104 ZERO_NULL,
00105 ZERO_NULL,
00106 ZERO_NULL,
00107 ZERO_NULL,
00108 ZERO_NULL,
00109 file_disconnect,
00110 ZERO_NULL,
00111 0,
00112 CURLPROTO_FILE,
00113 PROTOPT_NONETWORK | PROTOPT_NOURLQUERY
00114 };
00115
00116
00117 static CURLcode file_setup_connection(struct connectdata *conn)
00118 {
00119
00120 conn->data->req.protop = calloc(1, sizeof(struct FILEPROTO));
00121 if(!conn->data->req.protop)
00122 return CURLE_OUT_OF_MEMORY;
00123
00124 return CURLE_OK;
00125 }
00126
00127
00128
00129
00130
00131
00132 static CURLcode file_range(struct connectdata *conn)
00133 {
00134 curl_off_t from, to;
00135 curl_off_t totalsize=-1;
00136 char *ptr;
00137 char *ptr2;
00138 struct Curl_easy *data = conn->data;
00139
00140 if(data->state.use_range && data->state.range) {
00141 from=curlx_strtoofft(data->state.range, &ptr, 0);
00142 while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
00143 ptr++;
00144 to=curlx_strtoofft(ptr, &ptr2, 0);
00145 if(ptr == ptr2) {
00146
00147 to=-1;
00148 }
00149 if((-1 == to) && (from>=0)) {
00150
00151 data->state.resume_from = from;
00152 DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n",
00153 from));
00154 }
00155 else if(from < 0) {
00156
00157 data->req.maxdownload = -from;
00158 data->state.resume_from = from;
00159 DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n",
00160 -from));
00161 }
00162 else {
00163
00164 totalsize = to-from;
00165 data->req.maxdownload = totalsize+1;
00166 data->state.resume_from = from;
00167 DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T
00168 " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
00169 from, data->req.maxdownload));
00170 }
00171 DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T
00172 " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
00173 CURL_FORMAT_CURL_OFF_T " bytes\n",
00174 from, to, data->req.maxdownload));
00175 }
00176 else
00177 data->req.maxdownload = -1;
00178 return CURLE_OK;
00179 }
00180
00181
00182
00183
00184
00185
00186 static CURLcode file_connect(struct connectdata *conn, bool *done)
00187 {
00188 struct Curl_easy *data = conn->data;
00189 char *real_path;
00190 struct FILEPROTO *file = data->req.protop;
00191 int fd;
00192 #ifdef DOS_FILESYSTEM
00193 size_t i;
00194 char *actual_path;
00195 #endif
00196 size_t real_path_len;
00197
00198 CURLcode result = Curl_urldecode(data, data->state.path, 0, &real_path,
00199 &real_path_len, FALSE);
00200 if(result)
00201 return result;
00202
00203 #ifdef DOS_FILESYSTEM
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218 actual_path = real_path;
00219 if((actual_path[0] == '/') &&
00220 actual_path[1] &&
00221 (actual_path[2] == ':' || actual_path[2] == '|')) {
00222 actual_path[2] = ':';
00223 actual_path++;
00224 real_path_len--;
00225 }
00226
00227
00228 for(i=0; i < real_path_len; ++i)
00229 if(actual_path[i] == '/')
00230 actual_path[i] = '\\';
00231 else if(!actual_path[i]) {
00232 Curl_safefree(real_path);
00233 return CURLE_URL_MALFORMAT;
00234 }
00235
00236 fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
00237 file->path = actual_path;
00238 #else
00239 if(memchr(real_path, 0, real_path_len)) {
00240
00241 Curl_safefree(real_path);
00242 return CURLE_URL_MALFORMAT;
00243 }
00244
00245 fd = open_readonly(real_path, O_RDONLY);
00246 file->path = real_path;
00247 #endif
00248 file->freepath = real_path;
00249
00250 file->fd = fd;
00251 if(!data->set.upload && (fd == -1)) {
00252 failf(data, "Couldn't open file %s", data->state.path);
00253 file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
00254 return CURLE_FILE_COULDNT_READ_FILE;
00255 }
00256 *done = TRUE;
00257
00258 return CURLE_OK;
00259 }
00260
00261 static CURLcode file_done(struct connectdata *conn,
00262 CURLcode status, bool premature)
00263 {
00264 struct FILEPROTO *file = conn->data->req.protop;
00265 (void)status;
00266 (void)premature;
00267
00268 if(file) {
00269 Curl_safefree(file->freepath);
00270 file->path = NULL;
00271 if(file->fd != -1)
00272 close(file->fd);
00273 file->fd = -1;
00274 }
00275
00276 return CURLE_OK;
00277 }
00278
00279 static CURLcode file_disconnect(struct connectdata *conn,
00280 bool dead_connection)
00281 {
00282 struct FILEPROTO *file = conn->data->req.protop;
00283 (void)dead_connection;
00284
00285 if(file) {
00286 Curl_safefree(file->freepath);
00287 file->path = NULL;
00288 if(file->fd != -1)
00289 close(file->fd);
00290 file->fd = -1;
00291 }
00292
00293 return CURLE_OK;
00294 }
00295
00296 #ifdef DOS_FILESYSTEM
00297 #define DIRSEP '\\'
00298 #else
00299 #define DIRSEP '/'
00300 #endif
00301
00302 static CURLcode file_upload(struct connectdata *conn)
00303 {
00304 struct FILEPROTO *file = conn->data->req.protop;
00305 const char *dir = strchr(file->path, DIRSEP);
00306 int fd;
00307 int mode;
00308 CURLcode result = CURLE_OK;
00309 struct Curl_easy *data = conn->data;
00310 char *buf = data->state.buffer;
00311 size_t nread;
00312 size_t nwrite;
00313 curl_off_t bytecount = 0;
00314 struct timeval now = Curl_tvnow();
00315 struct_stat file_stat;
00316 const char *buf2;
00317
00318
00319
00320
00321
00322 conn->data->req.upload_fromhere = buf;
00323
00324 if(!dir)
00325 return CURLE_FILE_COULDNT_READ_FILE;
00326
00327 if(!dir[1])
00328 return CURLE_FILE_COULDNT_READ_FILE;
00329
00330 #ifdef O_BINARY
00331 #define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
00332 #else
00333 #define MODE_DEFAULT O_WRONLY|O_CREAT
00334 #endif
00335
00336 if(data->state.resume_from)
00337 mode = MODE_DEFAULT|O_APPEND;
00338 else
00339 mode = MODE_DEFAULT|O_TRUNC;
00340
00341 fd = open(file->path, mode, conn->data->set.new_file_perms);
00342 if(fd < 0) {
00343 failf(data, "Can't open %s for writing", file->path);
00344 return CURLE_WRITE_ERROR;
00345 }
00346
00347 if(-1 != data->state.infilesize)
00348
00349 Curl_pgrsSetUploadSize(data, data->state.infilesize);
00350
00351
00352 if(data->state.resume_from < 0) {
00353 if(fstat(fd, &file_stat)) {
00354 close(fd);
00355 failf(data, "Can't get the size of %s", file->path);
00356 return CURLE_WRITE_ERROR;
00357 }
00358 else
00359 data->state.resume_from = (curl_off_t)file_stat.st_size;
00360 }
00361
00362 while(!result) {
00363 int readcount;
00364 result = Curl_fillreadbuffer(conn, BUFSIZE, &readcount);
00365 if(result)
00366 break;
00367
00368 if(readcount <= 0)
00369 break;
00370
00371 nread = (size_t)readcount;
00372
00373
00374 if(data->state.resume_from) {
00375 if((curl_off_t)nread <= data->state.resume_from) {
00376 data->state.resume_from -= nread;
00377 nread = 0;
00378 buf2 = buf;
00379 }
00380 else {
00381 buf2 = buf + data->state.resume_from;
00382 nread -= (size_t)data->state.resume_from;
00383 data->state.resume_from = 0;
00384 }
00385 }
00386 else
00387 buf2 = buf;
00388
00389
00390 nwrite = write(fd, buf2, nread);
00391 if(nwrite != nread) {
00392 result = CURLE_SEND_ERROR;
00393 break;
00394 }
00395
00396 bytecount += nread;
00397
00398 Curl_pgrsSetUploadCounter(data, bytecount);
00399
00400 if(Curl_pgrsUpdate(conn))
00401 result = CURLE_ABORTED_BY_CALLBACK;
00402 else
00403 result = Curl_speedcheck(data, now);
00404 }
00405 if(!result && Curl_pgrsUpdate(conn))
00406 result = CURLE_ABORTED_BY_CALLBACK;
00407
00408 close(fd);
00409
00410 return result;
00411 }
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421 static CURLcode file_do(struct connectdata *conn, bool *done)
00422 {
00423
00424
00425
00426
00427
00428 CURLcode result = CURLE_OK;
00429 struct_stat statbuf;
00430
00431
00432 curl_off_t expected_size=0;
00433 bool size_known;
00434 bool fstated=FALSE;
00435 ssize_t nread;
00436 struct Curl_easy *data = conn->data;
00437 char *buf = data->state.buffer;
00438 curl_off_t bytecount = 0;
00439 int fd;
00440 struct timeval now = Curl_tvnow();
00441 struct FILEPROTO *file;
00442
00443 *done = TRUE;
00444
00445 Curl_initinfo(data);
00446 Curl_pgrsStartNow(data);
00447
00448 if(data->set.upload)
00449 return file_upload(conn);
00450
00451 file = conn->data->req.protop;
00452
00453
00454 fd = file->fd;
00455
00456
00457 if(-1 != fstat(fd, &statbuf)) {
00458
00459 expected_size = statbuf.st_size;
00460
00461 data->info.filetime = (long)statbuf.st_mtime;
00462 fstated = TRUE;
00463 }
00464
00465 if(fstated && !data->state.range && data->set.timecondition) {
00466 if(!Curl_meets_timecondition(data, (time_t)data->info.filetime)) {
00467 *done = TRUE;
00468 return CURLE_OK;
00469 }
00470 }
00471
00472
00473
00474
00475 if(data->set.opt_no_body && data->set.include_header && fstated) {
00476 time_t filetime;
00477 struct tm buffer;
00478 const struct tm *tm = &buffer;
00479 snprintf(buf, sizeof(data->state.buffer),
00480 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
00481 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
00482 if(result)
00483 return result;
00484
00485 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
00486 (char *)"Accept-ranges: bytes\r\n", 0);
00487 if(result)
00488 return result;
00489
00490 filetime = (time_t)statbuf.st_mtime;
00491 result = Curl_gmtime(filetime, &buffer);
00492 if(result)
00493 return result;
00494
00495
00496 snprintf(buf, BUFSIZE-1,
00497 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
00498 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
00499 tm->tm_mday,
00500 Curl_month[tm->tm_mon],
00501 tm->tm_year + 1900,
00502 tm->tm_hour,
00503 tm->tm_min,
00504 tm->tm_sec);
00505 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
00506 if(!result)
00507
00508 Curl_pgrsSetDownloadSize(data, expected_size);
00509 return result;
00510 }
00511
00512
00513 file_range(conn);
00514
00515
00516
00517 if(data->state.resume_from < 0) {
00518 if(!fstated) {
00519 failf(data, "Can't get the size of file.");
00520 return CURLE_READ_ERROR;
00521 }
00522 else
00523 data->state.resume_from += (curl_off_t)statbuf.st_size;
00524 }
00525
00526 if(data->state.resume_from <= expected_size)
00527 expected_size -= data->state.resume_from;
00528 else {
00529 failf(data, "failed to resume file:// transfer");
00530 return CURLE_BAD_DOWNLOAD_RESUME;
00531 }
00532
00533
00534 if(data->req.maxdownload > 0)
00535 expected_size = data->req.maxdownload;
00536
00537 if(!fstated || (expected_size == 0))
00538 size_known = FALSE;
00539 else
00540 size_known = TRUE;
00541
00542
00543
00544
00545
00546 if(fstated)
00547 Curl_pgrsSetDownloadSize(data, expected_size);
00548
00549 if(data->state.resume_from) {
00550 if(data->state.resume_from !=
00551 lseek(fd, data->state.resume_from, SEEK_SET))
00552 return CURLE_BAD_DOWNLOAD_RESUME;
00553 }
00554
00555 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
00556
00557 while(!result) {
00558
00559 size_t bytestoread;
00560
00561 if(size_known) {
00562 bytestoread =
00563 (expected_size < CURL_OFF_T_C(BUFSIZE) - CURL_OFF_T_C(1)) ?
00564 curlx_sotouz(expected_size) : BUFSIZE - 1;
00565 }
00566 else
00567 bytestoread = BUFSIZE-1;
00568
00569 nread = read(fd, buf, bytestoread);
00570
00571 if(nread > 0)
00572 buf[nread] = 0;
00573
00574 if(nread <= 0 || (size_known && (expected_size == 0)))
00575 break;
00576
00577 bytecount += nread;
00578 if(size_known)
00579 expected_size -= nread;
00580
00581 result = Curl_client_write(conn, CLIENTWRITE_BODY, buf, nread);
00582 if(result)
00583 return result;
00584
00585 Curl_pgrsSetDownloadCounter(data, bytecount);
00586
00587 if(Curl_pgrsUpdate(conn))
00588 result = CURLE_ABORTED_BY_CALLBACK;
00589 else
00590 result = Curl_speedcheck(data, now);
00591 }
00592 if(Curl_pgrsUpdate(conn))
00593 result = CURLE_ABORTED_BY_CALLBACK;
00594
00595 return result;
00596 }
00597
00598 #endif