diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 5285ac39..ce8c6919 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -83,6 +83,17 @@ int alpm_logaction(char *fmt, ...); typedef void (*alpm_cb_download)(const char *filename, off_t xfered, off_t total); typedef void (*alpm_cb_totaldl)(off_t total); +/** A callback for downloading files + * @param url the URL of the file to be downloaded + * @param localpath the directory to which the file should be downloaded + * @param mtimeold the modification time of the file previously downloaded + * @param mtimenew the modification time of the newly downloaded file. + * This should be set by the callback. + * @return 0 on success, 1 if the modification times are identical, -1 on + * error. + */ +typedef int (*alpm_cb_fetch)(const char *url, const char *localpath, + time_t mtimeold, time_t *mtimenew); /* * Options @@ -94,6 +105,9 @@ void alpm_option_set_logcb(alpm_cb_log cb); alpm_cb_download alpm_option_get_dlcb(); void alpm_option_set_dlcb(alpm_cb_download cb); +alpm_cb_fetch alpm_option_get_fetchcb(); +void alpm_option_set_fetchcb(alpm_cb_fetch cb); + alpm_cb_totaldl alpm_option_get_totaldlcb(); void alpm_option_set_totaldlcb(alpm_cb_totaldl cb); @@ -137,9 +151,6 @@ void alpm_option_add_ignoregrp(const char *grp); void alpm_option_set_ignoregrps(alpm_list_t *ignoregrps); int alpm_option_remove_ignoregrp(const char *grp); -const char *alpm_option_get_xfercommand(); -void alpm_option_set_xfercommand(const char *cmd); - unsigned short alpm_option_get_nopassiveftp(); void alpm_option_set_nopassiveftp(unsigned short nopasv); void alpm_option_set_usedelta(unsigned short usedelta); diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c index c8296f52..6b163ce6 100644 --- a/lib/libalpm/dload.c +++ b/lib/libalpm/dload.c @@ -263,111 +263,21 @@ cleanup: } #endif -static int download_external(const char *url, const char *localpath, - time_t mtimeold, time_t *mtimenew) { - int ret = 0; - int retval; - int usepart = 0; - char *ptr1, *ptr2; - char origCmd[PATH_MAX]; - char parsedCmd[PATH_MAX] = ""; - char cwd[PATH_MAX]; - char *destfile, *tempfile, *filename; - - if(!handle->xfercommand) { - RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1); - } - - filename = get_filename(url); - if(!filename) { - RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1); - } - destfile = get_destfile(localpath, filename); - tempfile = get_tempfile(localpath, filename); - - /* replace all occurrences of %o with fn.part */ - strncpy(origCmd, handle->xfercommand, sizeof(origCmd)); - ptr1 = origCmd; - while((ptr2 = strstr(ptr1, "%o"))) { - usepart = 1; - ptr2[0] = '\0'; - strcat(parsedCmd, ptr1); - strcat(parsedCmd, tempfile); - ptr1 = ptr2 + 2; - } - strcat(parsedCmd, ptr1); - /* replace all occurrences of %u with the download URL */ - strncpy(origCmd, parsedCmd, sizeof(origCmd)); - parsedCmd[0] = '\0'; - ptr1 = origCmd; - while((ptr2 = strstr(ptr1, "%u"))) { - ptr2[0] = '\0'; - strcat(parsedCmd, ptr1); - strcat(parsedCmd, url); - ptr1 = ptr2 + 2; - } - strcat(parsedCmd, ptr1); - /* cwd to the download directory */ - getcwd(cwd, PATH_MAX); - if(chdir(localpath)) { - _alpm_log(PM_LOG_WARNING, _("could not chdir to %s\n"), localpath); - pm_errno = PM_ERR_EXTERNAL_DOWNLOAD; - ret = -1; - goto cleanup; - } - /* execute the parsed command via /bin/sh -c */ - _alpm_log(PM_LOG_DEBUG, "running command: %s\n", parsedCmd); - retval = system(parsedCmd); - - if(retval == -1) { - _alpm_log(PM_LOG_WARNING, _("running XferCommand: fork failed!\n")); - pm_errno = PM_ERR_EXTERNAL_DOWNLOAD; - ret = -1; - } else if(retval != 0) { - /* download failed */ - _alpm_log(PM_LOG_DEBUG, "XferCommand command returned non-zero status " - "code (%d)\n", retval); - ret = -1; - } else { - /* download was successful */ - if(usepart) { - rename(tempfile, destfile); - } - ret = 0; - } - -cleanup: - chdir(cwd); - if(ret == -1) { - /* hack to let an user the time to cancel a download */ - sleep(2); - } - FREE(destfile); - FREE(tempfile); - - return(ret); -} - static int download(const char *url, const char *localpath, time_t mtimeold, time_t *mtimenew) { - int ret; - - /* We have a few things to take into account here. - * 1. If we have both internal/external available, choose based on - * whether xfercommand is populated. - * 2. If we only have external available, we should first check - * if a command was provided before we drop into download_external. - */ - if(handle->xfercommand == NULL) { + if(handle->fetchcb == NULL) { #if defined(INTERNAL_DOWNLOAD) - ret = download_internal(url, localpath, mtimeold, mtimenew); + return(download_internal(url, localpath, mtimeold, mtimenew)); #else RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1); #endif } else { - ret = download_external(url, localpath, mtimeold, mtimenew); + int ret = handle->fetchcb(url, localpath, mtimeold, mtimenew); + if(ret == -1) { + RET_ERR(PM_ERR_EXTERNAL_DOWNLOAD, -1); + } + return(ret); } - return(ret); } /* diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index 34115b38..d1a35ad9 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -79,7 +79,6 @@ void _alpm_handle_free(pmhandle_t *handle) FREELIST(handle->cachedirs); FREE(handle->logfile); FREE(handle->lockfile); - FREE(handle->xfercommand); FREELIST(handle->dbs_sync); FREELIST(handle->noupgrade); FREELIST(handle->noextract); @@ -106,6 +105,15 @@ alpm_cb_download SYMEXPORT alpm_option_get_dlcb() return handle->dlcb; } +alpm_cb_fetch SYMEXPORT alpm_option_get_fetchcb() +{ + if (handle == NULL) { + pm_errno = PM_ERR_HANDLE_NULL; + return NULL; + } + return handle->fetchcb; +} + alpm_cb_totaldl SYMEXPORT alpm_option_get_totaldlcb() { if (handle == NULL) { @@ -205,15 +213,6 @@ alpm_list_t SYMEXPORT *alpm_option_get_ignoregrps() return handle->ignoregrp; } -const char SYMEXPORT *alpm_option_get_xfercommand() -{ - if (handle == NULL) { - pm_errno = PM_ERR_HANDLE_NULL; - return NULL; - } - return handle->xfercommand; -} - unsigned short SYMEXPORT alpm_option_get_nopassiveftp() { if (handle == NULL) { @@ -259,6 +258,15 @@ void SYMEXPORT alpm_option_set_dlcb(alpm_cb_download cb) handle->dlcb = cb; } +void SYMEXPORT alpm_option_set_fetchcb(alpm_cb_fetch cb) +{ + if (handle == NULL) { + pm_errno = PM_ERR_HANDLE_NULL; + return; + } + handle->fetchcb = cb; +} + void SYMEXPORT alpm_option_set_totaldlcb(alpm_cb_totaldl cb) { if (handle == NULL) { @@ -521,12 +529,6 @@ int SYMEXPORT alpm_option_remove_ignoregrp(const char *grp) return(0); } -void SYMEXPORT alpm_option_set_xfercommand(const char *cmd) -{ - if(handle->xfercommand) FREE(handle->xfercommand); - if(cmd) handle->xfercommand = strdup(cmd); -} - void SYMEXPORT alpm_option_set_nopassiveftp(unsigned short nopasv) { handle->nopassiveftp = nopasv; diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h index 2ef94bd2..c7c262cf 100644 --- a/lib/libalpm/handle.h +++ b/lib/libalpm/handle.h @@ -41,6 +41,7 @@ typedef struct _pmhandle_t { alpm_cb_log logcb; /* Log callback function */ alpm_cb_download dlcb; /* Download callback function */ alpm_cb_totaldl totaldlcb; /* Total download callback function */ + alpm_cb_fetch fetchcb; /* Download file callback function */ /* filesystem paths */ char *root; /* Root path, default '/' */ @@ -58,7 +59,6 @@ typedef struct _pmhandle_t { /* options */ unsigned short usesyslog; /* Use syslog instead of logfile? */ /* TODO move to frontend */ unsigned short nopassiveftp; /* Don't use PASV ftp connections */ - char *xfercommand; /* External download command */ unsigned short usedelta; /* Download deltas if possible */ } pmhandle_t; diff --git a/src/pacman/conf.c b/src/pacman/conf.c index e25f8b72..92c6f4eb 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -61,6 +61,7 @@ int config_free(config_t *oldconfig) free(oldconfig->rootdir); free(oldconfig->dbpath); free(oldconfig->logfile); + free(oldconfig->xfercommand); free(oldconfig); oldconfig = NULL; diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 6523d490..2d3de987 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -73,6 +73,7 @@ typedef struct __config_t { unsigned short cleanmethod; /* select -Sc behavior */ alpm_list_t *holdpkg; alpm_list_t *syncfirst; + char *xfercommand; } config_t; /* Operations */ diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index d5e600a5..7cecd905 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -581,6 +581,118 @@ static void setrepeatingoption(const char *ptr, const char *option, pm_printf(PM_LOG_DEBUG, "config: %s: %s\n", option, p); } +static char *get_filename(const char *url) { + char *filename = strrchr(url, '/'); + if(filename != NULL) { + filename++; + } + return(filename); +} + +static char *get_destfile(const char *path, const char *filename) { + char *destfile; + /* len = localpath len + filename len + null */ + int len = strlen(path) + strlen(filename) + 1; + destfile = calloc(len, sizeof(char)); + snprintf(destfile, len, "%s%s", path, filename); + + return(destfile); +} + +static char *get_tempfile(const char *path, const char *filename) { + char *tempfile; + /* len = localpath len + filename len + '.part' len + null */ + int len = strlen(path) + strlen(filename) + 6; + tempfile = calloc(len, sizeof(char)); + snprintf(tempfile, len, "%s%s.part", path, filename); + + return(tempfile); +} + +/** External fetch callback */ +int download_with_xfercommand(const char *url, const char *localpath, + time_t mtimeold, time_t *mtimenew) { + int ret = 0; + int retval; + int usepart = 0; + char *ptr1, *ptr2; + char origCmd[PATH_MAX]; + char parsedCmd[PATH_MAX] = ""; + char cwd[PATH_MAX]; + char *destfile, *tempfile, *filename; + + if(!config->xfercommand) { + return -1; + } + + filename = get_filename(url); + if(!filename) { + return -1; + } + destfile = get_destfile(localpath, filename); + tempfile = get_tempfile(localpath, filename); + + strncpy(origCmd, config->xfercommand, sizeof(origCmd)); + /* replace all occurrences of %o with fn.part */ + ptr1 = origCmd; + while((ptr2 = strstr(ptr1, "%o"))) { + usepart = 1; + ptr2[0] = '\0'; + strcat(parsedCmd, ptr1); + strcat(parsedCmd, tempfile); + ptr1 = ptr2 + 2; + } + strcat(parsedCmd, ptr1); + /* replace all occurrences of %u with the download URL */ + strncpy(origCmd, parsedCmd, sizeof(origCmd)); + parsedCmd[0] = '\0'; + ptr1 = origCmd; + while((ptr2 = strstr(ptr1, "%u"))) { + ptr2[0] = '\0'; + strcat(parsedCmd, ptr1); + strcat(parsedCmd, url); + ptr1 = ptr2 + 2; + } + strcat(parsedCmd, ptr1); + /* cwd to the download directory */ + getcwd(cwd, PATH_MAX); + if(chdir(localpath)) { + pm_printf(PM_LOG_WARNING, "could not chdir to %s\n", localpath); + ret = -1; + goto cleanup; + } + /* execute the parsed command via /bin/sh -c */ + pm_printf(PM_LOG_DEBUG, "running command: %s\n", parsedCmd); + retval = system(parsedCmd); + + if(retval == -1) { + pm_printf(PM_LOG_WARNING, "running XferCommand: fork failed!\n"); + ret = -1; + } else if(retval != 0) { + /* download failed */ + pm_printf(PM_LOG_DEBUG, "XferCommand command returned non-zero status " + "code (%d)\n", retval); + ret = -1; + } else { + /* download was successful */ + if(usepart) { + rename(tempfile, destfile); + } + ret = 0; + } + +cleanup: + chdir(cwd); + if(ret == -1) { + /* hack to let an user the time to cancel a download */ + sleep(2); + } + free(destfile); + free(tempfile); + + return(ret); +} + /* The real parseconfig. Called with a null section argument by the publicly * visible parseconfig so we can recall from within ourself on an include */ static int _parseconfig(const char *file, const char *givensection, @@ -744,7 +856,8 @@ static int _parseconfig(const char *file, const char *givensection, pm_printf(PM_LOG_DEBUG, "config: logfile: %s\n", ptr); } } else if (strcmp(key, "XferCommand") == 0) { - alpm_option_set_xfercommand(ptr); + config->xfercommand = strdup(ptr); + alpm_option_set_fetchcb(download_with_xfercommand); pm_printf(PM_LOG_DEBUG, "config: xfercommand: %s\n", ptr); } else if (strcmp(key, "CleanMethod") == 0) { if (strcmp(ptr, "KeepInstalled") == 0) {