<commit_before>/* librepo - A library providing (libcURL like) API to downloading repository
 * Copyright (C) 2012  <NAME>
 *
 * Licensed under the GNU Lesser General Public License Version 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#define _POSIX_C_SOURCE 200809L
#define _BSD_SOURCE

#include <glib.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <curl/curl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>


#include "handle_internal.h"
#include "handle.h"
#include "result_internal.h"
#include "repomd.h"
#include "rcodes.h"
#include "util.h"
#include "yum.h"
#include "version.h"
#include "yum_internal.h"
#include "url_substitution.h"
#include "downloader.h"
#include "fastestmirror_internal.h"
#include "cleanup.h"

CURL *
lr_get_curl_handle()
{
    CURL *h;

    lr_global_init();

    h = curl_easy_init();
    curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(h, CURLOPT_MAXREDIRS, 6);
    curl_easy_setopt(h, CURLOPT_CONNECTTIMEOUT, LRO_CONNECTTIMEOUT_DEFAULT);
    curl_easy_setopt(h, CURLOPT_LOW_SPEED_TIME, LRO_LOWSPEEDTIME_DEFAULT);
    curl_easy_setopt(h, CURLOPT_LOW_SPEED_LIMIT, LRO_LOWSPEEDLIMIT_DEFAULT);
    curl_easy_setopt(h, CURLOPT_SSL_VERIFYHOST, 2);
    curl_easy_setopt(h, CURLOPT_SSL_VERIFYPEER, 1);
    curl_easy_setopt(h, CURLOPT_SSL_VERIFYSTATUS, 0);
    curl_easy_setopt(h, CURLOPT_FTP_USE_EPSV, LRO_FTPUSEEPSV_DEFAULT);
    curl_easy_setopt(h, CURLOPT_FILETIME, 0);

    return h;
}

void
lr_handle_free_list(char ***list)
{
    if (!list || *list == NULL)
        return;
    for (int x=0; (*list)[x]; x++)
        lr_free((*list)[x]);
    lr_free(*list);
    *list = NULL;
}

LrHandle *
lr_handle_init(void)
{
    LrHandle *handle;
    CURL *curl = lr_get_curl_handle();

    if (!curl)
        return NULL;

    handle = lr_malloc0(sizeof(LrHandle));
    handle->curl_handle = curl;
    handle->fastestmirrormaxage = LRO_FASTESTMIRRORMAXAGE_DEFAULT;
    handle->mirrorlist_fd = -1;
    handle->metalink_fd = -1;
    handle->onetimeflag_apply = FALSE;
    handle->checks |= LR_CHECK_CHECKSUM;
    handle->maxparalleldownloads = LRO_MAXPARALLELDOWNLOADS_DEFAULT;
    handle->maxdownloadspermirror = LRO_MAXDOWNLOADSPERMIRROR_DEFAULT;
    handle->lowspeedtime = LRO_LOWSPEEDTIME_DEFAULT;
    handle->lowspeedlimit = LRO_LOWSPEEDLIMIT_DEFAULT;
    handle->sslverifypeer = 1;
    handle->sslverifyhost = 2;
    handle->sslverifystatus = 0;
    handle->ipresolve = LRO_IPRESOLVE_DEFAULT;
    handle->allowed_mirror_failures = LRO_ALLOWEDMIRRORFAILURES_DEFAULT;
    handle->adaptivemirrorsorting = LRO_ADAPTIVEMIRRORSORTING_DEFAULT;
    handle->gnupghomedir = g_strdup(LRO_GNUPGHOMEDIR_DEFAULT);
    handle->fastestmirrortimeout = LRO_FASTESTMIRRORTIMEOUT_DEFAULT;
    handle->offline = LRO_OFFLINE_DEFAULT;
    handle->httpauthmethods = LRO_HTTPAUTHMETHODS_DEFAULT;
    handle->proxyauthmethods = LRO_PROXYAUTHMETHODS_DEFAULT;
    handle->ftpuseepsv = LRO_FTPUSEEPSV_DEFAULT;
    handle->cachedir = NULL;
    handle->preservetime = 0;

    return handle;
}

void
lr_handle_free(LrHandle *handle)
{
    if (!handle)
        return;
    if (handle->curl_handle)
        curl_easy_cleanup(handle->curl_handle);
    if (handle->mirrorlist_fd != -1)
        close(handle->mirrorlist_fd);
    if (handle->metalink_fd != -1)
        close(handle->metalink_fd);
    lr_handle_free_list(&handle->urls);
    lr_free(handle->fastestmirrorcache);
    lr_free(handle->mirrorlist);
    lr_free(handle->mirrorlisturl);
    lr_free(handle->metalinkurl);
    lr_free(handle->onetimeflag);
    lr_free(handle->used_mirror);
    lr_free(handle->destdir);
    lr_free(handle->useragent);
    lr_free(handle->sslclientcert);
    lr_free(handle->sslclientkey);
    lr_free(handle->sslcacert);
    lr_lrmirrorlist_free(handle->internal_mirrorlist);
    lr_lrmirrorlist_free(handle->urls_mirrors);
    lr_lrmirrorlist_free(handle->mirrorlist_mirrors);
    lr_lrmirrorlist_free(handle->metalink_mirrors);
    lr_lrmirrorlist_free(handle->mirrors);
    lr_metalink_free(handle->metalink);
    lr_handle_free_list(&handle->yumdlist);
    lr_urlvars_free(handle->yumslist);
    lr_handle_free_list(&handle->yumblist);
    lr_urlvars_free(handle->urlvars);
    lr_free(handle->gnupghomedir);
    lr_free(handle->cachedir);
    lr_handle_free_list(&handle->httpheader);
    lr_free(handle);
}

typedef enum {
    LR_REMOTESOURCE_URLS,
    LR_REMOTESOURCE_MIRRORLIST,
    LR_REMOTESOURCE_METALINK,
    LR_REMOTESOURCE_LOCAL_OPT,
    LR_REMOTESOURCE_OFFLINE_OPT,
    LR_REMOTESOURCE_OTHER,
} LrChangedRemoteSource;

static unsigned long curlauth_bitmask(LrAuth mask)
{
    unsigned long out_mask = 0UL;

    if (mask == LR_AUTH_NONE)
        return (unsigned long) CURLAUTH_NONE;

    if (mask & LR_AUTH_BASIC)
        out_mask |= CURLAUTH_BASIC;
    if (mask & LR_AUTH_DIGEST)
        out_mask |= CURLAUTH_DIGEST;
    if (mask & LR_AUTH_NEGOTIATE)
#ifdef CURLAUTH_NEGOTIATE
        out_mask |= CURLAUTH_NEGOTIATE;
#else
        out_mask |= CURLAUTH_GSSNEGOTIATE;
#endif
    if (mask & LR_AUTH_NTLM)
        out_mask |= CURLAUTH_NTLM;
    if (mask & LR_AUTH_DIGEST_IE)
        out_mask |= CURLAUTH_DIGEST_IE;
    if (mask & LR_AUTH_NTLM_WB)
        out_mask |= CURLAUTH_NTLM_WB;
    if (mask & LR_AUTH_ONLY)
        out_mask |= CURLAUTH_ONLY;
    if (mask == LR_AUTH_ANY)
        out_mask |= CURLAUTH_ANY;

    return out_mask;
}

static void
lr_handle_remote_sources_changed(LrHandle *handle, LrChangedRemoteSource type)
{
    // Called when options like:
    // LRO_URLS
    // LRO_MIRRORLIST
    // LRO_MIRRORLISTURL
    // LRO_METALINKURL
    // LRO_LOCAL
    // LRO_OFFLINE
    // are changed

    // Internal mirrorlist is no more valid
    lr_lrmirrorlist_free(handle->internal_mirrorlist);
    handle->internal_mirrorlist = NULL;

    // Mirrors reported via mirrors are no more valid too
    lr_lrmirrorlist_free(handle->mirrors);
    handle->mirrors = NULL;

    if (type == LR_REMOTESOURCE_URLS) {
        lr_lrmirrorlist_free(handle->urls_mirrors);
        handle->urls_mirrors = NULL;
    }

    if (type == LR_REMOTESOURCE_MIRRORLIST) {
        lr_lrmirrorlist_free(handle->mirrorlist_mirrors);
        handle->mirrorlist_mirrors = NULL;
        if (handle->mirrorlist_fd != -1)
            close(handle->mirrorlist_fd);
        handle->mirrorlist_fd = -1;
    }

    if (type == LR_REMOTESOURCE_METALINK) {
        lr_lrmirrorlist_free(handle->metalink_mirrors);
        handle->metalink_mirrors = NULL;
        if (handle->metalink_fd != -1)
            close(handle->metalink_fd);
        handle->metalink_fd = -1;
        lr_metalink_free(handle->metalink);
        handle->metalink = NULL;
    }
}

gboolean
lr_handle_setopt(LrHandle *handle,
                 GError **err,
                 LrHandleOption option,
                 ...)
{
    gboolean ret = TRUE;
    va_list arg;
    CURLcode c_rc = CURLE_OK;
    CURL *c_h;

    assert(!err || *err == NULL);

    // Variables for values from va_arg
    long val_long;
    gint64 val_gint64;

    if (!handle) {
        g_set_error(err, LR_HANDLE_ERROR, LRE_BADFUNCARG,
                    "No handle specified");
        return FALSE;
    }

    c_h = handle->curl_handle;

    va_start(arg, option);

    switch (option) {
    case LRO_UPDATE:
        handle->update = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_MIRRORLIST:
        // DEPRECATED!
        g_warning("WARNING! Deprecated LRO_MIRRORLIST used");
        if (handle->mirrorlist) lr_free(handle->mirrorlist);
        handle->mirrorlist = g_strdup(va_arg(arg, char *));

        if (handle->mirrorlisturl)
            lr_free(handle->mirrorlisturl);
        handle->mirrorlisturl = NULL;
        if (handle->metalinkurl)
            lr_free(handle->metalinkurl);
        handle->metalinkurl = NULL;
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_MIRRORLIST);
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_METALINK);

        if (!handle->mirrorlist)
            break;

        if (strstr(handle->mirrorlist, "metalink"))
            handle->metalinkurl = g_strdup(handle->mirrorlist);
        else
            handle->mirrorlisturl = g_strdup(handle->mirrorlist);
        break;

    case LRO_MIRRORLISTURL:
        if (handle->mirrorlisturl)
            lr_free(handle->mirrorlisturl);
        handle->mirrorlisturl = g_strdup(va_arg(arg, char *));
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_MIRRORLIST);
        break;

    case LRO_METALINKURL:
        if (handle->metalinkurl)
            lr_free(handle->metalinkurl);
        handle->metalinkurl = g_strdup(va_arg(arg, char *));
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_METALINK);
        break;

    case LRO_ONETIMEFLAG:
        if (handle->onetimeflag)
            lr_free(handle->onetimeflag);
        handle->onetimeflag = g_strdup(va_arg(arg, char *));
        break;

    case LRO_LOCAL:
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_LOCAL_OPT);
        handle->local = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_HTTPAUTH:
        if (va_arg(arg, long) ==  0) {
            c_rc = curl_easy_setopt(c_h, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
            handle->httpauthmethods = LR_AUTH_BASIC;
        } else {
            c_rc = curl_easy_setopt(c_h, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
            handle->httpauthmethods = LR_AUTH_ANY;
        }
        break;

    case LRO_USERPWD:
        c_rc = curl_easy_setopt(c_h, CURLOPT_USERPWD, va_arg(arg, char *));
        break;

    case LRO_PROXY:
        c_rc = curl_easy_setopt(c_h, CURLOPT_PROXY, va_arg(arg, char *));
        break;

    case LRO_PROXYPORT: {
        c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYPORT,va_arg(arg, long));
        break;
    }

    case LRO_PROXYTYPE: {
        long curl_proxy = -1;
        switch (va_arg(arg, long)) {
            case LR_PROXY_HTTP:     curl_proxy = CURLPROXY_HTTP;    break;
            case LR_PROXY_HTTP_1_0: curl_proxy = CURLPROXY_HTTP_1_0;break;
            case LR_PROXY_SOCKS4:   curl_proxy = CURLPROXY_SOCKS4;  break;
            case LR_PROXY_SOCKS5:   curl_proxy = CURLPROXY_SOCKS5;  break;
            case LR_PROXY_SOCKS4A:  curl_proxy = CURLPROXY_SOCKS4A; break;
            case LR_PROXY_SOCKS5_HOSTNAME: curl_proxy = CURLPROXY_SOCKS5_HOSTNAME; break;
            default: break;
        }
        if (curl_proxy == -1) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                    "Bad LRO_PROXYTYPE value");
            ret = FALSE;
        } else {
            c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYTYPE, curl_proxy);
        }
        break;
    }

    case LRO_PROXYAUTH:
        if (va_arg(arg, long) == 0) {
            c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
            handle->proxyauthmethods = LR_AUTH_BASIC;
        } else {
            c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
            handle->proxyauthmethods = LR_AUTH_ANY;
        }
        break;

    case LRO_PROXYUSERPWD:
        c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYUSERPWD, va_arg(arg, char *));
        break;

    case LRO_PROGRESSCB:
        handle->user_cb = va_arg(arg, LrProgressCb);
        break;

    case LRO_PROGRESSDATA:
        handle->user_data = va_arg(arg, void *);
        break;

    case LRO_MAXSPEED:
        val_gint64 = va_arg(arg, gint64);
        if (val_gint64 < 0) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Bad value of LRO_MAXSPEED");
            ret = FALSE;
            break;
        } else if (val_gint64 != 0 && val_gint64 < handle->lowspeedlimit) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "LRO_MAXSPEED (%"G_GINT64_FORMAT") is lower than "
                        "LRO_LOWSPEEDLIMIT (%ld)", val_gint64,
                        handle->lowspeedlimit);
            ret = FALSE;
            break;
        }
        handle->maxspeed = val_gint64;
        break;

    case LRO_DESTDIR:
        if (handle->destdir) lr_free(handle->destdir);
        handle->destdir = g_strdup(va_arg(arg, char *));
        break;

    case LRO_REPOTYPE:
        handle->repotype = va_arg(arg, LrRepotype);
        if (handle->repotype != LR_YUMREPO) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Bad value of LRO_REPOTYPE");
            ret = FALSE;
        }
        break;

    case LRO_CONNECTTIMEOUT:
        c_rc = curl_easy_setopt(c_h, CURLOPT_CONNECTTIMEOUT, va_arg(arg, long));
        break;

    case LRO_IGNOREMISSING:
        handle->ignoremissing = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_INTERRUPTIBLE:
        handle->interruptible = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_USERAGENT: {
        char *useragent = va_arg(arg, char *);
        if (handle->useragent) lr_free(handle->useragent);
        handle->useragent = g_strdup(useragent);
        c_rc = curl_easy_setopt(c_h, CURLOPT_USERAGENT, useragent);
        break;
    }

    case LRO_GPGCHECK:
        if (va_arg(arg, long))
            handle->checks |= LR_CHECK_GPG;
        else
            handle->checks &= ~LR_CHECK_GPG;
        break;

    case LRO_CHECKSUM:
        if (va_arg(arg, long))
            handle->checks |= LR_CHECK_CHECKSUM;
        else
            handle->checks &= ~LR_CHECK_CHECKSUM;
        break;

    case LRO_URLS:
    case LRO_YUMDLIST:
    case LRO_YUMBLIST:
    {
        int size = 0;
        char **list = va_arg(arg, char **);
        char ***handle_list = NULL;

        if (option == LRO_URLS) {
            handle_list = &handle->urls;
            lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_URLS);
        } else if (option == LRO_YUMDLIST) {
            handle_list = &handle->yumdlist;
        } else if (option == LRO_YUMBLIST) {
            handle_list = &handle->yumblist;
        }

        lr_handle_free_list(handle_list);
        if (!list)
            break;

        // Get list length
        while (list[size])
            size++;
        size++;

        if (size == 1 && option == LRO_URLS) {
            // Only NULL present in list of URLs, keep handle->urls = NULL
            break;
        }

        // Copy the list
        *handle_list = lr_strv_dup(list);
        break;
    }

    case LRO_HTTPHEADER:
    {
        char **list = va_arg(arg, char **);
        lr_handle_free_list(&handle->httpheader);
        handle->httpheader = lr_strv_dup(list);
        break;
    }

    case LRO_FETCHMIRRORS:
        handle->fetchmirrors = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_MAXMIRRORTRIES:
        val_long = va_arg(arg, long);

        if (handle->maxmirrortries < LRO_MAXMIRRORTRIES_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                    "Value of LRO_MAXMIRRORTRIES is too low (use value > %ld)",
                    LRO_MAXMIRRORTRIES_MIN);
            ret = FALSE;
        } else {
            handle->maxmirrortries = val_long;
        }

        break;

    case LRO_MAXPARALLELDOWNLOADS:
        val_long = va_arg(arg, long);

        if (val_long < LRO_MAXPARALLELDOWNLOADS_MIN ||
            val_long > LRO_MAXPARALLELDOWNLOADS_MAX) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Bad value of LRO_MAXPARALLELDOWNLOADS.");
            ret = FALSE;
        } else {
            handle->maxparalleldownloads = val_long;
        }

        break;

    case LRO_MAXDOWNLOADSPERMIRROR:
        val_long = va_arg(arg, long);

        if (val_long < LRO_MAXDOWNLOADSPERMIRROR_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_MAXDOWNLOADSPERMIRROR is too low.");
            ret = FALSE;
        } else {
            handle->maxdownloadspermirror = val_long;
        }

        break;

    case LRO_YUMSLIST: {
        LrUrlVars *vars = va_arg(arg, LrUrlVars *);
        lr_urlvars_free(handle->yumslist);
        handle->yumslist = vars;
        break;
    }

    case LRO_VARSUB: {
        LrUrlVars *vars = va_arg(arg, LrUrlVars *);
        lr_urlvars_free(handle->urlvars);
        handle->urlvars = vars;

        /* Do not do copy
        for (LrUrlVars *elem = vars; elem; elem = lr_list_next(elem)) {
            LrVar *var = elem->data;
            handle->urlvars = lr_urlvars_set(handle->urlvars, var->var, var->val);
        }
        */

        break;
    }

    case LRO_FASTESTMIRROR:
        handle->fastestmirror = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_FASTESTMIRRORCACHE: {
        char *fastestmirrorcache = va_arg(arg, char *);
        if (handle->fastestmirrorcache) lr_free(handle->fastestmirrorcache);
        handle->fastestmirrorcache = g_strdup(fastestmirrorcache);
        break;
    }

    case LRO_FASTESTMIRRORMAXAGE:
        val_long = va_arg(arg, long);

        if (val_long < LRO_FASTESTMIRRORMAXAGE_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_FASTESTMIRRORMAXAGE is too low.");
            ret = FALSE;
        } else {
            handle->fastestmirrormaxage = val_long;
        }

        break;

    case LRO_FASTESTMIRRORCB:
        handle->fastestmirrorcb = va_arg(arg, LrFastestMirrorCb);
        break;

    case LRO_FASTESTMIRRORDATA:
        handle->fastestmirrordata = va_arg(arg, void *);
        break;

    case LRO_LOWSPEEDTIME:
        val_long = va_arg(arg, long);

        if (val_long < LRO_LOWSPEEDTIME_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_LOWSPEEDTIME is too low.");
            ret = FALSE;
        } else {
            curl_easy_setopt(c_h, CURLOPT_LOW_SPEED_TIME, val_long);
            handle->lowspeedtime = val_long;
        }

        break;

    case LRO_LOWSPEEDLIMIT:
        val_long = va_arg(arg, long);

        if (val_long < LRO_LOWSPEEDLIMIT_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_LOWSPEEDLIMIT is too low.");
            ret = FALSE;
        } else if (handle->maxspeed != 0 && handle->maxspeed < val_long) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_LOWSPEEDLIMIT (%ld) is higher than "
                        "LRO_MAXSPEED (%"G_GINT64_FORMAT")",
                        val_long, handle->maxspeed);
            ret = FALSE;
        } else {
            curl_easy_setopt(c_h, CURLOPT_LOW_SPEED_LIMIT, val_long);
            handle->lowspeedlimit = val_long;
        }

        break;

    case LRO_HMFCB:
        handle->hmfcb = va_arg(arg, LrHandleMirrorFailureCb);
        break;

    case LRO_SSLVERIFYPEER:
        handle->sslverifypeer = va_arg(arg, long) ? 1 : 0;
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSL_VERIFYPEER, handle->sslverifypeer);
        break;

    case LRO_SSLVERIFYSTATUS:
        handle->sslverifystatus = va_arg(arg, long) ? 1 : 0;
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSL_VERIFYSTATUS, handle->sslverifystatus);
        break;

    case LRO_SSLVERIFYHOST:
        handle->sslverifyhost = va_arg(arg, long) ? 2 : 0;
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSL_VERIFYHOST, handle->sslverifyhost);
        break;

    case LRO_SSLCLIENTCERT:
        if (handle->sslclientcert)
            lr_free(handle->sslclientcert);
        handle->sslclientcert = g_strdup(va_arg(arg, char *));
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSLCERT, handle->sslclientcert);
        break;

    case LRO_SSLCLIENTKEY:
        if (handle->sslclientkey)
            lr_free(handle->sslclientkey);
        handle->sslclientkey = g_strdup(va_arg(arg, char *));
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSLKEY, handle->sslclientkey);
        break;

    case LRO_SSLCACERT:
        if (handle->sslcacert)
            lr_free(handle->sslcacert);
        handle->sslcacert = g_strdup(va_arg(arg, char *));
        c_rc = curl_easy_setopt(c_h, CURLOPT_CAINFO, handle->sslcacert);
        break;

    case LRO_IPRESOLVE: {
        long type = -1;
        long lr_type = va_arg(arg, LrIpResolveType);
        switch (lr_type) {
            case LR_IPRESOLVE_WHATEVER: type = CURL_IPRESOLVE_WHATEVER; break;
            case LR_IPRESOLVE_V4:       type = CURL_IPRESOLVE_V4;       break;
            case LR_IPRESOLVE_V6:       type = CURL_IPRESOLVE_V6;       break;<commit_msg>Add support for pkcs11 certificate and key for repository authorization<commit_after>/* librepo - A library providing (libcURL like) API to downloading repository
 * Copyright (C) 2012  <NAME>
 *
 * Licensed under the GNU Lesser General Public License Version 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#define _POSIX_C_SOURCE 200809L
#define _BSD_SOURCE

#include <glib.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>
#include <curl/curl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>


#include "handle_internal.h"
#include "handle.h"
#include "result_internal.h"
#include "repomd.h"
#include "rcodes.h"
#include "util.h"
#include "yum.h"
#include "version.h"
#include "yum_internal.h"
#include "url_substitution.h"
#include "downloader.h"
#include "fastestmirror_internal.h"
#include "cleanup.h"

CURL *
lr_get_curl_handle()
{
    CURL *h;

    lr_global_init();

    h = curl_easy_init();
    curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(h, CURLOPT_MAXREDIRS, 6);
    curl_easy_setopt(h, CURLOPT_CONNECTTIMEOUT, LRO_CONNECTTIMEOUT_DEFAULT);
    curl_easy_setopt(h, CURLOPT_LOW_SPEED_TIME, LRO_LOWSPEEDTIME_DEFAULT);
    curl_easy_setopt(h, CURLOPT_LOW_SPEED_LIMIT, LRO_LOWSPEEDLIMIT_DEFAULT);
    curl_easy_setopt(h, CURLOPT_SSL_VERIFYHOST, 2);
    curl_easy_setopt(h, CURLOPT_SSL_VERIFYPEER, 1);
    curl_easy_setopt(h, CURLOPT_SSL_VERIFYSTATUS, 0);
    curl_easy_setopt(h, CURLOPT_FTP_USE_EPSV, LRO_FTPUSEEPSV_DEFAULT);
    curl_easy_setopt(h, CURLOPT_FILETIME, 0);

    return h;
}

void
lr_handle_free_list(char ***list)
{
    if (!list || *list == NULL)
        return;
    for (int x=0; (*list)[x]; x++)
        lr_free((*list)[x]);
    lr_free(*list);
    *list = NULL;
}

LrHandle *
lr_handle_init(void)
{
    LrHandle *handle;
    CURL *curl = lr_get_curl_handle();

    if (!curl)
        return NULL;

    handle = lr_malloc0(sizeof(LrHandle));
    handle->curl_handle = curl;
    handle->fastestmirrormaxage = LRO_FASTESTMIRRORMAXAGE_DEFAULT;
    handle->mirrorlist_fd = -1;
    handle->metalink_fd = -1;
    handle->onetimeflag_apply = FALSE;
    handle->checks |= LR_CHECK_CHECKSUM;
    handle->maxparalleldownloads = LRO_MAXPARALLELDOWNLOADS_DEFAULT;
    handle->maxdownloadspermirror = LRO_MAXDOWNLOADSPERMIRROR_DEFAULT;
    handle->lowspeedtime = LRO_LOWSPEEDTIME_DEFAULT;
    handle->lowspeedlimit = LRO_LOWSPEEDLIMIT_DEFAULT;
    handle->sslverifypeer = 1;
    handle->sslverifyhost = 2;
    handle->sslverifystatus = 0;
    handle->ipresolve = LRO_IPRESOLVE_DEFAULT;
    handle->allowed_mirror_failures = LRO_ALLOWEDMIRRORFAILURES_DEFAULT;
    handle->adaptivemirrorsorting = LRO_ADAPTIVEMIRRORSORTING_DEFAULT;
    handle->gnupghomedir = g_strdup(LRO_GNUPGHOMEDIR_DEFAULT);
    handle->fastestmirrortimeout = LRO_FASTESTMIRRORTIMEOUT_DEFAULT;
    handle->offline = LRO_OFFLINE_DEFAULT;
    handle->httpauthmethods = LRO_HTTPAUTHMETHODS_DEFAULT;
    handle->proxyauthmethods = LRO_PROXYAUTHMETHODS_DEFAULT;
    handle->ftpuseepsv = LRO_FTPUSEEPSV_DEFAULT;
    handle->cachedir = NULL;
    handle->preservetime = 0;

    return handle;
}

void
lr_handle_free(LrHandle *handle)
{
    if (!handle)
        return;
    if (handle->curl_handle)
        curl_easy_cleanup(handle->curl_handle);
    if (handle->mirrorlist_fd != -1)
        close(handle->mirrorlist_fd);
    if (handle->metalink_fd != -1)
        close(handle->metalink_fd);
    lr_handle_free_list(&handle->urls);
    lr_free(handle->fastestmirrorcache);
    lr_free(handle->mirrorlist);
    lr_free(handle->mirrorlisturl);
    lr_free(handle->metalinkurl);
    lr_free(handle->onetimeflag);
    lr_free(handle->used_mirror);
    lr_free(handle->destdir);
    lr_free(handle->useragent);
    lr_free(handle->sslclientcert);
    lr_free(handle->sslclientkey);
    lr_free(handle->sslcacert);
    lr_lrmirrorlist_free(handle->internal_mirrorlist);
    lr_lrmirrorlist_free(handle->urls_mirrors);
    lr_lrmirrorlist_free(handle->mirrorlist_mirrors);
    lr_lrmirrorlist_free(handle->metalink_mirrors);
    lr_lrmirrorlist_free(handle->mirrors);
    lr_metalink_free(handle->metalink);
    lr_handle_free_list(&handle->yumdlist);
    lr_urlvars_free(handle->yumslist);
    lr_handle_free_list(&handle->yumblist);
    lr_urlvars_free(handle->urlvars);
    lr_free(handle->gnupghomedir);
    lr_free(handle->cachedir);
    lr_handle_free_list(&handle->httpheader);
    lr_free(handle);
}

typedef enum {
    LR_REMOTESOURCE_URLS,
    LR_REMOTESOURCE_MIRRORLIST,
    LR_REMOTESOURCE_METALINK,
    LR_REMOTESOURCE_LOCAL_OPT,
    LR_REMOTESOURCE_OFFLINE_OPT,
    LR_REMOTESOURCE_OTHER,
} LrChangedRemoteSource;

static unsigned long curlauth_bitmask(LrAuth mask)
{
    unsigned long out_mask = 0UL;

    if (mask == LR_AUTH_NONE)
        return (unsigned long) CURLAUTH_NONE;

    if (mask & LR_AUTH_BASIC)
        out_mask |= CURLAUTH_BASIC;
    if (mask & LR_AUTH_DIGEST)
        out_mask |= CURLAUTH_DIGEST;
    if (mask & LR_AUTH_NEGOTIATE)
#ifdef CURLAUTH_NEGOTIATE
        out_mask |= CURLAUTH_NEGOTIATE;
#else
        out_mask |= CURLAUTH_GSSNEGOTIATE;
#endif
    if (mask & LR_AUTH_NTLM)
        out_mask |= CURLAUTH_NTLM;
    if (mask & LR_AUTH_DIGEST_IE)
        out_mask |= CURLAUTH_DIGEST_IE;
    if (mask & LR_AUTH_NTLM_WB)
        out_mask |= CURLAUTH_NTLM_WB;
    if (mask & LR_AUTH_ONLY)
        out_mask |= CURLAUTH_ONLY;
    if (mask == LR_AUTH_ANY)
        out_mask |= CURLAUTH_ANY;

    return out_mask;
}

static void
lr_handle_remote_sources_changed(LrHandle *handle, LrChangedRemoteSource type)
{
    // Called when options like:
    // LRO_URLS
    // LRO_MIRRORLIST
    // LRO_MIRRORLISTURL
    // LRO_METALINKURL
    // LRO_LOCAL
    // LRO_OFFLINE
    // are changed

    // Internal mirrorlist is no more valid
    lr_lrmirrorlist_free(handle->internal_mirrorlist);
    handle->internal_mirrorlist = NULL;

    // Mirrors reported via mirrors are no more valid too
    lr_lrmirrorlist_free(handle->mirrors);
    handle->mirrors = NULL;

    if (type == LR_REMOTESOURCE_URLS) {
        lr_lrmirrorlist_free(handle->urls_mirrors);
        handle->urls_mirrors = NULL;
    }

    if (type == LR_REMOTESOURCE_MIRRORLIST) {
        lr_lrmirrorlist_free(handle->mirrorlist_mirrors);
        handle->mirrorlist_mirrors = NULL;
        if (handle->mirrorlist_fd != -1)
            close(handle->mirrorlist_fd);
        handle->mirrorlist_fd = -1;
    }

    if (type == LR_REMOTESOURCE_METALINK) {
        lr_lrmirrorlist_free(handle->metalink_mirrors);
        handle->metalink_mirrors = NULL;
        if (handle->metalink_fd != -1)
            close(handle->metalink_fd);
        handle->metalink_fd = -1;
        lr_metalink_free(handle->metalink);
        handle->metalink = NULL;
    }
}

gboolean
lr_handle_setopt(LrHandle *handle,
                 GError **err,
                 LrHandleOption option,
                 ...)
{
    gboolean ret = TRUE;
    va_list arg;
    CURLcode c_rc = CURLE_OK;
    CURL *c_h;

    assert(!err || *err == NULL);

    // Variables for values from va_arg
    long val_long;
    gint64 val_gint64;

    if (!handle) {
        g_set_error(err, LR_HANDLE_ERROR, LRE_BADFUNCARG,
                    "No handle specified");
        return FALSE;
    }

    c_h = handle->curl_handle;

    va_start(arg, option);

    switch (option) {
    case LRO_UPDATE:
        handle->update = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_MIRRORLIST:
        // DEPRECATED!
        g_warning("WARNING! Deprecated LRO_MIRRORLIST used");
        if (handle->mirrorlist) lr_free(handle->mirrorlist);
        handle->mirrorlist = g_strdup(va_arg(arg, char *));

        if (handle->mirrorlisturl)
            lr_free(handle->mirrorlisturl);
        handle->mirrorlisturl = NULL;
        if (handle->metalinkurl)
            lr_free(handle->metalinkurl);
        handle->metalinkurl = NULL;
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_MIRRORLIST);
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_METALINK);

        if (!handle->mirrorlist)
            break;

        if (strstr(handle->mirrorlist, "metalink"))
            handle->metalinkurl = g_strdup(handle->mirrorlist);
        else
            handle->mirrorlisturl = g_strdup(handle->mirrorlist);
        break;

    case LRO_MIRRORLISTURL:
        if (handle->mirrorlisturl)
            lr_free(handle->mirrorlisturl);
        handle->mirrorlisturl = g_strdup(va_arg(arg, char *));
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_MIRRORLIST);
        break;

    case LRO_METALINKURL:
        if (handle->metalinkurl)
            lr_free(handle->metalinkurl);
        handle->metalinkurl = g_strdup(va_arg(arg, char *));
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_METALINK);
        break;

    case LRO_ONETIMEFLAG:
        if (handle->onetimeflag)
            lr_free(handle->onetimeflag);
        handle->onetimeflag = g_strdup(va_arg(arg, char *));
        break;

    case LRO_LOCAL:
        lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_LOCAL_OPT);
        handle->local = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_HTTPAUTH:
        if (va_arg(arg, long) ==  0) {
            c_rc = curl_easy_setopt(c_h, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
            handle->httpauthmethods = LR_AUTH_BASIC;
        } else {
            c_rc = curl_easy_setopt(c_h, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
            handle->httpauthmethods = LR_AUTH_ANY;
        }
        break;

    case LRO_USERPWD:
        c_rc = curl_easy_setopt(c_h, CURLOPT_USERPWD, va_arg(arg, char *));
        break;

    case LRO_PROXY:
        c_rc = curl_easy_setopt(c_h, CURLOPT_PROXY, va_arg(arg, char *));
        break;

    case LRO_PROXYPORT: {
        c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYPORT,va_arg(arg, long));
        break;
    }

    case LRO_PROXYTYPE: {
        long curl_proxy = -1;
        switch (va_arg(arg, long)) {
            case LR_PROXY_HTTP:     curl_proxy = CURLPROXY_HTTP;    break;
            case LR_PROXY_HTTP_1_0: curl_proxy = CURLPROXY_HTTP_1_0;break;
            case LR_PROXY_SOCKS4:   curl_proxy = CURLPROXY_SOCKS4;  break;
            case LR_PROXY_SOCKS5:   curl_proxy = CURLPROXY_SOCKS5;  break;
            case LR_PROXY_SOCKS4A:  curl_proxy = CURLPROXY_SOCKS4A; break;
            case LR_PROXY_SOCKS5_HOSTNAME: curl_proxy = CURLPROXY_SOCKS5_HOSTNAME; break;
            default: break;
        }
        if (curl_proxy == -1) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                    "Bad LRO_PROXYTYPE value");
            ret = FALSE;
        } else {
            c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYTYPE, curl_proxy);
        }
        break;
    }

    case LRO_PROXYAUTH:
        if (va_arg(arg, long) == 0) {
            c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
            handle->proxyauthmethods = LR_AUTH_BASIC;
        } else {
            c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
            handle->proxyauthmethods = LR_AUTH_ANY;
        }
        break;

    case LRO_PROXYUSERPWD:
        c_rc = curl_easy_setopt(c_h, CURLOPT_PROXYUSERPWD, va_arg(arg, char *));
        break;

    case LRO_PROGRESSCB:
        handle->user_cb = va_arg(arg, LrProgressCb);
        break;

    case LRO_PROGRESSDATA:
        handle->user_data = va_arg(arg, void *);
        break;

    case LRO_MAXSPEED:
        val_gint64 = va_arg(arg, gint64);
        if (val_gint64 < 0) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Bad value of LRO_MAXSPEED");
            ret = FALSE;
            break;
        } else if (val_gint64 != 0 && val_gint64 < handle->lowspeedlimit) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "LRO_MAXSPEED (%"G_GINT64_FORMAT") is lower than "
                        "LRO_LOWSPEEDLIMIT (%ld)", val_gint64,
                        handle->lowspeedlimit);
            ret = FALSE;
            break;
        }
        handle->maxspeed = val_gint64;
        break;

    case LRO_DESTDIR:
        if (handle->destdir) lr_free(handle->destdir);
        handle->destdir = g_strdup(va_arg(arg, char *));
        break;

    case LRO_REPOTYPE:
        handle->repotype = va_arg(arg, LrRepotype);
        if (handle->repotype != LR_YUMREPO) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Bad value of LRO_REPOTYPE");
            ret = FALSE;
        }
        break;

    case LRO_CONNECTTIMEOUT:
        c_rc = curl_easy_setopt(c_h, CURLOPT_CONNECTTIMEOUT, va_arg(arg, long));
        break;

    case LRO_IGNOREMISSING:
        handle->ignoremissing = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_INTERRUPTIBLE:
        handle->interruptible = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_USERAGENT: {
        char *useragent = va_arg(arg, char *);
        if (handle->useragent) lr_free(handle->useragent);
        handle->useragent = g_strdup(useragent);
        c_rc = curl_easy_setopt(c_h, CURLOPT_USERAGENT, useragent);
        break;
    }

    case LRO_GPGCHECK:
        if (va_arg(arg, long))
            handle->checks |= LR_CHECK_GPG;
        else
            handle->checks &= ~LR_CHECK_GPG;
        break;

    case LRO_CHECKSUM:
        if (va_arg(arg, long))
            handle->checks |= LR_CHECK_CHECKSUM;
        else
            handle->checks &= ~LR_CHECK_CHECKSUM;
        break;

    case LRO_URLS:
    case LRO_YUMDLIST:
    case LRO_YUMBLIST:
    {
        int size = 0;
        char **list = va_arg(arg, char **);
        char ***handle_list = NULL;

        if (option == LRO_URLS) {
            handle_list = &handle->urls;
            lr_handle_remote_sources_changed(handle, LR_REMOTESOURCE_URLS);
        } else if (option == LRO_YUMDLIST) {
            handle_list = &handle->yumdlist;
        } else if (option == LRO_YUMBLIST) {
            handle_list = &handle->yumblist;
        }

        lr_handle_free_list(handle_list);
        if (!list)
            break;

        // Get list length
        while (list[size])
            size++;
        size++;

        if (size == 1 && option == LRO_URLS) {
            // Only NULL present in list of URLs, keep handle->urls = NULL
            break;
        }

        // Copy the list
        *handle_list = lr_strv_dup(list);
        break;
    }

    case LRO_HTTPHEADER:
    {
        char **list = va_arg(arg, char **);
        lr_handle_free_list(&handle->httpheader);
        handle->httpheader = lr_strv_dup(list);
        break;
    }

    case LRO_FETCHMIRRORS:
        handle->fetchmirrors = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_MAXMIRRORTRIES:
        val_long = va_arg(arg, long);

        if (handle->maxmirrortries < LRO_MAXMIRRORTRIES_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                    "Value of LRO_MAXMIRRORTRIES is too low (use value > %ld)",
                    LRO_MAXMIRRORTRIES_MIN);
            ret = FALSE;
        } else {
            handle->maxmirrortries = val_long;
        }

        break;

    case LRO_MAXPARALLELDOWNLOADS:
        val_long = va_arg(arg, long);

        if (val_long < LRO_MAXPARALLELDOWNLOADS_MIN ||
            val_long > LRO_MAXPARALLELDOWNLOADS_MAX) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Bad value of LRO_MAXPARALLELDOWNLOADS.");
            ret = FALSE;
        } else {
            handle->maxparalleldownloads = val_long;
        }

        break;

    case LRO_MAXDOWNLOADSPERMIRROR:
        val_long = va_arg(arg, long);

        if (val_long < LRO_MAXDOWNLOADSPERMIRROR_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_MAXDOWNLOADSPERMIRROR is too low.");
            ret = FALSE;
        } else {
            handle->maxdownloadspermirror = val_long;
        }

        break;

    case LRO_YUMSLIST: {
        LrUrlVars *vars = va_arg(arg, LrUrlVars *);
        lr_urlvars_free(handle->yumslist);
        handle->yumslist = vars;
        break;
    }

    case LRO_VARSUB: {
        LrUrlVars *vars = va_arg(arg, LrUrlVars *);
        lr_urlvars_free(handle->urlvars);
        handle->urlvars = vars;

        /* Do not do copy
        for (LrUrlVars *elem = vars; elem; elem = lr_list_next(elem)) {
            LrVar *var = elem->data;
            handle->urlvars = lr_urlvars_set(handle->urlvars, var->var, var->val);
        }
        */

        break;
    }

    case LRO_FASTESTMIRROR:
        handle->fastestmirror = va_arg(arg, long) ? 1 : 0;
        break;

    case LRO_FASTESTMIRRORCACHE: {
        char *fastestmirrorcache = va_arg(arg, char *);
        if (handle->fastestmirrorcache) lr_free(handle->fastestmirrorcache);
        handle->fastestmirrorcache = g_strdup(fastestmirrorcache);
        break;
    }

    case LRO_FASTESTMIRRORMAXAGE:
        val_long = va_arg(arg, long);

        if (val_long < LRO_FASTESTMIRRORMAXAGE_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_FASTESTMIRRORMAXAGE is too low.");
            ret = FALSE;
        } else {
            handle->fastestmirrormaxage = val_long;
        }

        break;

    case LRO_FASTESTMIRRORCB:
        handle->fastestmirrorcb = va_arg(arg, LrFastestMirrorCb);
        break;

    case LRO_FASTESTMIRRORDATA:
        handle->fastestmirrordata = va_arg(arg, void *);
        break;

    case LRO_LOWSPEEDTIME:
        val_long = va_arg(arg, long);

        if (val_long < LRO_LOWSPEEDTIME_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_LOWSPEEDTIME is too low.");
            ret = FALSE;
        } else {
            curl_easy_setopt(c_h, CURLOPT_LOW_SPEED_TIME, val_long);
            handle->lowspeedtime = val_long;
        }

        break;

    case LRO_LOWSPEEDLIMIT:
        val_long = va_arg(arg, long);

        if (val_long < LRO_LOWSPEEDLIMIT_MIN) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_LOWSPEEDLIMIT is too low.");
            ret = FALSE;
        } else if (handle->maxspeed != 0 && handle->maxspeed < val_long) {
            g_set_error(err, LR_HANDLE_ERROR, LRE_BADOPTARG,
                        "Value of LRO_LOWSPEEDLIMIT (%ld) is higher than "
                        "LRO_MAXSPEED (%"G_GINT64_FORMAT")",
                        val_long, handle->maxspeed);
            ret = FALSE;
        } else {
            curl_easy_setopt(c_h, CURLOPT_LOW_SPEED_LIMIT, val_long);
            handle->lowspeedlimit = val_long;
        }

        break;

    case LRO_HMFCB:
        handle->hmfcb = va_arg(arg, LrHandleMirrorFailureCb);
        break;

    case LRO_SSLVERIFYPEER:
        handle->sslverifypeer = va_arg(arg, long) ? 1 : 0;
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSL_VERIFYPEER, handle->sslverifypeer);
        break;

    case LRO_SSLVERIFYSTATUS:
        handle->sslverifystatus = va_arg(arg, long) ? 1 : 0;
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSL_VERIFYSTATUS, handle->sslverifystatus);
        break;

    case LRO_SSLVERIFYHOST:
        handle->sslverifyhost = va_arg(arg, long) ? 2 : 0;
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSL_VERIFYHOST, handle->sslverifyhost);
        break;

    case LRO_SSLCLIENTCERT:
        if (handle->sslclientcert)
            lr_free(handle->sslclientcert);
        handle->sslclientcert = g_strdup(va_arg(arg, char *));
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSLCERT, handle->sslclientcert);
        if (c_rc == CURLE_OK && handle->sslclientcert && !strncasecmp(handle->sslclientcert, "pkcs11:", 7)) {
            c_rc = curl_easy_setopt(c_h, CURLOPT_SSLCERTTYPE, "ENG");
        }
        break;

    case LRO_SSLCLIENTKEY:
        if (handle->sslclientkey)
            lr_free(handle->sslclientkey);
        handle->sslclientkey = g_strdup(va_arg(arg, char *));
        c_rc = curl_easy_setopt(c_h, CURLOPT_SSLKEY, handle->sslclientkey);
        if (c_rc == CURLE_OK && handle->sslclientkey && !strncasecmp(handle->sslclientkey, "pkcs11:", 7)) {
            c_rc = curl_easy_setopt(c_h, CURLOPT_SSLKEYTYPE, "ENG");
        }
        break;

    case LRO_SSLCACERT:
        if (handle->sslcacert)
            lr_free(handle->sslcacert);
        handle->sslcacert = g_strdup(va_arg(arg, char *));
        c_rc = curl_easy_setopt(c_h, CURLOPT_CAINFO, handle->sslcacert);
        break;

    case LRO_IPRESOLVE: {
        long type = -1;
        long lr_type = va_arg(arg, LrIpResolveType);
        switch (lr_type) {
            case LR_IPRESOLVE_WHATEVER: type = CURL_IPRESOLVE_WHATEVER; break;
            case LR_IPRESOLVE_V4:       type = CURL_IPRESOLVE_V4;       break;
            case LR_IPRESOLVE_V6:       type = CURL_IPRESOLVE_V6;       break;
{'fraction_non_alphanumeric': 0.09962007318835513, 'fraction_numerical': 0.005407547257767522, 'mean_word_length': 2.5248110417351297, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 383, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7413756', 'n_tokens_mistral': 15972, 'n_tokens_neox': 15013, 'n_words': 2704}