| /*************************************************************************** |
| * _ _ ____ _ |
| * Project ___| | | | _ \| | |
| * / __| | | | |_) | | |
| * | (__| |_| | _ <| |___ |
| * \___|\___/|_| \_\_____| |
| * |
| * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at https://curl.haxx.se/docs/copyright.html. |
| * |
| * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| * copies of the Software, and permit persons to whom the Software is |
| * furnished to do so, under the terms of the COPYING file. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ***************************************************************************/ |
| #include "tool_setup.h" |
| #define ENABLE_CURLX_PRINTF |
| /* use our own printf() functions */ |
| #include "curlx.h" |
| #include "tool_cfgable.h" |
| #include "tool_writeout.h" |
| #include "tool_writeout_json.h" |
| |
| #include "memdebug.h" /* keep this as LAST include */ |
| |
| static const struct writeoutvar variables[] = { |
| {"url_effective", VAR_EFFECTIVE_URL, 0, |
| CURLINFO_EFFECTIVE_URL, JSON_STRING}, |
| {"method", VAR_EFFECTIVE_METHOD, 0, |
| CURLINFO_EFFECTIVE_METHOD, JSON_STRING}, |
| {"http_code", VAR_HTTP_CODE, 0, |
| CURLINFO_RESPONSE_CODE, JSON_LONG}, |
| {"response_code", VAR_HTTP_CODE, 0, |
| CURLINFO_RESPONSE_CODE, JSON_LONG}, |
| {"num_headers", VAR_NUM_HEADERS, 0, |
| 0, JSON_LONG}, |
| {"http_connect", VAR_HTTP_CODE_PROXY, 0, |
| CURLINFO_HTTP_CONNECTCODE, JSON_LONG}, |
| {"time_total", VAR_TOTAL_TIME, 0, |
| CURLINFO_TOTAL_TIME_T, JSON_TIME}, |
| {"time_namelookup", VAR_NAMELOOKUP_TIME, 0, |
| CURLINFO_NAMELOOKUP_TIME_T, JSON_TIME}, |
| {"time_connect", VAR_CONNECT_TIME, 0, |
| CURLINFO_CONNECT_TIME_T, JSON_TIME}, |
| {"time_appconnect", VAR_APPCONNECT_TIME, 0, |
| CURLINFO_APPCONNECT_TIME_T, JSON_TIME}, |
| {"time_pretransfer", VAR_PRETRANSFER_TIME, 0, |
| CURLINFO_PRETRANSFER_TIME_T, JSON_TIME}, |
| {"time_starttransfer", VAR_STARTTRANSFER_TIME, 0, |
| CURLINFO_STARTTRANSFER_TIME_T, JSON_TIME}, |
| {"size_header", VAR_HEADER_SIZE, 0, |
| CURLINFO_HEADER_SIZE, JSON_LONG}, |
| {"size_request", VAR_REQUEST_SIZE, 0, |
| CURLINFO_REQUEST_SIZE, JSON_LONG}, |
| {"size_download", VAR_SIZE_DOWNLOAD, 0, |
| CURLINFO_SIZE_DOWNLOAD_T, JSON_OFFSET}, |
| {"size_upload", VAR_SIZE_UPLOAD, 0, |
| CURLINFO_SIZE_UPLOAD_T, JSON_OFFSET}, |
| {"speed_download", VAR_SPEED_DOWNLOAD, 0, |
| CURLINFO_SPEED_DOWNLOAD_T, JSON_OFFSET}, |
| {"speed_upload", VAR_SPEED_UPLOAD, 0, |
| CURLINFO_SPEED_UPLOAD_T, JSON_OFFSET}, |
| {"content_type", VAR_CONTENT_TYPE, 0, |
| CURLINFO_CONTENT_TYPE, JSON_STRING}, |
| {"num_connects", VAR_NUM_CONNECTS, 0, |
| CURLINFO_NUM_CONNECTS, JSON_LONG}, |
| {"time_redirect", VAR_REDIRECT_TIME, 0, |
| CURLINFO_REDIRECT_TIME_T, JSON_TIME}, |
| {"num_redirects", VAR_REDIRECT_COUNT, 0, |
| CURLINFO_REDIRECT_COUNT, JSON_LONG}, |
| {"ftp_entry_path", VAR_FTP_ENTRY_PATH, 0, |
| CURLINFO_FTP_ENTRY_PATH, JSON_STRING}, |
| {"redirect_url", VAR_REDIRECT_URL, 0, |
| CURLINFO_REDIRECT_URL, JSON_STRING}, |
| {"ssl_verify_result", VAR_SSL_VERIFY_RESULT, 0, |
| CURLINFO_SSL_VERIFYRESULT, JSON_LONG}, |
| {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT, 0, |
| CURLINFO_PROXY_SSL_VERIFYRESULT, JSON_LONG}, |
| {"filename_effective", VAR_EFFECTIVE_FILENAME, 0, |
| 0, JSON_FILENAME}, |
| {"remote_ip", VAR_PRIMARY_IP, 0, |
| CURLINFO_PRIMARY_IP, JSON_STRING}, |
| {"remote_port", VAR_PRIMARY_PORT, 0, |
| CURLINFO_PRIMARY_PORT, JSON_LONG}, |
| {"local_ip", VAR_LOCAL_IP, 0, |
| CURLINFO_LOCAL_IP, JSON_STRING}, |
| {"local_port", VAR_LOCAL_PORT, 0, |
| CURLINFO_LOCAL_PORT, JSON_LONG}, |
| {"http_version", VAR_HTTP_VERSION, 0, |
| CURLINFO_HTTP_VERSION, JSON_VERSION}, |
| {"scheme", VAR_SCHEME, 0, |
| CURLINFO_SCHEME, JSON_STRING}, |
| {"stdout", VAR_STDOUT, 1, |
| 0, JSON_NONE}, |
| {"stderr", VAR_STDERR, 1, |
| 0, JSON_NONE}, |
| {"json", VAR_JSON, 1, |
| 0, JSON_NONE}, |
| {NULL, VAR_NONE, 1, |
| 0, JSON_NONE} |
| }; |
| |
| void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo) |
| { |
| FILE *stream = stdout; |
| const char *ptr = writeinfo; |
| char *stringp = NULL; |
| long longinfo; |
| double doubleinfo; |
| |
| while(ptr && *ptr) { |
| if('%' == *ptr && ptr[1]) { |
| if('%' == ptr[1]) { |
| /* an escaped %-letter */ |
| fputc('%', stream); |
| ptr += 2; |
| } |
| else { |
| /* this is meant as a variable to output */ |
| char *end; |
| if('{' == ptr[1]) { |
| char keepit; |
| int i; |
| bool match = FALSE; |
| end = strchr(ptr, '}'); |
| ptr += 2; /* pass the % and the { */ |
| if(!end) { |
| fputs("%{", stream); |
| continue; |
| } |
| keepit = *end; |
| *end = 0; /* null-terminate */ |
| for(i = 0; variables[i].name; i++) { |
| if(curl_strequal(ptr, variables[i].name)) { |
| match = TRUE; |
| switch(variables[i].id) { |
| case VAR_EFFECTIVE_URL: |
| if((CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp)) |
| && stringp) |
| fputs(stringp, stream); |
| break; |
| case VAR_EFFECTIVE_METHOD: |
| if((CURLE_OK == curl_easy_getinfo(curl, |
| CURLINFO_EFFECTIVE_METHOD, |
| &stringp)) |
| && stringp) |
| fputs(stringp, stream); |
| break; |
| case VAR_HTTP_CODE: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &longinfo)) |
| fprintf(stream, "%03ld", longinfo); |
| break; |
| case VAR_NUM_HEADERS: |
| fprintf(stream, "%ld", per->num_headers); |
| break; |
| case VAR_HTTP_CODE_PROXY: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE, |
| &longinfo)) |
| fprintf(stream, "%03ld", longinfo); |
| break; |
| case VAR_HEADER_SIZE: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &longinfo)) |
| fprintf(stream, "%ld", longinfo); |
| break; |
| case VAR_REQUEST_SIZE: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &longinfo)) |
| fprintf(stream, "%ld", longinfo); |
| break; |
| case VAR_NUM_CONNECTS: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &longinfo)) |
| fprintf(stream, "%ld", longinfo); |
| break; |
| case VAR_REDIRECT_COUNT: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &longinfo)) |
| fprintf(stream, "%ld", longinfo); |
| break; |
| case VAR_REDIRECT_TIME: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME, |
| &doubleinfo)) |
| fprintf(stream, "%.6f", doubleinfo); |
| break; |
| case VAR_TOTAL_TIME: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &doubleinfo)) |
| fprintf(stream, "%.6f", doubleinfo); |
| break; |
| case VAR_NAMELOOKUP_TIME: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, |
| &doubleinfo)) |
| fprintf(stream, "%.6f", doubleinfo); |
| break; |
| case VAR_CONNECT_TIME: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &doubleinfo)) |
| fprintf(stream, "%.6f", doubleinfo); |
| break; |
| case VAR_APPCONNECT_TIME: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, |
| &doubleinfo)) |
| fprintf(stream, "%.6f", doubleinfo); |
| break; |
| case VAR_PRETRANSFER_TIME: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, |
| &doubleinfo)) |
| fprintf(stream, "%.6f", doubleinfo); |
| break; |
| case VAR_STARTTRANSFER_TIME: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, |
| &doubleinfo)) |
| fprintf(stream, "%.6f", doubleinfo); |
| break; |
| case VAR_SIZE_UPLOAD: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &doubleinfo)) |
| fprintf(stream, "%.0f", doubleinfo); |
| break; |
| case VAR_SIZE_DOWNLOAD: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, |
| &doubleinfo)) |
| fprintf(stream, "%.0f", doubleinfo); |
| break; |
| case VAR_SPEED_DOWNLOAD: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD, |
| &doubleinfo)) |
| fprintf(stream, "%.3f", doubleinfo); |
| break; |
| case VAR_SPEED_UPLOAD: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &doubleinfo)) |
| fprintf(stream, "%.3f", doubleinfo); |
| break; |
| case VAR_CONTENT_TYPE: |
| if((CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &stringp)) |
| && stringp) |
| fputs(stringp, stream); |
| break; |
| case VAR_FTP_ENTRY_PATH: |
| if((CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_FTP_ENTRY_PATH, &stringp)) |
| && stringp) |
| fputs(stringp, stream); |
| break; |
| case VAR_REDIRECT_URL: |
| if((CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &stringp)) |
| && stringp) |
| fputs(stringp, stream); |
| break; |
| case VAR_SSL_VERIFY_RESULT: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT, |
| &longinfo)) |
| fprintf(stream, "%ld", longinfo); |
| break; |
| case VAR_PROXY_SSL_VERIFY_RESULT: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_PROXY_SSL_VERIFYRESULT, |
| &longinfo)) |
| fprintf(stream, "%ld", longinfo); |
| break; |
| case VAR_EFFECTIVE_FILENAME: |
| if(per->outs.filename) |
| fputs(per->outs.filename, stream); |
| break; |
| case VAR_PRIMARY_IP: |
| if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP, |
| &stringp)) && stringp) |
| fputs(stringp, stream); |
| break; |
| case VAR_PRIMARY_PORT: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_PRIMARY_PORT, |
| &longinfo)) |
| fprintf(stream, "%ld", longinfo); |
| break; |
| case VAR_LOCAL_IP: |
| if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_LOCAL_IP, |
| &stringp)) && stringp) |
| fputs(stringp, stream); |
| break; |
| case VAR_LOCAL_PORT: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_LOCAL_PORT, |
| &longinfo)) |
| fprintf(stream, "%ld", longinfo); |
| break; |
| case VAR_HTTP_VERSION: |
| if(CURLE_OK == |
| curl_easy_getinfo(curl, CURLINFO_HTTP_VERSION, |
| &longinfo)) { |
| const char *version = "0"; |
| switch(longinfo) { |
| case CURL_HTTP_VERSION_1_0: |
| version = "1.0"; |
| break; |
| case CURL_HTTP_VERSION_1_1: |
| version = "1.1"; |
| break; |
| case CURL_HTTP_VERSION_2_0: |
| version = "2"; |
| break; |
| case CURL_HTTP_VERSION_3: |
| version = "3"; |
| break; |
| } |
| |
| fprintf(stream, version); |
| } |
| break; |
| case VAR_SCHEME: |
| if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_SCHEME, |
| &stringp)) && stringp) |
| fputs(stringp, stream); |
| break; |
| case VAR_STDOUT: |
| stream = stdout; |
| break; |
| case VAR_STDERR: |
| stream = stderr; |
| break; |
| case VAR_JSON: |
| ourWriteOutJSON(variables, curl, per, stream); |
| default: |
| break; |
| } |
| break; |
| } |
| } |
| if(!match) { |
| fprintf(stderr, "curl: unknown --write-out variable: '%s'\n", ptr); |
| } |
| ptr = end + 1; /* pass the end */ |
| *end = keepit; |
| } |
| else { |
| /* illegal syntax, then just output the characters that are used */ |
| fputc('%', stream); |
| fputc(ptr[1], stream); |
| ptr += 2; |
| } |
| } |
| } |
| else if('\\' == *ptr && ptr[1]) { |
| switch(ptr[1]) { |
| case 'r': |
| fputc('\r', stream); |
| break; |
| case 'n': |
| fputc('\n', stream); |
| break; |
| case 't': |
| fputc('\t', stream); |
| break; |
| default: |
| /* unknown, just output this */ |
| fputc(*ptr, stream); |
| fputc(ptr[1], stream); |
| break; |
| } |
| ptr += 2; |
| } |
| else { |
| fputc(*ptr, stream); |
| ptr++; |
| } |
| } |
| |
| } |