1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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 https://curl.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 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24/* <DESC> 25 * Upload to FTP, resuming failed transfers. Active mode. 26 * </DESC> 27 */ 28 29#include <stdlib.h> 30#include <stdio.h> 31#include <curl/curl.h> 32 33/* parse headers for Content-Length */ 34static size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, 35 void *stream) 36{ 37 int r; 38 long len = 0; 39 40 r = sscanf(ptr, "Content-Length: %ld\n", &len); 41 if(r) 42 *((long *) stream) = len; 43 44 return size * nmemb; 45} 46 47/* discard downloaded data */ 48static size_t discardfunc(void *ptr, size_t size, size_t nmemb, void *stream) 49{ 50 (void)ptr; 51 (void)stream; 52 return size * nmemb; 53} 54 55/* read data to upload */ 56static size_t readfunc(char *ptr, size_t size, size_t nmemb, void *stream) 57{ 58 FILE *f = stream; 59 size_t n; 60 61 if(ferror(f)) 62 return CURL_READFUNC_ABORT; 63 64 n = fread(ptr, size, nmemb, f) * size; 65 66 return n; 67} 68 69 70static int upload(CURL *curlhandle, const char *remotepath, 71 const char *localpath, long timeout, long tries) 72{ 73 FILE *f; 74 long uploaded_len = 0; 75 CURLcode r = CURLE_GOT_NOTHING; 76 int c; 77 78 f = fopen(localpath, "rb"); 79 if(!f) { 80 perror(NULL); 81 return 0; 82 } 83 84 curl_easy_setopt(curlhandle, CURLOPT_UPLOAD, 1L); 85 86 curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath); 87 88 if(timeout) 89 curl_easy_setopt(curlhandle, CURLOPT_SERVER_RESPONSE_TIMEOUT, timeout); 90 91 curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc); 92 curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &uploaded_len); 93 94 curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, discardfunc); 95 96 curl_easy_setopt(curlhandle, CURLOPT_READFUNCTION, readfunc); 97 curl_easy_setopt(curlhandle, CURLOPT_READDATA, f); 98 99 /* enable active mode */ 100 curl_easy_setopt(curlhandle, CURLOPT_FTPPORT, "-"); 101 102 /* allow the server no more than 7 seconds to connect back */ 103 curl_easy_setopt(curlhandle, CURLOPT_ACCEPTTIMEOUT_MS, 7000L); 104 105 curl_easy_setopt(curlhandle, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L); 106 107 curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L); 108 109 for(c = 0; (r != CURLE_OK) && (c < tries); c++) { 110 /* are we resuming? */ 111 if(c) { /* yes */ 112 /* determine the length of the file already written */ 113 114 /* 115 * With NOBODY and NOHEADER, libcurl will issue a SIZE 116 * command, but the only way to retrieve the result is 117 * to parse the returned Content-Length header. Thus, 118 * getcontentlengthfunc(). We need discardfunc() above 119 * because HEADER will dump the headers to stdout 120 * without it. 121 */ 122 curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 1L); 123 curl_easy_setopt(curlhandle, CURLOPT_HEADER, 1L); 124 125 r = curl_easy_perform(curlhandle); 126 if(r != CURLE_OK) 127 continue; 128 129 curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 0L); 130 curl_easy_setopt(curlhandle, CURLOPT_HEADER, 0L); 131 132 fseek(f, uploaded_len, SEEK_SET); 133 134 curl_easy_setopt(curlhandle, CURLOPT_APPEND, 1L); 135 } 136 else { /* no */ 137 curl_easy_setopt(curlhandle, CURLOPT_APPEND, 0L); 138 } 139 140 r = curl_easy_perform(curlhandle); 141 } 142 143 fclose(f); 144 145 if(r == CURLE_OK) 146 return 1; 147 else { 148 fprintf(stderr, "%s\n", curl_easy_strerror(r)); 149 return 0; 150 } 151} 152 153int main(void) 154{ 155 CURL *curlhandle = NULL; 156 157 curl_global_init(CURL_GLOBAL_ALL); 158 curlhandle = curl_easy_init(); 159 160 upload(curlhandle, "ftp://user:pass@example.com/path/file", "C:\\file", 161 0, 3); 162 163 curl_easy_cleanup(curlhandle); 164 curl_global_cleanup(); 165 166 return 0; 167} 168