1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 1998 - 2017, 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.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 | #include "tool_setup.h"
|
---|
23 |
|
---|
24 | #define ENABLE_CURLX_PRINTF
|
---|
25 | /* use our own printf() functions */
|
---|
26 | #include "curlx.h"
|
---|
27 |
|
---|
28 | #include "tool_cfgable.h"
|
---|
29 | #include "tool_msgs.h"
|
---|
30 | #include "tool_cb_wrt.h"
|
---|
31 |
|
---|
32 | #include "memdebug.h" /* keep this as LAST include */
|
---|
33 |
|
---|
34 | /* create a local file for writing, return TRUE on success */
|
---|
35 | bool tool_create_output_file(struct OutStruct *outs)
|
---|
36 | {
|
---|
37 | struct GlobalConfig *global = outs->config->global;
|
---|
38 | FILE *file;
|
---|
39 |
|
---|
40 | if(!outs->filename || !*outs->filename) {
|
---|
41 | warnf(global, "Remote filename has no length!\n");
|
---|
42 | return FALSE;
|
---|
43 | }
|
---|
44 |
|
---|
45 | if(outs->is_cd_filename) {
|
---|
46 | /* don't overwrite existing files */
|
---|
47 | file = fopen(outs->filename, "rb");
|
---|
48 | if(file) {
|
---|
49 | fclose(file);
|
---|
50 | warnf(global, "Refusing to overwrite %s: %s\n", outs->filename,
|
---|
51 | strerror(EEXIST));
|
---|
52 | return FALSE;
|
---|
53 | }
|
---|
54 | }
|
---|
55 |
|
---|
56 | /* open file for writing */
|
---|
57 | file = fopen(outs->filename, "wb");
|
---|
58 | if(!file) {
|
---|
59 | warnf(global, "Failed to create the file %s: %s\n", outs->filename,
|
---|
60 | strerror(errno));
|
---|
61 | return FALSE;
|
---|
62 | }
|
---|
63 | outs->s_isreg = TRUE;
|
---|
64 | outs->fopened = TRUE;
|
---|
65 | outs->stream = file;
|
---|
66 | outs->bytes = 0;
|
---|
67 | outs->init = 0;
|
---|
68 | return TRUE;
|
---|
69 | }
|
---|
70 |
|
---|
71 | /*
|
---|
72 | ** callback for CURLOPT_WRITEFUNCTION
|
---|
73 | */
|
---|
74 |
|
---|
75 | size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
|
---|
76 | {
|
---|
77 | size_t rc;
|
---|
78 | struct OutStruct *outs = userdata;
|
---|
79 | struct OperationConfig *config = outs->config;
|
---|
80 | size_t bytes = sz * nmemb;
|
---|
81 | bool is_tty = config->global->isatty;
|
---|
82 |
|
---|
83 | /*
|
---|
84 | * Once that libcurl has called back tool_write_cb() the returned value
|
---|
85 | * is checked against the amount that was intended to be written, if
|
---|
86 | * it does not match then it fails with CURLE_WRITE_ERROR. So at this
|
---|
87 | * point returning a value different from sz*nmemb indicates failure.
|
---|
88 | */
|
---|
89 | const size_t failure = bytes ? 0 : 1;
|
---|
90 |
|
---|
91 | #ifdef DEBUGBUILD
|
---|
92 | {
|
---|
93 | char *tty = curlx_getenv("CURL_ISATTY");
|
---|
94 | if(tty) {
|
---|
95 | is_tty = TRUE;
|
---|
96 | curl_free(tty);
|
---|
97 | }
|
---|
98 | }
|
---|
99 |
|
---|
100 | if(config->include_headers) {
|
---|
101 | if(bytes > (size_t)CURL_MAX_HTTP_HEADER) {
|
---|
102 | warnf(config->global, "Header data size exceeds single call write "
|
---|
103 | "limit!\n");
|
---|
104 | return failure;
|
---|
105 | }
|
---|
106 | }
|
---|
107 | else {
|
---|
108 | if(bytes > (size_t)CURL_MAX_WRITE_SIZE) {
|
---|
109 | warnf(config->global, "Data size exceeds single call write limit!\n");
|
---|
110 | return failure;
|
---|
111 | }
|
---|
112 | }
|
---|
113 |
|
---|
114 | {
|
---|
115 | /* Some internal congruency checks on received OutStruct */
|
---|
116 | bool check_fails = FALSE;
|
---|
117 | if(outs->filename) {
|
---|
118 | /* regular file */
|
---|
119 | if(!*outs->filename)
|
---|
120 | check_fails = TRUE;
|
---|
121 | if(!outs->s_isreg)
|
---|
122 | check_fails = TRUE;
|
---|
123 | if(outs->fopened && !outs->stream)
|
---|
124 | check_fails = TRUE;
|
---|
125 | if(!outs->fopened && outs->stream)
|
---|
126 | check_fails = TRUE;
|
---|
127 | if(!outs->fopened && outs->bytes)
|
---|
128 | check_fails = TRUE;
|
---|
129 | }
|
---|
130 | else {
|
---|
131 | /* standard stream */
|
---|
132 | if(!outs->stream || outs->s_isreg || outs->fopened)
|
---|
133 | check_fails = TRUE;
|
---|
134 | if(outs->alloc_filename || outs->is_cd_filename || outs->init)
|
---|
135 | check_fails = TRUE;
|
---|
136 | }
|
---|
137 | if(check_fails) {
|
---|
138 | warnf(config->global, "Invalid output struct data for write callback\n");
|
---|
139 | return failure;
|
---|
140 | }
|
---|
141 | }
|
---|
142 | #endif
|
---|
143 |
|
---|
144 | if(!outs->stream && !tool_create_output_file(outs))
|
---|
145 | return failure;
|
---|
146 |
|
---|
147 | if(is_tty && (outs->bytes < 2000) && !config->terminal_binary_ok) {
|
---|
148 | /* binary output to terminal? */
|
---|
149 | if(memchr(buffer, 0, bytes)) {
|
---|
150 | warnf(config->global, "Binary output can mess up your terminal. "
|
---|
151 | "Use \"--output -\" to tell curl to output it to your terminal "
|
---|
152 | "anyway, or consider \"--output <FILE>\" to save to a file.\n");
|
---|
153 | config->synthetic_error = ERR_BINARY_TERMINAL;
|
---|
154 | return failure;
|
---|
155 | }
|
---|
156 | }
|
---|
157 |
|
---|
158 | rc = fwrite(buffer, sz, nmemb, outs->stream);
|
---|
159 |
|
---|
160 | if(bytes == rc)
|
---|
161 | /* we added this amount of data to the output */
|
---|
162 | outs->bytes += bytes;
|
---|
163 |
|
---|
164 | if(config->readbusy) {
|
---|
165 | config->readbusy = FALSE;
|
---|
166 | curl_easy_pause(config->easy, CURLPAUSE_CONT);
|
---|
167 | }
|
---|
168 |
|
---|
169 | if(config->nobuffer) {
|
---|
170 | /* output buffering disabled */
|
---|
171 | int res = fflush(outs->stream);
|
---|
172 | if(res)
|
---|
173 | return failure;
|
---|
174 | }
|
---|
175 |
|
---|
176 | return rc;
|
---|
177 | }
|
---|