1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@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 http://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/* Upload to FTP, resuming failed transfers 23 * 24 * Compile for MinGW like this: 25 * gcc -Wall -pedantic -std=c99 ftpuploadwithresume.c -o ftpuploadresume.exe 26 * -lcurl -lmsvcr70 27 * 28 * Written by Philip Bock 29 */ 30 31#include <stdlib.h> 32#include <stdio.h> 33 34#include <curl/curl.h> 35 36#if defined(_MSC_VER) && (_MSC_VER < 1300) 37# error _snscanf requires MSVC 7.0 or later. 38#endif 39 40/* The MinGW headers are missing a few Win32 function definitions, 41 you shouldn't need this if you use VC++ */ 42#if defined(__MINGW32__) && !defined(__MINGW64__) 43int __cdecl _snscanf(const char * input, size_t length, const char * format, ...); 44#endif 45 46 47/* parse headers for Content-Length */ 48size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, void *stream) 49{ 50 int r; 51 long len = 0; 52 53 /* _snscanf() is Win32 specific */ 54 r = _snscanf(ptr, size * nmemb, "Content-Length: %ld\n", &len); 55 56 if (r) /* Microsoft: we don't read the specs */ 57 *((long *) stream) = len; 58 59 return size * nmemb; 60} 61 62/* discard downloaded data */ 63size_t discardfunc(void *ptr, size_t size, size_t nmemb, void *stream) 64{ 65 return size * nmemb; 66} 67 68/* read data to upload */ 69size_t readfunc(void *ptr, size_t size, size_t nmemb, void *stream) 70{ 71 FILE *f = stream; 72 size_t n; 73 74 if (ferror(f)) 75 return CURL_READFUNC_ABORT; 76 77 n = fread(ptr, size, nmemb, f) * size; 78 79 return n; 80} 81 82 83int upload(CURL *curlhandle, const char * remotepath, const char * localpath, 84 long timeout, long tries) 85{ 86 FILE *f; 87 long uploaded_len = 0; 88 CURLcode r = CURLE_GOT_NOTHING; 89 int c; 90 91 f = fopen(localpath, "rb"); 92 if (f == NULL) { 93 perror(NULL); 94 return 0; 95 } 96 97 curl_easy_setopt(curlhandle, CURLOPT_UPLOAD, 1L); 98 99 curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath); 100 101 if (timeout) 102 curl_easy_setopt(curlhandle, CURLOPT_FTP_RESPONSE_TIMEOUT, timeout); 103 104 curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc); 105 curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &uploaded_len); 106 107 curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, discardfunc); 108 109 curl_easy_setopt(curlhandle, CURLOPT_READFUNCTION, readfunc); 110 curl_easy_setopt(curlhandle, CURLOPT_READDATA, f); 111 112 curl_easy_setopt(curlhandle, CURLOPT_FTPPORT, "-"); /* disable passive mode */ 113 curl_easy_setopt(curlhandle, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L); 114 115 curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L); 116 117 for (c = 0; (r != CURLE_OK) && (c < tries); c++) { 118 /* are we resuming? */ 119 if (c) { /* yes */ 120 /* determine the length of the file already written */ 121 122 /* 123 * With NOBODY and NOHEADER, libcurl will issue a SIZE 124 * command, but the only way to retrieve the result is 125 * to parse the returned Content-Length header. Thus, 126 * getcontentlengthfunc(). We need discardfunc() above 127 * because HEADER will dump the headers to stdout 128 * without it. 129 */ 130 curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 1L); 131 curl_easy_setopt(curlhandle, CURLOPT_HEADER, 1L); 132 133 r = curl_easy_perform(curlhandle); 134 if (r != CURLE_OK) 135 continue; 136 137 curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 0L); 138 curl_easy_setopt(curlhandle, CURLOPT_HEADER, 0L); 139 140 fseek(f, uploaded_len, SEEK_SET); 141 142 curl_easy_setopt(curlhandle, CURLOPT_APPEND, 1L); 143 } 144 else { /* no */ 145 curl_easy_setopt(curlhandle, CURLOPT_APPEND, 0L); 146 } 147 148 r = curl_easy_perform(curlhandle); 149 } 150 151 fclose(f); 152 153 if (r == CURLE_OK) 154 return 1; 155 else { 156 fprintf(stderr, "%s\n", curl_easy_strerror(r)); 157 return 0; 158 } 159} 160 161int main(int c, char **argv) 162{ 163 CURL *curlhandle = NULL; 164 165 curl_global_init(CURL_GLOBAL_ALL); 166 curlhandle = curl_easy_init(); 167 168 upload(curlhandle, "ftp://user:pass@example.com/path/file", "C:\\file", 0, 3); 169 170 curl_easy_cleanup(curlhandle); 171 curl_global_cleanup(); 172 173 return 0; 174} 175