tool_cb_wrt.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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. #include "tool_setup.h"
  25. #ifdef HAVE_FCNTL_H
  26. /* for open() */
  27. #include <fcntl.h>
  28. #endif
  29. #include <sys/stat.h>
  30. #define ENABLE_CURLX_PRINTF
  31. /* use our own printf() functions */
  32. #include "curlx.h"
  33. #include "tool_cfgable.h"
  34. #include "tool_msgs.h"
  35. #include "tool_cb_wrt.h"
  36. #include "tool_operate.h"
  37. #include "memdebug.h" /* keep this as LAST include */
  38. #ifndef O_BINARY
  39. #define O_BINARY 0
  40. #endif
  41. #ifdef WIN32
  42. #define OPENMODE S_IREAD | S_IWRITE
  43. #else
  44. #define OPENMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
  45. #endif
  46. /* create/open a local file for writing, return TRUE on success */
  47. bool tool_create_output_file(struct OutStruct *outs,
  48. struct OperationConfig *config)
  49. {
  50. struct GlobalConfig *global;
  51. FILE *file = NULL;
  52. char *fname = outs->filename;
  53. char *aname = NULL;
  54. DEBUGASSERT(outs);
  55. DEBUGASSERT(config);
  56. global = config->global;
  57. if(!fname || !*fname) {
  58. warnf(global, "Remote filename has no length!\n");
  59. return FALSE;
  60. }
  61. if(config->output_dir && outs->is_cd_filename) {
  62. aname = aprintf("%s/%s", config->output_dir, fname);
  63. if(!aname) {
  64. errorf(global, "out of memory\n");
  65. return FALSE;
  66. }
  67. fname = aname;
  68. }
  69. if(config->file_clobber_mode == CLOBBER_ALWAYS ||
  70. (config->file_clobber_mode == CLOBBER_DEFAULT &&
  71. !outs->is_cd_filename)) {
  72. /* open file for writing */
  73. file = fopen(fname, "wb");
  74. }
  75. else {
  76. int fd;
  77. do {
  78. fd = open(fname, O_CREAT | O_WRONLY | O_EXCL | O_BINARY, OPENMODE);
  79. /* Keep retrying in the hope that it isn't interrupted sometime */
  80. } while(fd == -1 && errno == EINTR);
  81. if(config->file_clobber_mode == CLOBBER_NEVER && fd == -1) {
  82. int next_num = 1;
  83. size_t len = strlen(fname);
  84. size_t newlen = len + 13; /* nul + 1-11 digits + dot */
  85. char *newname;
  86. /* Guard against wraparound in new filename */
  87. if(newlen < len) {
  88. free(aname);
  89. errorf(global, "overflow in filename generation\n");
  90. return FALSE;
  91. }
  92. newname = malloc(newlen);
  93. if(!newname) {
  94. errorf(global, "out of memory\n");
  95. free(aname);
  96. return FALSE;
  97. }
  98. memcpy(newname, fname, len);
  99. newname[len] = '.';
  100. while(fd == -1 && /* haven't successfully opened a file */
  101. (errno == EEXIST || errno == EISDIR) &&
  102. /* because we keep having files that already exist */
  103. next_num < 100 /* and we haven't reached the retry limit */ ) {
  104. curlx_msnprintf(newname + len + 1, 12, "%d", next_num);
  105. next_num++;
  106. do {
  107. fd = open(newname, O_CREAT | O_WRONLY | O_EXCL | O_BINARY, OPENMODE);
  108. /* Keep retrying in the hope that it isn't interrupted sometime */
  109. } while(fd == -1 && errno == EINTR);
  110. }
  111. outs->filename = newname; /* remember the new one */
  112. outs->alloc_filename = TRUE;
  113. }
  114. /* An else statement to not overwrite existing files and not retry with
  115. new numbered names (which would cover
  116. config->file_clobber_mode == CLOBBER_DEFAULT && outs->is_cd_filename)
  117. is not needed because we would have failed earlier, in the while loop
  118. and `fd` would now be -1 */
  119. if(fd != -1) {
  120. file = fdopen(fd, "wb");
  121. if(!file)
  122. close(fd);
  123. }
  124. }
  125. if(!file) {
  126. warnf(global, "Failed to open the file %s: %s\n", fname,
  127. strerror(errno));
  128. free(aname);
  129. return FALSE;
  130. }
  131. free(aname);
  132. outs->s_isreg = TRUE;
  133. outs->fopened = TRUE;
  134. outs->stream = file;
  135. outs->bytes = 0;
  136. outs->init = 0;
  137. return TRUE;
  138. }
  139. /*
  140. ** callback for CURLOPT_WRITEFUNCTION
  141. */
  142. size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
  143. {
  144. size_t rc;
  145. struct per_transfer *per = userdata;
  146. struct OutStruct *outs = &per->outs;
  147. struct OperationConfig *config = per->config;
  148. size_t bytes = sz * nmemb;
  149. bool is_tty = config->global->isatty;
  150. #ifdef WIN32
  151. CONSOLE_SCREEN_BUFFER_INFO console_info;
  152. intptr_t fhnd;
  153. #endif
  154. #ifdef DEBUGBUILD
  155. {
  156. char *tty = curlx_getenv("CURL_ISATTY");
  157. if(tty) {
  158. is_tty = TRUE;
  159. curl_free(tty);
  160. }
  161. }
  162. if(config->show_headers) {
  163. if(bytes > (size_t)CURL_MAX_HTTP_HEADER) {
  164. warnf(config->global, "Header data size exceeds single call write "
  165. "limit!\n");
  166. return CURL_WRITEFUNC_ERROR;
  167. }
  168. }
  169. else {
  170. if(bytes > (size_t)CURL_MAX_WRITE_SIZE) {
  171. warnf(config->global, "Data size exceeds single call write limit!\n");
  172. return CURL_WRITEFUNC_ERROR;
  173. }
  174. }
  175. {
  176. /* Some internal congruency checks on received OutStruct */
  177. bool check_fails = FALSE;
  178. if(outs->filename) {
  179. /* regular file */
  180. if(!*outs->filename)
  181. check_fails = TRUE;
  182. if(!outs->s_isreg)
  183. check_fails = TRUE;
  184. if(outs->fopened && !outs->stream)
  185. check_fails = TRUE;
  186. if(!outs->fopened && outs->stream)
  187. check_fails = TRUE;
  188. if(!outs->fopened && outs->bytes)
  189. check_fails = TRUE;
  190. }
  191. else {
  192. /* standard stream */
  193. if(!outs->stream || outs->s_isreg || outs->fopened)
  194. check_fails = TRUE;
  195. if(outs->alloc_filename || outs->is_cd_filename || outs->init)
  196. check_fails = TRUE;
  197. }
  198. if(check_fails) {
  199. warnf(config->global, "Invalid output struct data for write callback\n");
  200. return CURL_WRITEFUNC_ERROR;
  201. }
  202. }
  203. #endif
  204. if(!outs->stream && !tool_create_output_file(outs, per->config))
  205. return CURL_WRITEFUNC_ERROR;
  206. if(is_tty && (outs->bytes < 2000) && !config->terminal_binary_ok) {
  207. /* binary output to terminal? */
  208. if(memchr(buffer, 0, bytes)) {
  209. warnf(config->global, "Binary output can mess up your terminal. "
  210. "Use \"--output -\" to tell curl to output it to your terminal "
  211. "anyway, or consider \"--output <FILE>\" to save to a file.\n");
  212. config->synthetic_error = TRUE;
  213. return CURL_WRITEFUNC_ERROR;
  214. }
  215. }
  216. #ifdef WIN32
  217. fhnd = _get_osfhandle(fileno(outs->stream));
  218. if(isatty(fileno(outs->stream)) &&
  219. GetConsoleScreenBufferInfo((HANDLE)fhnd, &console_info)) {
  220. DWORD in_len = (DWORD)(sz * nmemb);
  221. wchar_t* wc_buf;
  222. DWORD wc_len;
  223. /* calculate buffer size for wide characters */
  224. wc_len = MultiByteToWideChar(CP_UTF8, 0, buffer, in_len, NULL, 0);
  225. wc_buf = (wchar_t*) malloc(wc_len * sizeof(wchar_t));
  226. if(!wc_buf)
  227. return CURL_WRITEFUNC_ERROR;
  228. /* calculate buffer size for multi-byte characters */
  229. wc_len = MultiByteToWideChar(CP_UTF8, 0, buffer, in_len, wc_buf, wc_len);
  230. if(!wc_len) {
  231. free(wc_buf);
  232. return CURL_WRITEFUNC_ERROR;
  233. }
  234. if(!WriteConsoleW(
  235. (HANDLE) fhnd,
  236. wc_buf,
  237. wc_len,
  238. &wc_len,
  239. NULL)) {
  240. free(wc_buf);
  241. return CURL_WRITEFUNC_ERROR;
  242. }
  243. free(wc_buf);
  244. rc = bytes;
  245. }
  246. else
  247. #endif
  248. rc = fwrite(buffer, sz, nmemb, outs->stream);
  249. if(bytes == rc)
  250. /* we added this amount of data to the output */
  251. outs->bytes += bytes;
  252. if(config->readbusy) {
  253. config->readbusy = FALSE;
  254. curl_easy_pause(per->curl, CURLPAUSE_CONT);
  255. }
  256. if(config->nobuffer) {
  257. /* output buffering disabled */
  258. int res = fflush(outs->stream);
  259. if(res)
  260. return CURL_WRITEFUNC_ERROR;
  261. }
  262. return rc;
  263. }