tool_cb_hdr.c 13 KB

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