/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, 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 "memdebug.h" /* keep this as LAST include */ typedef enum { VAR_NONE, /* must be the first */ VAR_TOTAL_TIME, VAR_NAMELOOKUP_TIME, VAR_CONNECT_TIME, VAR_APPCONNECT_TIME, VAR_PRETRANSFER_TIME, VAR_STARTTRANSFER_TIME, VAR_SIZE_DOWNLOAD, VAR_SIZE_UPLOAD, VAR_SPEED_DOWNLOAD, VAR_SPEED_UPLOAD, VAR_HTTP_CODE, VAR_HTTP_CODE_PROXY, VAR_HEADER_SIZE, VAR_REQUEST_SIZE, VAR_EFFECTIVE_URL, VAR_CONTENT_TYPE, VAR_NUM_CONNECTS, VAR_REDIRECT_TIME, VAR_REDIRECT_COUNT, VAR_FTP_ENTRY_PATH, VAR_REDIRECT_URL, VAR_SSL_VERIFY_RESULT, VAR_EFFECTIVE_FILENAME, VAR_PRIMARY_IP, VAR_PRIMARY_PORT, VAR_LOCAL_IP, VAR_LOCAL_PORT, VAR_HTTP_VERSION, VAR_NUM_OF_VARS /* must be the last */ } replaceid; struct variable { const char *name; replaceid id; }; static const struct variable replacements[]={ {"url_effective", VAR_EFFECTIVE_URL}, {"http_code", VAR_HTTP_CODE}, {"response_code", VAR_HTTP_CODE}, {"http_connect", VAR_HTTP_CODE_PROXY}, {"time_total", VAR_TOTAL_TIME}, {"time_namelookup", VAR_NAMELOOKUP_TIME}, {"time_connect", VAR_CONNECT_TIME}, {"time_appconnect", VAR_APPCONNECT_TIME}, {"time_pretransfer", VAR_PRETRANSFER_TIME}, {"time_starttransfer", VAR_STARTTRANSFER_TIME}, {"size_header", VAR_HEADER_SIZE}, {"size_request", VAR_REQUEST_SIZE}, {"size_download", VAR_SIZE_DOWNLOAD}, {"size_upload", VAR_SIZE_UPLOAD}, {"speed_download", VAR_SPEED_DOWNLOAD}, {"speed_upload", VAR_SPEED_UPLOAD}, {"content_type", VAR_CONTENT_TYPE}, {"num_connects", VAR_NUM_CONNECTS}, {"time_redirect", VAR_REDIRECT_TIME}, {"num_redirects", VAR_REDIRECT_COUNT}, {"ftp_entry_path", VAR_FTP_ENTRY_PATH}, {"redirect_url", VAR_REDIRECT_URL}, {"ssl_verify_result", VAR_SSL_VERIFY_RESULT}, {"filename_effective", VAR_EFFECTIVE_FILENAME}, {"remote_ip", VAR_PRIMARY_IP}, {"remote_port", VAR_PRIMARY_PORT}, {"local_ip", VAR_LOCAL_IP}, {"local_port", VAR_LOCAL_PORT}, {"http_version", VAR_HTTP_VERSION}, {NULL, VAR_NONE} }; void ourWriteOut(CURL *curl, struct OutStruct *outs, const char *writeinfo) { FILE *stream = stdout; const char *ptr = writeinfo; char *stringp = NULL; long longinfo; double doubleinfo; while(ptr && *ptr) { if('%' == *ptr) { if('%' == ptr[1]) { /* an escaped %-letter */ fputc('%', stream); ptr += 2; } else { /* this is meant as a variable to output */ char *end; char keepit; int i; if(('{' == ptr[1]) && ((end = strchr(ptr, '}')) != NULL)) { bool match = FALSE; ptr += 2; /* pass the % and the { */ keepit = *end; *end = 0; /* zero terminate */ for(i = 0; replacements[i].name; i++) { if(curl_strequal(ptr, replacements[i].name)) { match = TRUE; switch(replacements[i].id) { case VAR_EFFECTIVE_URL: if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &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_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, "%.3f", doubleinfo); break; case VAR_TOTAL_TIME: if(CURLE_OK == curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &doubleinfo)) fprintf(stream, "%.3f", doubleinfo); break; case VAR_NAMELOOKUP_TIME: if(CURLE_OK == curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, &doubleinfo)) fprintf(stream, "%.3f", doubleinfo); break; case VAR_CONNECT_TIME: if(CURLE_OK == curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &doubleinfo)) fprintf(stream, "%.3f", doubleinfo); break; case VAR_APPCONNECT_TIME: if(CURLE_OK == curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &doubleinfo)) fprintf(stream, "%.3f", doubleinfo); break; case VAR_PRETRANSFER_TIME: if(CURLE_OK == curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &doubleinfo)) fprintf(stream, "%.3f", doubleinfo); break; case VAR_STARTTRANSFER_TIME: if(CURLE_OK == curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &doubleinfo)) fprintf(stream, "%.3f", 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_EFFECTIVE_FILENAME: if(outs->filename) fprintf(stream, "%s", outs->filename); break; case VAR_PRIMARY_IP: if(CURLE_OK == curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP, &stringp)) fprintf(stream, "%s", stringp); 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)) fprintf(stream, "%s", stringp); 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; } fprintf(stream, version); } break; 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) { 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++; } } }