1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 /* <DESC> 23 * Upload to FTP, resuming failed transfers. 24 * </DESC> 25 */ 26 27 #include <stdlib.h> 28 #include <stdio.h> 29 30 #include <curl/curl.h> 31 32 #if defined(_MSC_VER) && (_MSC_VER < 1300) 33 # error _snscanf requires MSVC 7.0 or later. 34 #endif 35 36 /* The MinGW headers are missing a few Win32 function definitions, 37 you shouldn't need this if you use VC++ */ 38 #if defined(__MINGW32__) && !defined(__MINGW64__) 39 int __cdecl _snscanf(const char *input, size_t length, 40 const char *format, ...); 41 #endif 42 43 44 /* parse headers for Content-Length */ 45 size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, void *stream) 46 { 47 int r; 48 long len = 0; 49 50 /* _snscanf() is Win32 specific */ 51 r = _snscanf(ptr, size * nmemb, "Content-Length: %ld\n", &len); 52 53 if(r) /* Microsoft: we don't read the specs */ 54 *((long *) stream) = len; 55 56 return size * nmemb; 57 } 58 59 /* discard downloaded data */ 60 size_t discardfunc(void *ptr, size_t size, size_t nmemb, void *stream) 61 { 62 return size * nmemb; 63 } 64 65 /* read data to upload */ 66 size_t readfunc(void *ptr, size_t size, size_t nmemb, void *stream) 67 { 68 FILE *f = stream; 69 size_t n; 70 71 if(ferror(f)) 72 return CURL_READFUNC_ABORT; 73 74 n = fread(ptr, size, nmemb, f) * size; 75 76 return n; 77 } 78 79 80 int upload(CURL *curlhandle, const char *remotepath, const char *localpath, 81 long timeout, long tries) 82 { 83 FILE *f; 84 long uploaded_len = 0; 85 CURLcode r = CURLE_GOT_NOTHING; 86 int c; 87 88 f = fopen(localpath, "rb"); 89 if(!f) { 90 perror(NULL); 91 return 0; 92 } 93 94 curl_easy_setopt(curlhandle, CURLOPT_UPLOAD, 1L); 95 96 curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath); 97 98 if(timeout) 99 curl_easy_setopt(curlhandle, CURLOPT_FTP_RESPONSE_TIMEOUT, timeout); 100 101 curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc); 102 curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &uploaded_len); 103 104 curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, discardfunc); 105 106 curl_easy_setopt(curlhandle, CURLOPT_READFUNCTION, readfunc); 107 curl_easy_setopt(curlhandle, CURLOPT_READDATA, f); 108 109 /* disable passive mode */ 110 curl_easy_setopt(curlhandle, CURLOPT_FTPPORT, "-"); 111 curl_easy_setopt(curlhandle, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L); 112 113 curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L); 114 115 for(c = 0; (r != CURLE_OK) && (c < tries); c++) { 116 /* are we resuming? */ 117 if(c) { /* yes */ 118 /* determine the length of the file already written */ 119 120 /* 121 * With NOBODY and NOHEADER, libcurl will issue a SIZE 122 * command, but the only way to retrieve the result is 123 * to parse the returned Content-Length header. Thus, 124 * getcontentlengthfunc(). We need discardfunc() above 125 * because HEADER will dump the headers to stdout 126 * without it. 127 */ 128 curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 1L); 129 curl_easy_setopt(curlhandle, CURLOPT_HEADER, 1L); 130 131 r = curl_easy_perform(curlhandle); 132 if(r != CURLE_OK) 133 continue; 134 135 curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 0L); 136 curl_easy_setopt(curlhandle, CURLOPT_HEADER, 0L); 137 138 fseek(f, uploaded_len, SEEK_SET); 139 140 curl_easy_setopt(curlhandle, CURLOPT_APPEND, 1L); 141 } 142 else { /* no */ 143 curl_easy_setopt(curlhandle, CURLOPT_APPEND, 0L); 144 } 145 146 r = curl_easy_perform(curlhandle); 147 } 148 149 fclose(f); 150 151 if(r == CURLE_OK) 152 return 1; 153 else { 154 fprintf(stderr, "%s\n", curl_easy_strerror(r)); 155 return 0; 156 } 157 } 158 159 int main(int c, char **argv) 160 { 161 CURL *curlhandle = NULL; 162 163 curl_global_init(CURL_GLOBAL_ALL); 164 curlhandle = curl_easy_init(); 165 166 upload(curlhandle, "ftp://user:pass@example.com/path/file", "C:\\file", 167 0, 3); 168 169 curl_easy_cleanup(curlhandle); 170 curl_global_cleanup(); 171 172 return 0; 173 } 174