From 4a80e32f3e9ecea853184fe8f4d28b01ecba59c2 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Tue, 14 Jul 2026 17:01:03 +0200 Subject: [PATCH] Add secure libcurl transfer backend --- CMakeLists.txt | 8 +- cmake/libprozillaConfig.cmake.in | 1 + src/CMakeLists.txt | 5 +- src/common.h | 2 + src/connection.c | 27 ++-- src/curl-retr.c | 210 +++++++++++++++++++++++++++++++ src/curl-retr.h | 9 ++ src/download.c | 23 ++-- src/ftp-retr.c | 2 +- src/ftp.c | 49 ++++---- src/ftpsearch.c | 15 ++- src/http-retr.c | 16 +-- src/http.c | 10 +- src/main.c | 5 + src/misc.c | 11 +- src/prozilla.h | 8 +- src/url.c | 17 ++- tests/CMakeLists.txt | 3 + tests/url_parse_test.c | 28 +++++ 19 files changed, 361 insertions(+), 88 deletions(-) create mode 100644 src/curl-retr.c create mode 100644 src/curl-retr.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/url_parse_test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5672b1a..a570bda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.20) -project(libprozilla VERSION 1.1.1 LANGUAGES C) +project(libprozilla VERSION 1.2.0 LANGUAGES C) + +include(CTest) include(GNUInstallDirs) include(CheckIncludeFile) @@ -8,6 +10,7 @@ include(CheckFunctionExists) include(CMakePackageConfigHelpers) find_package(Threads REQUIRED) +find_package(CURL 8.17 REQUIRED) find_package(Intl) set(CMAKE_C_STANDARD 11) @@ -39,6 +42,9 @@ configure_file(cmake/config.h.in generated/config.h) add_subdirectory(src) add_subdirectory(po) add_subdirectory(docs) +if(BUILD_TESTING) + add_subdirectory(tests) +endif() configure_package_config_file(cmake/libprozillaConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/libprozillaConfig.cmake" diff --git a/cmake/libprozillaConfig.cmake.in b/cmake/libprozillaConfig.cmake.in index fef6294..d61c918 100644 --- a/cmake/libprozillaConfig.cmake.in +++ b/cmake/libprozillaConfig.cmake.in @@ -1,4 +1,5 @@ @PACKAGE_INIT@ include(CMakeFindDependencyMacro) find_dependency(Threads) +find_dependency(CURL 8.17) include("${CMAKE_CURRENT_LIST_DIR}/libprozillaTargets.cmake") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 82cb34e..3f99740 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,8 +1,8 @@ add_library(prozilla connect.c connection.c debug.c download.c ftp-retr.c ftp.c ftpparse.c - ftpsearch.c http-retr.c http.c logfile.c main.c misc.c netrc.c ping.c url.c) + ftpsearch.c http-retr.c http.c curl-retr.c logfile.c main.c misc.c netrc.c ping.c url.c) add_library(libprozilla::prozilla ALIAS prozilla) -set_target_properties(prozilla PROPERTIES VERSION 1.0.1 SOVERSION 1) +set_target_properties(prozilla PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 1) target_include_directories(prozilla PUBLIC $ @@ -11,6 +11,7 @@ target_include_directories(prozilla target_compile_definitions(prozilla PRIVATE HAVE_CONFIG_H=1 _GNU_SOURCE LOCALEDIR="${LOCALEDIR}") target_compile_options(prozilla PRIVATE $<$:-Wall;-Wextra>) target_link_libraries(prozilla PUBLIC Threads::Threads) +target_link_libraries(prozilla PRIVATE CURL::libcurl) if(Intl_FOUND) target_link_libraries(prozilla PRIVATE Intl::Intl) endif() diff --git a/src/common.h b/src/common.h index 13c6080..4bb2f0c 100644 --- a/src/common.h +++ b/src/common.h @@ -25,6 +25,8 @@ #ifndef COMMON_H #define COMMON_H +#include + #if HAVE_CONFIG_H # include diff --git a/src/connection.c b/src/connection.c index 9bdc852..bf020a6 100644 --- a/src/connection.c +++ b/src/connection.c @@ -25,6 +25,7 @@ #include "prozilla.h" #include "connection.h" +#include "curl-retr.h" #include "misc.h" #include "connect.h" #include "ftp.h" @@ -208,8 +209,8 @@ uerr_t connection_retr_fsize_not_known(connection_t * connection, connection_show_message(connection, _("download for this connection completed" - "%s : %ld received"), connection->localfile, - connection->remote_bytes_received); + "%s : %jd received"), connection->localfile, + (intmax_t) connection->remote_bytes_received); return FILEGETOK; } @@ -290,8 +291,8 @@ uerr_t connection_retr_fsize_known(connection_t * connection, connection_show_message(connection, _("download for this connection completed" - "%s : %ld received"), connection->localfile, - connection->remote_bytes_received); + "%s : %jd received"), connection->localfile, + (intmax_t) connection->remote_bytes_received); return FILEGETOK; } @@ -305,11 +306,13 @@ int connection_load_resume_info(connection_t * connection) { if (connection->remote_startpos - connection->orig_remote_startpos != connection->remote_bytes_received) { - proz_debug("connection->remote start pos before loading %ld", connection->remote_startpos); + proz_debug("connection->remote start pos before loading %jd", + (intmax_t) connection->remote_startpos); //connection->remote_startpos +=connection->remote_bytes_received; connection->remote_startpos += (connection->remote_bytes_received - (connection->remote_startpos - connection->orig_remote_startpos)); - proz_debug("connection->remote start pos after loading %ld", connection->remote_startpos); + proz_debug("connection->remote start pos after loading %jd", + (intmax_t) connection->remote_startpos); } return 1; } @@ -455,11 +458,11 @@ void get_url_info_loop(connection_t * connection) switch (connection->u.proto) { case URLHTTP: - connection->err = http_get_url_info_loop(connection); - break; - case URLFTP: - connection->err = ftp_get_url_info_loop(connection); + case URLHTTPS: + case URLFTPS: + case URLSFTP: + connection->err = curl_get_url_info(connection); break; default: @@ -505,6 +508,10 @@ void get_url_info_loop(connection_t * connection) } } while (connection->err == NEWLOCATION); + pthread_mutex_lock(&connection->access_mutex); + connection->running = FALSE; + pthread_mutex_unlock(&connection->access_mutex); + return; } diff --git a/src/curl-retr.c b/src/curl-retr.c new file mode 100644 index 0000000..78c1ae2 --- /dev/null +++ b/src/curl-retr.c @@ -0,0 +1,210 @@ +#include "common.h" +#include "prozilla.h" +#include "connection.h" +#include "curl-retr.h" +#include "debug.h" + +#include +#include + +typedef struct { + connection_t *connection; + CURL *curl; + int write_failed; + int require_partial_response; + size_t bytes_written; +} curl_write_context; + +static CURLcode configure_curl(CURL *curl, connection_t *connection) +{ + char known_hosts[PATH_MAX]; + proxy_info *proxy = NULL; + + curl_easy_setopt(curl, CURLOPT_URL, connection->u.url); + curl_easy_setopt(curl, CURLOPT_USERAGENT, connection->user_agent); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L); + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, + (long) connection->conn_timeout.tv_sec); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, + (long) connection->xfer_timeout.tv_sec); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); + curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, "http,https,ftp,ftps,sftp"); + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, + "http,https,ftp,ftps,sftp"); + + if (connection->u.user) + curl_easy_setopt(curl, CURLOPT_USERNAME, connection->u.user); + if (connection->u.passwd) + curl_easy_setopt(curl, CURLOPT_PASSWORD, connection->u.passwd); + + if ((connection->u.proto == URLHTTP || connection->u.proto == URLHTTPS) + && connection->http_proxy && connection->http_proxy->use_proxy) + proxy = connection->http_proxy; + else if ((connection->u.proto == URLFTP || connection->u.proto == URLFTPS) + && connection->ftp_proxy && connection->ftp_proxy->use_proxy + && connection->ftp_proxy->type == HTTPPROXY) + proxy = connection->ftp_proxy; + if (proxy && proxy->proxy_url.url) + { + curl_easy_setopt(curl, CURLOPT_PROXY, proxy->proxy_url.url); + if (proxy->username) + curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, proxy->username); + if (proxy->passwd) + curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, proxy->passwd); + } + + if (connection->u.proto == URLSFTP) + { + if (snprintf(known_hosts, sizeof(known_hosts), "%s/.ssh/known_hosts", + libprozrtinfo.home_dir) >= (int) sizeof(known_hosts) + || access(known_hosts, R_OK) != 0) + return CURLE_PEER_FAILED_VERIFICATION; + curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, known_hosts); + } + + return CURLE_OK; +} + +static uerr_t map_curl_error(CURLcode result, long response) +{ + if (result == CURLE_OK && response < 400) + return HOK; + if (response == 401 || response == 403 + || result == CURLE_LOGIN_DENIED) + return HAUTHFAIL; + if (response == 404 || result == CURLE_REMOTE_FILE_NOT_FOUND) + return HTTPNSFOD; + if (result == CURLE_WRITE_ERROR) + return FWRITEERR; + return HERR; +} + +uerr_t curl_get_url_info(connection_t *connection) +{ + CURL *curl = curl_easy_init(); + CURLcode result; + curl_off_t length = -1; + long response = 0; + + if (!curl) + return HERR; + result = configure_curl(curl, connection); + if (result == CURLE_OK) + { + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + result = curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); + curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &length); + } + curl_easy_cleanup(curl); + + if (result != CURLE_OK || response >= 400) + return map_curl_error(result, response); + if (length < 0 || (curl_off_t) (off_t) length != length) + connection->main_file_size = -1; + else + connection->main_file_size = (off_t) length; + connection->hs.contlen = connection->main_file_size; + connection->hs.accept_ranges = 1; + connection->resume_support = TRUE; + return HOK; +} + +static size_t curl_write(void *data, size_t size, size_t count, void *opaque) +{ + curl_write_context *ctx = opaque; + size_t bytes; + long response = 0; + + if (size != 0 && count > SIZE_MAX / size) + return 0; + bytes = size * count; + if (ctx->require_partial_response) + { + curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &response); + if (response != 206) + return 0; + } + if (write_data_with_lock(ctx->connection, data, 1, bytes) != bytes) + { + ctx->write_failed = 1; + return 0; + } + pthread_mutex_lock(&ctx->connection->access_mutex); + ctx->connection->remote_bytes_received += (off_t) bytes; + pthread_mutex_unlock(&ctx->connection->access_mutex); + ctx->bytes_written += bytes; + connection_calc_ratebps(ctx->connection); + connection_throttle_bps(ctx->connection); + return bytes; +} + +static uerr_t curl_get_file(connection_t *connection) +{ + CURL *curl = curl_easy_init(); + CURLcode result; + curl_write_context ctx = { connection, curl, 0, 0, 0 }; + char range[96]; + long response = 0; + + if (!curl) + return HERR; + result = configure_curl(curl, connection); + if (result == CURLE_OK) + { + if (connection->remote_endpos > connection->remote_startpos) + { + snprintf(range, sizeof(range), "%jd-%jd", + (intmax_t) connection->remote_startpos, + (intmax_t) (connection->remote_endpos - 1)); + curl_easy_setopt(curl, CURLOPT_RANGE, range); + if (connection->u.proto == URLHTTP + || connection->u.proto == URLHTTPS) + ctx.require_partial_response = 1; + } + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ctx); + connection_change_status(connection, DOWNLOADING); + gettimeofday(&connection->time_begin, NULL); + result = curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); + } + curl_easy_cleanup(curl); + if (ctx.write_failed) + return FWRITEERR; + if (ctx.require_partial_response && response != 206) + return CANTRESUME; + if (connection->remote_endpos > connection->remote_startpos + && (off_t) ctx.bytes_written + != connection->remote_endpos - connection->remote_startpos) + return READERR; + if (result == CURLE_OK && response < 400) + { + connection_change_status(connection, COMPLETED); + return HOK; + } + connection_change_status(connection, REMOTEFATAL); + return map_curl_error(result, response); +} + +void *curl_loop(void *opaque) +{ + connection_t *connection = opaque; + boolean retry = FALSE; + + do { + if (retry) + delay_ms((int) connection->retry_delay.tv_sec * 1000); + connection->err = curl_get_file(connection); + connection->attempts++; + if (connection->err == HOK || connection->err == FWRITEERR + || connection->err == HTTPNSFOD || connection->err == HAUTHFAIL) + return (void *) (intptr_t) connection->err; + retry = TRUE; + } while (connection->max_attempts == 0 + || connection->attempts < connection->max_attempts); + return (void *) (intptr_t) connection->err; +} diff --git a/src/curl-retr.h b/src/curl-retr.h new file mode 100644 index 0000000..2d4eadf --- /dev/null +++ b/src/curl-retr.h @@ -0,0 +1,9 @@ +#ifndef CURL_RETR_H +#define CURL_RETR_H + +#include "prozilla.h" + +uerr_t curl_get_url_info(connection_t *connection); +void *curl_loop(void *connection); + +#endif diff --git a/src/download.c b/src/download.c index 2dbb815..008ea59 100644 --- a/src/download.c +++ b/src/download.c @@ -24,6 +24,7 @@ #include "common.h" #include "download.h" +#include "curl-retr.h" #include "ftp-retr.h" #include "http-retr.h" #include "logfile.h" @@ -305,25 +306,14 @@ void proz_download_start_downloads(download_t * download, switch (download->pconnections[i]->u.proto) { case URLHTTP: - /* http_loop(&download->connections[i]); */ - if (pthread_create(&download->threads[i], NULL, - (void *)&http_loop, - (void *)(download->pconnections[i])) != 0) - - proz_die(_("Error: Not enough system resources")); - - break; - case URLFTP: - - /* ftp_loop(&download->connections[i]); */ - - + case URLHTTPS: + case URLFTPS: + case URLSFTP: if (pthread_create(&download->threads[i], NULL, - (void *)&ftp_loop, + curl_loop, (void *)(download->pconnections[i])) != 0) proz_die(_("Error: Not enough system resources")); - break; default: @@ -884,7 +874,8 @@ off_t proz_download_get_total_remote_bytes_got(download_t * download) for (i = 0; i < download->num_connections; i++) { - proz_debug("DOWNLOAD_TOTAL_BYTES_RECV=%lld for connection %d", total_bytes_recv, i); + proz_debug("DOWNLOAD_TOTAL_BYTES_RECV=%jd for connection %d", + (intmax_t) total_bytes_recv, i); total_bytes_recv += proz_connection_get_total_remote_bytes_got(download-> pconnections[i]); diff --git a/src/ftp-retr.c b/src/ftp-retr.c index 2d04010..4906fe8 100644 --- a/src/ftp-retr.c +++ b/src/ftp-retr.c @@ -123,7 +123,7 @@ uerr_t proz_ftp_get_file(connection_t * connection) user = user ? user : libprozrtinfo.ftp_default_user; passwd = passwd ? passwd : libprozrtinfo.ftp_default_passwd; - proz_debug(_("Logging in as user %s with password %s."), user, passwd); + proz_debug(_("Logging in as user %s."), user); connection_change_status(connection, LOGGININ); diff --git a/src/ftp.c b/src/ftp.c index c76e513..34c0a85 100644 --- a/src/ftp.c +++ b/src/ftp.c @@ -31,6 +31,7 @@ #include "debug.h" #include "ftpparse.h" #include "ftp.h" +#include /* #define UNIMPLEMENTED_CMD(a) ((a == 500) || (a == 502) || (a == 504)) */ @@ -188,14 +189,17 @@ uerr_t ftp_get_line(connection_t * connection, char *line) connection->szBuffer = &ch; - while ((iBuffLen < BUFFER_SIZE) + while ((iBuffLen < BUFFER_SIZE - 1) && ((ret = ftp_check_msg(connection, 1)) > 0)) { /* Now get the full string. */ iLen = ftp_read_msg(connection, 1); if (iLen != 1) - return FTPERR; + { + connection->szBuffer = NULL; + return FTPERR; + } iBuffLen += iLen; *szptr = ch; @@ -207,13 +211,14 @@ uerr_t ftp_get_line(connection_t * connection, char *line) /* Check for error returned in ftp_check_msg(). */ if (ret == -1) - return FTPERR; + { + connection->szBuffer = NULL; + return FTPERR; + } /* if zero bytes were found that means the server has closed the connection*/ - if (ret == 0) - *(szptr) = '\0'; - else - *(szptr + 1) = '\0'; + *szptr = '\0'; + connection->szBuffer = NULL; proz_debug(_("Received: %s"), line); @@ -393,7 +398,7 @@ uerr_t ftp_rest(connection_t * connection, off_t bytes) { uerr_t err; - err = ftp_send_msg(connection, "REST %lld\r\n", bytes); + err = ftp_send_msg(connection, "REST %jd\r\n", (intmax_t) bytes); if (err != FTPOK) return err; @@ -445,7 +450,6 @@ uerr_t ftp_pwd(connection_t * connection, char *dir) { uerr_t err; char *r, *l; - char szBuffer[FTP_BUFFER_SIZE]; err = ftp_send_msg(connection, "PWD\r\n"); if (err != FTPOK) @@ -477,7 +481,7 @@ uerr_t ftp_pwd(connection_t * connection, char *dir) if ((r = strchr(connection->serv_ret_lines->line, ' ')) != NULL) { *r = '\0'; - strcpy(dir, szBuffer); + strcpy(dir, r + 1); *r = ' '; } } @@ -491,6 +495,8 @@ uerr_t ftp_pwd(connection_t * connection, char *dir) uerr_t ftp_size(connection_t * connection, const char *file, off_t *size) { uerr_t err; + char *end; + intmax_t parsed_size; *size = -1; @@ -505,7 +511,12 @@ uerr_t ftp_size(connection_t * connection, const char *file, off_t *size) /* Now lets figure out what happened. */ if (connection->serv_ret_lines->line[0] == '2') { - sscanf(connection->serv_ret_lines->line + 3, "%lld", size); + errno = 0; + parsed_size = strtoimax(connection->serv_ret_lines->line + 3, &end, 10); + if (errno != 0 || end == connection->serv_ret_lines->line + 3 + || parsed_size < 0 || (intmax_t) (off_t) parsed_size != parsed_size) + return FTPSIZEFAIL; + *size = (off_t) parsed_size; return FTPOK; } else if (connection->serv_ret_lines->line[0] == '5') /* An error occured. */ @@ -902,21 +913,7 @@ uerr_t proz_ftp_get_url_info(connection_t * connection) user = user ? user : libprozrtinfo.ftp_default_user; passwd = passwd ? passwd : libprozrtinfo.ftp_default_passwd; - if (strcmp(user, "anonymous") == 0) - connection_show_message(connection, - _("Logging in as user %s with password %s"), - user, passwd); - else - { - int pwd_len = strlen(passwd); - char *tmp_pwd = (char *)kmalloc(pwd_len + 1); - memset(tmp_pwd, 'x', pwd_len); - tmp_pwd[pwd_len] = 0; - connection_show_message(connection, - _("Logging in as user %s with password %s"), - user, tmp_pwd); - kfree(tmp_pwd); - } + connection_show_message(connection, _("Logging in as user %s"), user); init_response(connection); err = ftp_login(connection, user, passwd); diff --git a/src/ftpsearch.c b/src/ftpsearch.c index 3dd5250..ba90f5d 100644 --- a/src/ftpsearch.c +++ b/src/ftpsearch.c @@ -121,9 +121,9 @@ urlinfo *prepare_lycos_url(ftps_request_t * request, char *ftps_loc, (char *)kmalloc(lycos_url_len + strlen(request->file_name) + 300); sprintf(lycos_url_buf, - "%s?form=advanced&query=%s&doit=Search&type=Exact+search&hits=%d&matches=&hitsprmatch=&limdom=&limpath=&limsize1=%zd&limsize2=%zd&f1=Host&f2=Path&f3=Size&f4=-&f5=-&f6=-&header=none&sort=none&trlen=20", + "%s?form=advanced&query=%s&doit=Search&type=Exact+search&hits=%d&matches=&hitsprmatch=&limdom=&limpath=&limsize1=%jd&limsize2=%jd&f1=Host&f2=Path&f3=Size&f4=-&f5=-&f6=-&header=none&sort=none&trlen=20", ftps_loc, request->file_name, num_req_mirrors, - request->file_size, request->file_size); + (intmax_t) request->file_size, (intmax_t) request->file_size); /* Debugging purposes */ /*sprintf(lycos_url_buf,"localhost/search.html"); */ @@ -161,9 +161,9 @@ urlinfo *prepare_filesearching_url(ftps_request_t * request, char *ftps_loc, (char *)kmalloc(filesearching_url_len + strlen(request->file_name) + 300); sprintf(filesearching_url_buf, - "%s?q=%s&l=en&t=f&e=on&m=%d&o=n&s=on&s1=%zd&s2=%zd&d=&p=&p2=&x=10&y=14", + "%s?q=%s&l=en&t=f&e=on&m=%d&o=n&s=on&s1=%jd&s2=%jd&d=&p=&p2=&x=10&y=14", ftps_loc, request->file_name, num_req_mirrors, - request->file_size, request->file_size); + (intmax_t) request->file_size, (intmax_t) request->file_size); /* Debugging purposes */ /* sprintf(filesearching_url_buf,"localhost/fs.html"); */ @@ -287,7 +287,7 @@ uerr_t parse_lycos_html_mirror_list(ftps_request_t * request, char *p) strncpy(p1, i + 5, j - i - 5); p1[j - i - 5 + 1] = 0; - proz_debug("\nstring len= %ld", strlen(p1)); + proz_debug("\nstring len= %zu", strlen(p1)); p2 = p1; @@ -464,7 +464,7 @@ uerr_t parse_filesearching_html_mirror_list(ftps_request_t * request, char *p) p1 = kmalloc((j - i - 16) + 100); strncpy(p1, i + 16, j - i - 16); - proz_debug("\nstring len= %ld", strlen(p1)); + proz_debug("\nstring len= %zu", strlen(p1)); proz_debug("\nstring value= %s", p1); p1[j - i - 16 + 1] = 0; @@ -640,8 +640,7 @@ uerr_t get_mirror_info(connection_t * connection, char **ret_buf) { /* Construct the necessary header. */ www_auth = get_basic_auth_str(user, passwd, "Authorization"); - proz_debug(_("Authenticating as user %s password %s"), user, passwd); - proz_debug(_("Authentification string=%s"), www_auth); + proz_debug(_("Authenticating as user %s"), user); } else www_auth = 0; diff --git a/src/http-retr.c b/src/http-retr.c index c592dae..ad7e058 100644 --- a/src/http-retr.c +++ b/src/http-retr.c @@ -28,6 +28,7 @@ #include "debug.h" #include "http.h" #include "http-retr.h" +#include /* Will download a portion of/or the full file from the connection->url. */ @@ -117,8 +118,7 @@ uerr_t proz_http_get_file(connection_t * connection) { /* Construct the necessary header. */ www_auth = get_basic_auth_str(user, passwd, "Authorization"); - proz_debug(_("Authenticating as user %s password %s"), user, passwd); - proz_debug(_("Authentification string=%s"), www_auth); + proz_debug(_("Authenticating as user %s"), user); } else www_auth = 0; @@ -147,8 +147,10 @@ uerr_t proz_http_get_file(connection_t * connection) if (connection->hs.accept_ranges == 1) { range = (char *)alloca(18 + 64); - sprintf(range, "Range: bytes=%lld-\r\n", connection->remote_startpos); - proz_debug("Range = %lld Range = %s", connection->remote_startpos, range); + snprintf(range, 18 + 64, "Range: bytes=%jd-\r\n", + (intmax_t) connection->remote_startpos); + proz_debug("Range = %jd Range = %s", + (intmax_t) connection->remote_startpos, range); } if (connection->u.referer) @@ -423,8 +425,7 @@ uerr_t ftp_get_file_from_http_proxy(connection_t * connection) { /* Construct the necessary header. */ www_auth = get_basic_auth_str(user, passwd, "Authorization"); - proz_debug(_("Authenticating as user %s password %s"), user, passwd); - proz_debug(_("Authentification string=%s"), www_auth); + proz_debug(_("Authenticating as user %s"), user); } else www_auth = 0; @@ -442,7 +443,8 @@ uerr_t ftp_get_file_from_http_proxy(connection_t * connection) if (connection->hs.accept_ranges == 1) { range = (char *)alloca(18 + 64); - sprintf(range, "Range: bytes=%lld-\r\n", connection->remote_startpos); + snprintf(range, 18 + 64, "Range: bytes=%jd-\r\n", + (intmax_t) connection->remote_startpos); } diff --git a/src/http.c b/src/http.c index a620b91..908513f 100644 --- a/src/http.c +++ b/src/http.c @@ -261,7 +261,7 @@ off_t hgetlen(const char *hdr) for (len = 0; isdigit(*hdr); hdr++) len = 10 * len + (*hdr - '0'); - proz_debug("contenlen %s contentlen %lld", *hdr, len); + proz_debug("content length %jd", (intmax_t) len); return len; } @@ -297,7 +297,7 @@ off_t hgetrange(const char *hdr) for (len = 0; isdigit(*hdr); hdr++) len = 10 * len + (*hdr - '0'); - proz_debug("range %s range %lld", *hdr, len); + proz_debug("range %jd", (intmax_t) len); return len; } @@ -614,8 +614,7 @@ uerr_t proz_http_get_url_info(connection_t * connection) { /* Construct the necessary header. */ www_auth = get_basic_auth_str(user, passwd, "Authorization"); - proz_debug(_("Authenticating as user %s password %s"), user, passwd); - proz_debug(_("Authentification string=%s"), www_auth); + proz_debug(_("Authenticating as user %s"), user); } else www_auth = 0; @@ -829,8 +828,7 @@ uerr_t ftp_get_url_info_from_http_proxy(connection_t * connection) { /* Construct the necessary header. */ www_auth = get_basic_auth_str(user, passwd, "Authorization"); - proz_debug(_("Authenticating as user %s password %s"), user, passwd); - proz_debug(_("Authentification string=%s"), www_auth); + proz_debug(_("Authenticating as user %s"), user); } else www_auth = 0; diff --git a/src/main.c b/src/main.c index 04787aa..0403a50 100644 --- a/src/main.c +++ b/src/main.c @@ -27,6 +27,7 @@ #include "misc.h" //#include "getopt.h" #include "debug.h" +#include @@ -48,6 +49,9 @@ int proz_init(int argc, char **argv) { // int c; + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) + return 0; + /* Gettext stuff */ setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); @@ -125,6 +129,7 @@ void proz_shutdown(void) kfree(libprozrtinfo.dl_dir); kfree(libprozrtinfo.output_dir); kfree(libprozrtinfo.log_dir); + curl_global_cleanup(); } /****************************************************************************** diff --git a/src/misc.c b/src/misc.c index 748b917..738b0a6 100644 --- a/src/misc.c +++ b/src/misc.c @@ -45,7 +45,7 @@ void *kmalloc(size_t size) ret = malloc(size); if (ret == NULL) - proz_die(_("Failed to malloc() %lu bytes."), size); + proz_die(_("Failed to malloc() %zu bytes."), size); return ret; } @@ -60,7 +60,7 @@ void *krealloc(void *ptr, size_t new_size) ret = realloc(ptr, new_size); if (!ret) - proz_die(_("Failed to realloc() %lu bytes."), new_size); + proz_die(_("Failed to realloc() %zu bytes."), new_size); return ret; } @@ -507,6 +507,11 @@ void cleanup_socks(void *cdata) cleanup_ftpsocks(connection); break; + case URLHTTPS: + case URLFTPS: + case URLSFTP: + break; + default: proz_die(_("Error: unsupported protocol")); } @@ -564,5 +569,3 @@ void cleanup_httpsocks(connection_t * connection) } - - diff --git a/src/prozilla.h b/src/prozilla.h index 556bc77..db06ddb 100644 --- a/src/prozilla.h +++ b/src/prozilla.h @@ -81,7 +81,10 @@ typedef enum { FILEISDIR, MIRINFOK, MIRPARSEOK, MIRPARSEFAIL, FILEGETOK, /*Related to file joining */ - JOININPROGRESS, JOINDONE, JOINERR + JOININPROGRESS, JOINDONE, JOINERR, + + /* Secure protocols are appended to preserve the historical ABI values. */ + URLHTTPS, URLFTPS, URLSFTP } uerr_t; @@ -91,6 +94,9 @@ typedef enum { #define DEFAULT_FTP_PORT 21 #define DEFAULT_HTTP_PORT 80 +#define DEFAULT_HTTPS_PORT 443 +#define DEFAULT_FTPS_PORT 990 +#define DEFAULT_SFTP_PORT 22 /* This is used when no password is found or specified. */ #define DEFAULT_FTP_USER "anonymous" diff --git a/src/url.c b/src/url.c index c953ee9..9b9a082 100644 --- a/src/url.c +++ b/src/url.c @@ -83,7 +83,10 @@ char *protostrings[] = { /* Similar to former, but for supported protocols: */ proto_t sup_protos[] = { { "http://", URLHTTP, DEFAULT_HTTP_PORT }, - { "ftp://", URLFTP, DEFAULT_FTP_PORT } + { "ftp://", URLFTP, DEFAULT_FTP_PORT }, + { "https://", URLHTTPS, DEFAULT_HTTPS_PORT }, + { "ftps://", URLFTPS, DEFAULT_FTPS_PORT }, + { "sftp://", URLSFTP, DEFAULT_SFTP_PORT } /* { "file://", URLFILE, DEFAULT_FTP_PORT } */ }; @@ -637,7 +640,8 @@ char *str_url(const urlinfo * u, int hide) else passwd = encode_string(u->passwd); } - if (u->proto == URLFTP && *dir == '/') + if ((u->proto == URLFTP || u->proto == URLFTPS || u->proto == URLSFTP) + && *dir == '/') { char *tmp = (char *)kmalloc(strlen(dir) + 3); /*sprintf (tmp, "%%2F%s", dir + 1); */ @@ -797,12 +801,12 @@ uerr_t proz_parse_url(const char *url, urlinfo * u, int strict) /* Some delimiter troubles... */ if (url[i] == '/' && url[i - 1] != ':') ++i; - if (type == URLHTTP) + if (type == URLHTTP || type == URLHTTPS) while (url[i] && url[i] == '/') ++i; u->path = (char *)kmalloc(strlen(url + i) + 8); strcpy(u->path, url + i); - if (type == URLFTP) + if (type == URLFTP || type == URLFTPS || type == URLSFTP) { u->ftp_type = process_ftp_type(u->path); /* #### We don't handle type `d' correctly yet. */ @@ -825,14 +829,15 @@ uerr_t proz_parse_url(const char *url, urlinfo * u, int strict) /* Simplify the directory. */ path_simplify(u->dir); /* Remove the leading `/' in HTTP. */ - if (type == URLHTTP && *u->dir == '/') + if ((type == URLHTTP || type == URLHTTPS) && *u->dir == '/') strcpy(u->dir, u->dir + 1); /* Strip trailing `/'. */ l = strlen(u->dir); if (l > 1 && u->dir[l - 1] == '/') u->dir[l - 1] = '\0'; /* Re-create the path: */ - abs_ftp = (u->proto == URLFTP && *u->dir == '/'); + abs_ftp = ((u->proto == URLFTP || u->proto == URLFTPS + || u->proto == URLSFTP) && *u->dir == '/'); /* sprintf (u->path, "%s%s%s%s", abs_ftp ? "%2F": "/", abs_ftp ? (u->dir + 1) : u->dir, *u->dir ? "/" : "", u->file); */ strcpy(u->path, abs_ftp ? "%2F" : "/"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..3ded262 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(url_parse_test url_parse_test.c) +target_link_libraries(url_parse_test PRIVATE prozilla) +add_test(NAME url_parse COMMAND url_parse_test) diff --git a/tests/url_parse_test.c b/tests/url_parse_test.c new file mode 100644 index 0000000..3a4f357 --- /dev/null +++ b/tests/url_parse_test.c @@ -0,0 +1,28 @@ +#include +#include + +#include + +static int check(const char *text, uerr_t protocol, unsigned short port) +{ + urlinfo url; + uerr_t result = proz_parse_url(text, &url, 0); + + if (result != URLOK || url.proto != protocol || url.port != port) + { + fprintf(stderr, "failed to parse %s (result=%d protocol=%d port=%u)\n", + text, result, url.proto, url.port); + return 1; + } + proz_free_url(&url, 0); + return 0; +} + +int main(void) +{ + return check("http://example.invalid/file", URLHTTP, 80) + || check("https://example.invalid/file", URLHTTPS, 443) + || check("ftp://example.invalid/file", URLFTP, 21) + || check("ftps://example.invalid/file", URLFTPS, 990) + || check("sftp://example.invalid/file", URLSFTP, 22); +}