tool_cb_hdr.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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. #include "strcase.h"
  26. #ifdef HAVE_UNISTD_H
  27. #include <unistd.h>
  28. #endif
  29. #define ENABLE_CURLX_PRINTF
  30. /* use our own printf() functions */
  31. #include "curlx.h"
  32. #include "tool_cfgable.h"
  33. #include "tool_doswin.h"
  34. #include "tool_msgs.h"
  35. #include "tool_cb_hdr.h"
  36. #include "tool_cb_wrt.h"
  37. #include "tool_operate.h"
  38. #include "tool_libinfo.h"
  39. #include "memdebug.h" /* keep this as LAST include */
  40. static char *parse_filename(const char *ptr, size_t len);
  41. #ifdef _WIN32
  42. #define BOLD "\x1b[1m"
  43. #define BOLDOFF "\x1b[22m"
  44. #else
  45. #define BOLD "\x1b[1m"
  46. /* Switch off bold by setting "all attributes off" since the explicit
  47. bold-off code (21) isn't supported everywhere - like in the mac
  48. Terminal. */
  49. #define BOLDOFF "\x1b[0m"
  50. /* OSC 8 hyperlink escape sequence */
  51. #define LINK "\x1b]8;;"
  52. #define LINKST "\x1b\\"
  53. #define LINKOFF LINK LINKST
  54. #endif
  55. #ifdef LINK
  56. static void write_linked_location(CURL *curl, const char *location,
  57. size_t loclen, FILE *stream);
  58. #endif
  59. /*
  60. ** callback for CURLOPT_HEADERFUNCTION
  61. */
  62. size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
  63. {
  64. struct per_transfer *per = userdata;
  65. struct HdrCbData *hdrcbdata = &per->hdrcbdata;
  66. struct OutStruct *outs = &per->outs;
  67. struct OutStruct *heads = &per->heads;
  68. struct OutStruct *etag_save = &per->etag_save;
  69. const char *str = ptr;
  70. const size_t cb = size * nmemb;
  71. const char *end = (char *)ptr + cb;
  72. const char *scheme = NULL;
  73. if(!per->config)
  74. return CURL_WRITEFUNC_ERROR;
  75. #ifdef DEBUGBUILD
  76. if(size * nmemb > (size_t)CURL_MAX_HTTP_HEADER) {
  77. warnf(per->config->global, "Header data exceeds single call write limit");
  78. return CURL_WRITEFUNC_ERROR;
  79. }
  80. #endif
  81. #ifdef _WIN32
  82. /* Discard incomplete UTF-8 sequence buffered from body */
  83. if(outs->utf8seq[0])
  84. memset(outs->utf8seq, 0, sizeof(outs->utf8seq));
  85. #endif
  86. /*
  87. * Write header data when curl option --dump-header (-D) is given.
  88. */
  89. if(per->config->headerfile && heads->stream) {
  90. size_t rc = fwrite(ptr, size, nmemb, heads->stream);
  91. if(rc != cb)
  92. return rc;
  93. /* flush the stream to send off what we got earlier */
  94. (void)fflush(heads->stream);
  95. }
  96. curl_easy_getinfo(per->curl, CURLINFO_SCHEME, &scheme);
  97. scheme = proto_token(scheme);
  98. if((scheme == proto_http || scheme == proto_https)) {
  99. long response = 0;
  100. curl_easy_getinfo(per->curl, CURLINFO_RESPONSE_CODE, &response);
  101. if(response/100 != 2)
  102. /* only care about these headers in 2xx responses */
  103. ;
  104. /*
  105. * Write etag to file when --etag-save option is given.
  106. */
  107. else if(per->config->etag_save_file && etag_save->stream &&
  108. /* match only header that start with etag (case insensitive) */
  109. checkprefix("etag:", str)) {
  110. const char *etag_h = &str[5];
  111. const char *eot = end - 1;
  112. if(*eot == '\n') {
  113. while(ISBLANK(*etag_h) && (etag_h < eot))
  114. etag_h++;
  115. while(ISSPACE(*eot))
  116. eot--;
  117. if(eot >= etag_h) {
  118. size_t etag_length = eot - etag_h + 1;
  119. /*
  120. * Truncate the etag save stream, it can have an existing etag value.
  121. */
  122. #ifdef HAVE_FTRUNCATE
  123. if(ftruncate(fileno(etag_save->stream), 0)) {
  124. return CURL_WRITEFUNC_ERROR;
  125. }
  126. #else
  127. if(fseek(etag_save->stream, 0, SEEK_SET)) {
  128. return CURL_WRITEFUNC_ERROR;
  129. }
  130. #endif
  131. fwrite(etag_h, size, etag_length, etag_save->stream);
  132. /* terminate with newline */
  133. fputc('\n', etag_save->stream);
  134. (void)fflush(etag_save->stream);
  135. }
  136. }
  137. }
  138. /*
  139. * This callback sets the filename where output shall be written when
  140. * curl options --remote-name (-O) and --remote-header-name (-J) have
  141. * been simultaneously given and additionally server returns an HTTP
  142. * Content-Disposition header specifying a filename property.
  143. */
  144. else if(hdrcbdata->honor_cd_filename &&
  145. (cb > 20) && checkprefix("Content-disposition:", str)) {
  146. const char *p = str + 20;
  147. /* look for the 'filename=' parameter
  148. (encoded filenames (*=) are not supported) */
  149. for(;;) {
  150. char *filename;
  151. size_t len;
  152. while((p < end) && *p && !ISALPHA(*p))
  153. p++;
  154. if(p > end - 9)
  155. break;
  156. if(memcmp(p, "filename=", 9)) {
  157. /* no match, find next parameter */
  158. while((p < end) && *p && (*p != ';'))
  159. p++;
  160. if((p < end) && *p)
  161. continue;
  162. else
  163. break;
  164. }
  165. p += 9;
  166. /* this expression below typecasts 'cb' only to avoid
  167. warning: signed and unsigned type in conditional expression
  168. */
  169. len = (ssize_t)cb - (p - str);
  170. filename = parse_filename(p, len);
  171. if(filename) {
  172. if(outs->stream) {
  173. /* indication of problem, get out! */
  174. free(filename);
  175. return CURL_WRITEFUNC_ERROR;
  176. }
  177. if(per->config->output_dir) {
  178. outs->filename = aprintf("%s/%s", per->config->output_dir,
  179. filename);
  180. free(filename);
  181. if(!outs->filename)
  182. return CURL_WRITEFUNC_ERROR;
  183. }
  184. else
  185. outs->filename = filename;
  186. outs->is_cd_filename = TRUE;
  187. outs->s_isreg = TRUE;
  188. outs->fopened = FALSE;
  189. outs->alloc_filename = TRUE;
  190. hdrcbdata->honor_cd_filename = FALSE; /* done now! */
  191. if(!tool_create_output_file(outs, per->config))
  192. return CURL_WRITEFUNC_ERROR;
  193. }
  194. break;
  195. }
  196. if(!outs->stream && !tool_create_output_file(outs, per->config))
  197. return CURL_WRITEFUNC_ERROR;
  198. }
  199. }
  200. if(hdrcbdata->config->writeout) {
  201. char *value = memchr(ptr, ':', cb);
  202. if(value) {
  203. if(per->was_last_header_empty)
  204. per->num_headers = 0;
  205. per->was_last_header_empty = FALSE;
  206. per->num_headers++;
  207. }
  208. else if(ptr[0] == '\r' || ptr[0] == '\n')
  209. per->was_last_header_empty = TRUE;
  210. }
  211. if(hdrcbdata->config->show_headers &&
  212. (scheme == proto_http || scheme == proto_https ||
  213. scheme == proto_rtsp || scheme == proto_file)) {
  214. /* bold headers only for selected protocols */
  215. char *value = NULL;
  216. if(!outs->stream && !tool_create_output_file(outs, per->config))
  217. return CURL_WRITEFUNC_ERROR;
  218. if(hdrcbdata->global->isatty &&
  219. #ifdef _WIN32
  220. tool_term_has_bold &&
  221. #endif
  222. hdrcbdata->global->styled_output)
  223. value = memchr(ptr, ':', cb);
  224. if(value) {
  225. size_t namelen = value - ptr;
  226. fprintf(outs->stream, BOLD "%.*s" BOLDOFF ":", (int)namelen, ptr);
  227. #ifndef LINK
  228. fwrite(&value[1], cb - namelen - 1, 1, outs->stream);
  229. #else
  230. if(curl_strnequal("Location", ptr, namelen)) {
  231. write_linked_location(per->curl, &value[1], cb - namelen - 1,
  232. outs->stream);
  233. }
  234. else
  235. fwrite(&value[1], cb - namelen - 1, 1, outs->stream);
  236. #endif
  237. }
  238. else
  239. /* not "handled", just show it */
  240. fwrite(ptr, cb, 1, outs->stream);
  241. }
  242. return cb;
  243. }
  244. /*
  245. * Copies a file name part and returns an ALLOCATED data buffer.
  246. */
  247. static char *parse_filename(const char *ptr, size_t len)
  248. {
  249. char *copy;
  250. char *p;
  251. char *q;
  252. char stop = '\0';
  253. /* simple implementation of strndup() */
  254. copy = malloc(len + 1);
  255. if(!copy)
  256. return NULL;
  257. memcpy(copy, ptr, len);
  258. copy[len] = '\0';
  259. p = copy;
  260. if(*p == '\'' || *p == '"') {
  261. /* store the starting quote */
  262. stop = *p;
  263. p++;
  264. }
  265. else
  266. stop = ';';
  267. /* scan for the end letter and stop there */
  268. q = strchr(p, stop);
  269. if(q)
  270. *q = '\0';
  271. /* if the filename contains a path, only use filename portion */
  272. q = strrchr(p, '/');
  273. if(q) {
  274. p = q + 1;
  275. if(!*p) {
  276. Curl_safefree(copy);
  277. return NULL;
  278. }
  279. }
  280. /* If the filename contains a backslash, only use filename portion. The idea
  281. is that even systems that don't handle backslashes as path separators
  282. probably want the path removed for convenience. */
  283. q = strrchr(p, '\\');
  284. if(q) {
  285. p = q + 1;
  286. if(!*p) {
  287. Curl_safefree(copy);
  288. return NULL;
  289. }
  290. }
  291. /* make sure the file name doesn't end in \r or \n */
  292. q = strchr(p, '\r');
  293. if(q)
  294. *q = '\0';
  295. q = strchr(p, '\n');
  296. if(q)
  297. *q = '\0';
  298. if(copy != p)
  299. memmove(copy, p, strlen(p) + 1);
  300. #if defined(_WIN32) || defined(MSDOS)
  301. {
  302. char *sanitized;
  303. SANITIZEcode sc = sanitize_file_name(&sanitized, copy, 0);
  304. Curl_safefree(copy);
  305. if(sc)
  306. return NULL;
  307. copy = sanitized;
  308. }
  309. #endif /* _WIN32 || MSDOS */
  310. /* in case we built debug enabled, we allow an environment variable
  311. * named CURL_TESTDIR to prefix the given file name to put it into a
  312. * specific directory
  313. */
  314. #ifdef DEBUGBUILD
  315. {
  316. char *tdir = curl_getenv("CURL_TESTDIR");
  317. if(tdir) {
  318. char buffer[512]; /* suitably large */
  319. msnprintf(buffer, sizeof(buffer), "%s/%s", tdir, copy);
  320. Curl_safefree(copy);
  321. copy = strdup(buffer); /* clone the buffer, we don't use the libcurl
  322. aprintf() or similar since we want to use the
  323. same memory code as the "real" parse_filename
  324. function */
  325. curl_free(tdir);
  326. }
  327. }
  328. #endif
  329. return copy;
  330. }
  331. #ifdef LINK
  332. /*
  333. * Treat the Location: header specially, by writing a special escape
  334. * sequence that adds a hyperlink to the displayed text. This makes
  335. * the absolute URL of the redirect clickable in supported terminals,
  336. * which couldn't happen otherwise for relative URLs. The Location:
  337. * header is supposed to always be absolute so this theoretically
  338. * shouldn't be needed but the real world returns plenty of relative
  339. * URLs here.
  340. */
  341. static
  342. void write_linked_location(CURL *curl, const char *location, size_t loclen,
  343. FILE *stream) {
  344. /* This would so simple if CURLINFO_REDIRECT_URL were available here */
  345. CURLU *u = NULL;
  346. char *copyloc = NULL, *locurl = NULL, *scheme = NULL, *finalurl = NULL;
  347. const char *loc = location;
  348. size_t llen = loclen;
  349. int space_skipped = 0;
  350. char *vver = getenv("VTE_VERSION");
  351. if(vver) {
  352. long vvn = strtol(vver, NULL, 10);
  353. /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since some
  354. of those versions have formatting bugs. (#10428) */
  355. if(0 < vvn && vvn <= 4801)
  356. goto locout;
  357. }
  358. /* Strip leading whitespace of the redirect URL */
  359. while(llen && (*loc == ' ' || *loc == '\t')) {
  360. ++loc;
  361. --llen;
  362. ++space_skipped;
  363. }
  364. /* Strip the trailing end-of-line characters, normally "\r\n" */
  365. while(llen && (loc[llen-1] == '\n' || loc[llen-1] == '\r'))
  366. --llen;
  367. /* CURLU makes it easy to handle the relative URL case */
  368. u = curl_url();
  369. if(!u)
  370. goto locout;
  371. /* Create a NUL-terminated and whitespace-stripped copy of Location: */
  372. copyloc = malloc(llen + 1);
  373. if(!copyloc)
  374. goto locout;
  375. memcpy(copyloc, loc, llen);
  376. copyloc[llen] = 0;
  377. /* The original URL to use as a base for a relative redirect URL */
  378. if(curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &locurl))
  379. goto locout;
  380. if(curl_url_set(u, CURLUPART_URL, locurl, 0))
  381. goto locout;
  382. /* Redirected location. This can be either absolute or relative. */
  383. if(curl_url_set(u, CURLUPART_URL, copyloc, 0))
  384. goto locout;
  385. if(curl_url_get(u, CURLUPART_URL, &finalurl, CURLU_NO_DEFAULT_PORT))
  386. goto locout;
  387. if(curl_url_get(u, CURLUPART_SCHEME, &scheme, 0))
  388. goto locout;
  389. if(!strcmp("http", scheme) ||
  390. !strcmp("https", scheme) ||
  391. !strcmp("ftp", scheme) ||
  392. !strcmp("ftps", scheme)) {
  393. fprintf(stream, "%.*s" LINK "%s" LINKST "%.*s" LINKOFF,
  394. space_skipped, location,
  395. finalurl,
  396. (int)loclen - space_skipped, loc);
  397. goto locdone;
  398. }
  399. /* Not a "safe" URL: don't linkify it */
  400. locout:
  401. /* Write the normal output in case of error or unsafe */
  402. fwrite(location, loclen, 1, stream);
  403. locdone:
  404. if(u) {
  405. curl_free(finalurl);
  406. curl_free(scheme);
  407. curl_url_cleanup(u);
  408. free(copyloc);
  409. }
  410. }
  411. #endif