summaryrefslogtreecommitdiff
path: root/src/pacman
diff options
context:
space:
mode:
Diffstat (limited to 'src/pacman')
-rw-r--r--src/pacman/conf.c1
-rw-r--r--src/pacman/conf.h1
-rw-r--r--src/pacman/pacman.c115
3 files changed, 116 insertions, 1 deletions
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) {