libalpm
Arch Linux Package Manager Library
dload.c
Go to the documentation of this file.
00001 /*
00002  *  download.c
00003  *
00004  *  Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
00005  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <errno.h>
00024 #include <string.h>
00025 #include <unistd.h>
00026 #include <sys/time.h>
00027 #include <sys/types.h>
00028 #include <sys/stat.h>
00029 #include <signal.h>
00030 
00031 #ifdef HAVE_LIBCURL
00032 #include <curl/curl.h>
00033 #endif
00034 
00035 /* libalpm */
00036 #include "dload.h"
00037 #include "alpm_list.h"
00038 #include "alpm.h"
00039 #include "log.h"
00040 #include "util.h"
00041 #include "handle.h"
00042 
00043 #ifdef HAVE_LIBCURL
00044 static const char *get_filename(const char *url)
00045 {
00046     char *filename = strrchr(url, '/');
00047     if(filename != NULL) {
00048         filename++;
00049     }
00050     return filename;
00051 }
00052 
00053 static char *get_fullpath(const char *path, const char *filename,
00054         const char *suffix)
00055 {
00056     char *filepath;
00057     /* len = localpath len + filename len + suffix len + null */
00058     size_t len = strlen(path) + strlen(filename) + strlen(suffix) + 1;
00059     MALLOC(filepath, len, return NULL);
00060     snprintf(filepath, len, "%s%s%s", path, filename, suffix);
00061 
00062     return filepath;
00063 }
00064 
00065 static CURL *get_libcurl_handle(alpm_handle_t *handle)
00066 {
00067     if(!handle->curl) {
00068         curl_global_init(CURL_GLOBAL_SSL);
00069         handle->curl = curl_easy_init();
00070     }
00071     return handle->curl;
00072 }
00073 
00074 enum {
00075     ABORT_SIGINT = 1,
00076     ABORT_OVER_MAXFILESIZE
00077 };
00078 
00079 static int dload_interrupted;
00080 static void inthandler(int UNUSED signum)
00081 {
00082     dload_interrupted = ABORT_SIGINT;
00083 }
00084 
00085 static int curl_progress(void *file, double dltotal, double dlnow,
00086         double UNUSED ultotal, double UNUSED ulnow)
00087 {
00088     struct dload_payload *payload = (struct dload_payload *)file;
00089     off_t current_size, total_size;
00090 
00091     /* SIGINT sent, abort by alerting curl */
00092     if(dload_interrupted) {
00093         return 1;
00094     }
00095 
00096     current_size = payload->initial_size + (off_t)dlnow;
00097 
00098     /* is our filesize still under any set limit? */
00099     if(payload->max_size && current_size > payload->max_size) {
00100         dload_interrupted = ABORT_OVER_MAXFILESIZE;
00101         return 1;
00102     }
00103 
00104     /* none of what follows matters if the front end has no callback */
00105     if(payload->handle->dlcb == NULL) {
00106         return 0;
00107     }
00108 
00109     total_size = payload->initial_size + (off_t)dltotal;
00110 
00111     if(DOUBLE_EQ(dltotal, 0.0) || payload->prevprogress == total_size) {
00112         return 0;
00113     }
00114 
00115     /* initialize the progress bar here to avoid displaying it when
00116      * a repo is up to date and nothing gets downloaded */
00117     if(payload->prevprogress == 0) {
00118         payload->handle->dlcb(payload->remote_name, 0, (off_t)dltotal);
00119     }
00120 
00121     payload->handle->dlcb(payload->remote_name, current_size, total_size);
00122 
00123     payload->prevprogress = current_size;
00124 
00125     return 0;
00126 }
00127 
00128 static int curl_gethost(const char *url, char *buffer, size_t buf_len)
00129 {
00130     size_t hostlen;
00131     char *p, *q;
00132 
00133     if(strncmp(url, "file://", 7) == 0) {
00134         p = _("disk");
00135         hostlen = strlen(p);
00136     } else {
00137         p = strstr(url, "//");
00138         if(!p) {
00139             return 1;
00140         }
00141         p += 2; /* jump over the found // */
00142         hostlen = strcspn(p, "/");
00143 
00144         /* there might be a user:pass@ on the URL. hide it. avoid using memrchr()
00145          * for portability concerns. */
00146         q = p + hostlen;
00147         while(--q > p) {
00148             if(*q == '@') {
00149                 break;
00150             }
00151         }
00152         if(*q == '@' && p != q) {
00153             hostlen -= q - p + 1;
00154             p = q + 1;
00155         }
00156     }
00157 
00158     if(hostlen > buf_len - 1) {
00159         /* buffer overflow imminent */
00160         return 1;
00161     }
00162     memcpy(buffer, p, hostlen);
00163     buffer[hostlen] = '\0';
00164 
00165     return 0;
00166 }
00167 
00168 static int utimes_long(const char *path, long seconds)
00169 {
00170     if(seconds != -1) {
00171         struct timeval tv[2];
00172         memset(&tv, 0, sizeof(tv));
00173         tv[0].tv_sec = tv[1].tv_sec = seconds;
00174         return utimes(path, tv);
00175     }
00176     return 0;
00177 }
00178 
00179 /* prefix to avoid possible future clash with getumask(3) */
00180 static mode_t _getumask(void)
00181 {
00182     mode_t mask = umask(0);
00183     umask(mask);
00184     return mask;
00185 }
00186 
00187 static size_t parse_headers(void *ptr, size_t size, size_t nmemb, void *user)
00188 {
00189     size_t realsize = size * nmemb;
00190     const char *fptr, *endptr = NULL;
00191     const char * const cd_header = "Content-Disposition:";
00192     const char * const fn_key = "filename=";
00193     struct dload_payload *payload = (struct dload_payload *)user;
00194 
00195     if(_alpm_raw_ncmp(cd_header, ptr, strlen(cd_header)) == 0) {
00196         if((fptr = strstr(ptr, fn_key))) {
00197             fptr += strlen(fn_key);
00198 
00199             /* find the end of the field, which is either a semi-colon, or the end of
00200              * the data. As per curl_easy_setopt(3), we cannot count on headers being
00201              * null terminated, so we look for the closing \r\n */
00202             endptr = fptr + strcspn(fptr, ";\r\n") - 1;
00203 
00204             /* remove quotes */
00205             if(*fptr == '"' && *endptr == '"') {
00206                 fptr++;
00207                 endptr--;
00208             }
00209 
00210             STRNDUP(payload->content_disp_name, fptr, endptr - fptr + 1,
00211                     RET_ERR(payload->handle, ALPM_ERR_MEMORY, realsize));
00212         }
00213     }
00214 
00215     return realsize;
00216 }
00217 
00218 static void curl_set_handle_opts(struct dload_payload *payload,
00219         CURL *curl, char *error_buffer)
00220 {
00221     alpm_handle_t *handle = payload->handle;
00222     const char *useragent = getenv("HTTP_USER_AGENT");
00223     struct stat st;
00224 
00225     /* the curl_easy handle is initialized with the alpm handle, so we only need
00226      * to reset the handle's parameters for each time it's used. */
00227     curl_easy_reset(curl);
00228     curl_easy_setopt(curl, CURLOPT_URL, payload->fileurl);
00229     curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
00230     curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer);
00231     curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
00232     curl_easy_setopt(curl, CURLOPT_FILETIME, 1L);
00233     curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
00234     curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
00235     curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, curl_progress);
00236     curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, (void *)payload);
00237     curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1024L);
00238     curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 10L);
00239     curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, parse_headers);
00240     curl_easy_setopt(curl, CURLOPT_WRITEHEADER, (void *)payload);
00241     curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
00242 
00243     _alpm_log(handle, ALPM_LOG_DEBUG, "url: %s\n", payload->fileurl);
00244 
00245     if(payload->max_size) {
00246         _alpm_log(handle, ALPM_LOG_DEBUG, "maxsize: %jd\n",
00247                 (intmax_t)payload->max_size);
00248         curl_easy_setopt(curl, CURLOPT_MAXFILESIZE_LARGE,
00249                 (curl_off_t)payload->max_size);
00250     }
00251 
00252     if(useragent != NULL) {
00253         curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent);
00254     }
00255 
00256     if(!payload->allow_resume && !payload->force && payload->destfile_name &&
00257             stat(payload->destfile_name, &st) == 0) {
00258         /* start from scratch, but only download if our local is out of date. */
00259         curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
00260         curl_easy_setopt(curl, CURLOPT_TIMEVALUE, (long)st.st_mtime);
00261         _alpm_log(handle, ALPM_LOG_DEBUG,
00262                 "using time condition: %lu\n", (long)st.st_mtime);
00263     } else if(stat(payload->tempfile_name, &st) == 0 && payload->allow_resume) {
00264         /* a previous partial download exists, resume from end of file. */
00265         payload->tempfile_openmode = "ab";
00266         curl_easy_setopt(curl, CURLOPT_RESUME_FROM_LARGE, (curl_off_t)st.st_size);
00267         _alpm_log(handle, ALPM_LOG_DEBUG,
00268                 "tempfile found, attempting continuation from %jd bytes\n",
00269                 (intmax_t)st.st_size);
00270         payload->initial_size = st.st_size;
00271     }
00272 }
00273 
00274 static void mask_signal(int signal, void (*handler)(int),
00275         struct sigaction *origaction)
00276 {
00277     struct sigaction newaction;
00278 
00279     newaction.sa_handler = handler;
00280     sigemptyset(&newaction.sa_mask);
00281     newaction.sa_flags = 0;
00282 
00283     sigaction(signal, NULL, origaction);
00284     sigaction(signal, &newaction, NULL);
00285 }
00286 
00287 static void unmask_signal(int signal, struct sigaction sa)
00288 {
00289     sigaction(signal, &sa, NULL);
00290 }
00291 
00292 static FILE *create_tempfile(struct dload_payload *payload, const char *localpath)
00293 {
00294     int fd;
00295     FILE *fp;
00296     char *randpath;
00297     size_t len;
00298 
00299     /* create a random filename, which is opened with O_EXCL */
00300     len = strlen(localpath) + 14 + 1;
00301     MALLOC(randpath, len, RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
00302     snprintf(randpath, len, "%salpmtmp.XXXXXX", localpath);
00303     if((fd = mkstemp(randpath)) == -1 ||
00304             fchmod(fd, ~(_getumask()) & 0666) ||
00305             !(fp = fdopen(fd, payload->tempfile_openmode))) {
00306         unlink(randpath);
00307         CLOSE(fd);
00308         _alpm_log(payload->handle, ALPM_LOG_ERROR,
00309                 _("failed to create temporary file for download\n"));
00310         return NULL;
00311     }
00312     /* fp now points to our alpmtmp.XXXXXX */
00313     free(payload->tempfile_name);
00314     payload->tempfile_name = randpath;
00315     free(payload->remote_name);
00316     STRDUP(payload->remote_name, strrchr(randpath, '/') + 1,
00317             RET_ERR(payload->handle, ALPM_ERR_MEMORY, NULL));
00318 
00319     return fp;
00320 }
00321 
00322 /* RFC1123 states applications should support this length */
00323 #define HOSTNAME_SIZE 256
00324 
00325 static int curl_download_internal(struct dload_payload *payload,
00326         const char *localpath, char **final_file)
00327 {
00328     int ret = -1;
00329     FILE *localf = NULL;
00330     char *effective_url;
00331     char hostname[HOSTNAME_SIZE];
00332     char error_buffer[CURL_ERROR_SIZE] = {0};
00333     struct stat st;
00334     long timecond, respcode = 0, remote_time = -1;
00335     double remote_size, bytes_dl;
00336     struct sigaction orig_sig_pipe, orig_sig_int;
00337     /* shortcut to our handle within the payload */
00338     alpm_handle_t *handle = payload->handle;
00339     CURL *curl = get_libcurl_handle(handle);
00340     handle->pm_errno = 0;
00341 
00342     payload->tempfile_openmode = "wb";
00343     if(!payload->remote_name) {
00344         STRDUP(payload->remote_name, get_filename(payload->fileurl),
00345                 RET_ERR(handle, ALPM_ERR_MEMORY, -1));
00346     }
00347     if(curl_gethost(payload->fileurl, hostname, sizeof(hostname)) != 0) {
00348         _alpm_log(handle, ALPM_LOG_ERROR, _("url '%s' is invalid\n"), payload->fileurl);
00349         RET_ERR(handle, ALPM_ERR_SERVER_BAD_URL, -1);
00350     }
00351 
00352     if(strlen(payload->remote_name) > 0 && strcmp(payload->remote_name, ".sig") != 0) {
00353         payload->destfile_name = get_fullpath(localpath, payload->remote_name, "");
00354         payload->tempfile_name = get_fullpath(localpath, payload->remote_name, ".part");
00355         if(!payload->destfile_name || !payload->tempfile_name) {
00356             goto cleanup;
00357         }
00358     } else {
00359         /* URL doesn't contain a filename, so make a tempfile. We can't support
00360          * resuming this kind of download; partial transfers will be destroyed */
00361         payload->unlink_on_fail = 1;
00362 
00363         localf = create_tempfile(payload, localpath);
00364         if(localf == NULL) {
00365             goto cleanup;
00366         }
00367     }
00368 
00369     curl_set_handle_opts(payload, curl, error_buffer);
00370 
00371     if(localf == NULL) {
00372         localf = fopen(payload->tempfile_name, payload->tempfile_openmode);
00373         if(localf == NULL) {
00374             goto cleanup;
00375         }
00376     }
00377 
00378     _alpm_log(handle, ALPM_LOG_DEBUG,
00379             "opened tempfile for download: %s (%s)\n", payload->tempfile_name,
00380             payload->tempfile_openmode);
00381 
00382     curl_easy_setopt(curl, CURLOPT_WRITEDATA, localf);
00383 
00384     /* ignore any SIGPIPE signals- these may occur if our FTP socket dies or
00385      * something along those lines. Store the old signal handler first. */
00386     mask_signal(SIGPIPE, SIG_IGN, &orig_sig_pipe);
00387     mask_signal(SIGINT, &inthandler, &orig_sig_int);
00388 
00389     /* perform transfer */
00390     payload->curlerr = curl_easy_perform(curl);
00391 
00392     /* disconnect relationships from the curl handle for things that might go out
00393      * of scope, but could still be touched on connection teardown.  This really
00394      * only applies to FTP transfers. See FS#26327 for an example. */
00395     curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
00396     curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *)NULL);
00397 
00398     /* was it a success? */
00399     switch(payload->curlerr) {
00400         case CURLE_OK:
00401             /* get http/ftp response code */
00402             curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &respcode);
00403             _alpm_log(handle, ALPM_LOG_DEBUG, "response code: %ld\n", respcode);
00404             if(respcode >= 400) {
00405                 payload->unlink_on_fail = 1;
00406                 /* non-translated message is same as libcurl */
00407                 snprintf(error_buffer, sizeof(error_buffer),
00408                         "The requested URL returned error: %ld", respcode);
00409                 _alpm_log(handle, ALPM_LOG_ERROR,
00410                         _("failed retrieving file '%s' from %s : %s\n"),
00411                         payload->remote_name, hostname, error_buffer);
00412                 goto cleanup;
00413             }
00414             break;
00415         case CURLE_ABORTED_BY_CALLBACK:
00416             /* handle the interrupt accordingly */
00417             if(dload_interrupted == ABORT_OVER_MAXFILESIZE) {
00418                 payload->curlerr = CURLE_FILESIZE_EXCEEDED;
00419                 handle->pm_errno = ALPM_ERR_LIBCURL;
00420                 /* use the 'size exceeded' message from libcurl */
00421                 _alpm_log(handle, ALPM_LOG_ERROR,
00422                         _("failed retrieving file '%s' from %s : %s\n"),
00423                         payload->remote_name, hostname,
00424                         curl_easy_strerror(CURLE_FILESIZE_EXCEEDED));
00425             }
00426             goto cleanup;
00427         default:
00428             /* delete zero length downloads */
00429             if(fstat(fileno(localf), &st) == 0 && st.st_size == 0) {
00430                 payload->unlink_on_fail = 1;
00431             }
00432             if(!payload->errors_ok) {
00433                 handle->pm_errno = ALPM_ERR_LIBCURL;
00434                 _alpm_log(handle, ALPM_LOG_ERROR,
00435                         _("failed retrieving file '%s' from %s : %s\n"),
00436                         payload->remote_name, hostname, error_buffer);
00437             } else {
00438                 _alpm_log(handle, ALPM_LOG_DEBUG,
00439                         "failed retrieving file '%s' from %s : %s\n",
00440                         payload->remote_name, hostname, error_buffer);
00441             }
00442             goto cleanup;
00443     }
00444 
00445     /* retrieve info about the state of the transfer */
00446     curl_easy_getinfo(curl, CURLINFO_FILETIME, &remote_time);
00447     curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &remote_size);
00448     curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &bytes_dl);
00449     curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &timecond);
00450     curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url);
00451 
00452     /* time condition was met and we didn't download anything. we need to
00453      * clean up the 0 byte .part file that's left behind. */
00454     if(timecond == 1 && DOUBLE_EQ(bytes_dl, 0)) {
00455         _alpm_log(handle, ALPM_LOG_DEBUG, "file met time condition\n");
00456         ret = 1;
00457         unlink(payload->tempfile_name);
00458         goto cleanup;
00459     }
00460 
00461     /* remote_size isn't necessarily the full size of the file, just what the
00462      * server reported as remaining to download. compare it to what curl reported
00463      * as actually being transferred during curl_easy_perform() */
00464     if(!DOUBLE_EQ(remote_size, -1) && !DOUBLE_EQ(bytes_dl, -1) &&
00465             !DOUBLE_EQ(bytes_dl, remote_size)) {
00466         handle->pm_errno = ALPM_ERR_RETRIEVE;
00467         _alpm_log(handle, ALPM_LOG_ERROR, _("%s appears to be truncated: %jd/%jd bytes\n"),
00468                 payload->remote_name, (intmax_t)bytes_dl, (intmax_t)remote_size);
00469         goto cleanup;
00470     }
00471 
00472     if(payload->content_disp_name) {
00473         /* content-disposition header has a better name for our file */
00474         free(payload->destfile_name);
00475         payload->destfile_name = get_fullpath(localpath, payload->content_disp_name, "");
00476     } else {
00477         const char *effective_filename = strrchr(effective_url, '/');
00478         if(effective_filename && strlen(effective_filename) > 2) {
00479             effective_filename++;
00480 
00481             /* if destfile was never set, we wrote to a tempfile. even if destfile is
00482              * set, we may have followed some redirects and the effective url may
00483              * have a better suggestion as to what to name our file. in either case,
00484              * refactor destfile to this newly derived name. */
00485             if(!payload->destfile_name || strcmp(effective_filename,
00486                         strrchr(payload->destfile_name, '/') + 1) != 0) {
00487                 free(payload->destfile_name);
00488                 payload->destfile_name = get_fullpath(localpath, effective_filename, "");
00489             }
00490         }
00491     }
00492 
00493     ret = 0;
00494 
00495 cleanup:
00496     if(localf != NULL) {
00497         fclose(localf);
00498         utimes_long(payload->tempfile_name, remote_time);
00499     }
00500 
00501     if(ret == 0) {
00502         const char *realname = payload->tempfile_name;
00503         if(payload->destfile_name) {
00504             realname = payload->destfile_name;
00505             if(rename(payload->tempfile_name, payload->destfile_name)) {
00506                 _alpm_log(handle, ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
00507                         payload->tempfile_name, payload->destfile_name, strerror(errno));
00508                 ret = -1;
00509             }
00510         }
00511         if(ret != -1 && final_file) {
00512             STRDUP(*final_file, strrchr(realname, '/') + 1,
00513                     RET_ERR(handle, ALPM_ERR_MEMORY, -1));
00514         }
00515     }
00516 
00517     if((ret == -1 || dload_interrupted) && payload->unlink_on_fail &&
00518             payload->tempfile_name) {
00519         unlink(payload->tempfile_name);
00520     }
00521 
00522     /* restore the old signal handlers */
00523     unmask_signal(SIGINT, orig_sig_int);
00524     unmask_signal(SIGPIPE, orig_sig_pipe);
00525     /* if we were interrupted, trip the old handler */
00526     if(dload_interrupted) {
00527         raise(SIGINT);
00528     }
00529 
00530     return ret;
00531 }
00532 #endif
00533 
00534 /** Download a file given by a URL to a local directory.
00535  * Does not overwrite an existing file if the download fails.
00536  * @param payload the payload context
00537  * @param localpath the directory to save the file in
00538  * @param final_file the real name of the downloaded file (may be NULL)
00539  * @return 0 on success, -1 on error (pm_errno is set accordingly if errors_ok == 0)
00540  */
00541 int _alpm_download(struct dload_payload *payload, const char *localpath,
00542         char **final_file)
00543 {
00544     alpm_handle_t *handle = payload->handle;
00545 
00546     if(handle->fetchcb == NULL) {
00547 #ifdef HAVE_LIBCURL
00548         return curl_download_internal(payload, localpath, final_file);
00549 #else
00550         RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
00551 #endif
00552     } else {
00553         int ret = handle->fetchcb(payload->fileurl, localpath, payload->force);
00554         if(ret == -1 && !payload->errors_ok) {
00555             RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
00556         }
00557         return ret;
00558     }
00559 }
00560 
00561 /** Fetch a remote pkg. */
00562 char SYMEXPORT *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url)
00563 {
00564     char *filepath;
00565     const char *cachedir;
00566     char *final_file = NULL;
00567     struct dload_payload payload;
00568     int ret;
00569 
00570     CHECK_HANDLE(handle, return NULL);
00571     ASSERT(url, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, NULL));
00572 
00573     /* find a valid cache dir to download to */
00574     cachedir = _alpm_filecache_setup(handle);
00575 
00576     memset(&payload, 0, sizeof(struct dload_payload));
00577     payload.handle = handle;
00578     STRDUP(payload.fileurl, url, RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
00579     payload.allow_resume = 1;
00580 
00581     /* download the file */
00582     ret = _alpm_download(&payload, cachedir, &final_file);
00583     _alpm_dload_payload_reset(&payload);
00584     if(ret == -1) {
00585         _alpm_log(handle, ALPM_LOG_WARNING, _("failed to download %s\n"), url);
00586         free(final_file);
00587         return NULL;
00588     }
00589     _alpm_log(handle, ALPM_LOG_DEBUG, "successfully downloaded %s\n", url);
00590 
00591     /* attempt to download the signature */
00592     if(ret == 0 && (handle->siglevel & ALPM_SIG_PACKAGE)) {
00593         char *sig_final_file = NULL;
00594         size_t len;
00595 
00596         len = strlen(url) + 5;
00597         MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
00598         snprintf(payload.fileurl, len, "%s.sig", url);
00599         payload.handle = handle;
00600         payload.force = 1;
00601         payload.errors_ok = (handle->siglevel & ALPM_SIG_PACKAGE_OPTIONAL);
00602 
00603         /* set hard upper limit of 16KiB */
00604         payload.max_size = 16 * 1024;
00605 
00606         ret = _alpm_download(&payload, cachedir, &sig_final_file);
00607         if(ret == -1 && !payload.errors_ok) {
00608             _alpm_log(handle, ALPM_LOG_WARNING,
00609                     _("failed to download %s\n"), payload.fileurl);
00610             /* Warn now, but don't return NULL. We will fail later during package
00611              * load time. */
00612         } else if(ret == 0) {
00613             _alpm_log(handle, ALPM_LOG_DEBUG,
00614                     "successfully downloaded %s\n", payload.fileurl);
00615         }
00616         FREE(sig_final_file);
00617         _alpm_dload_payload_reset(&payload);
00618     }
00619 
00620     /* we should be able to find the file the second time around */
00621     filepath = _alpm_filecache_find(handle, final_file);
00622     free(final_file);
00623 
00624     return filepath;
00625 }
00626 
00627 void _alpm_dload_payload_reset(struct dload_payload *payload)
00628 {
00629     ASSERT(payload, return);
00630 
00631     FREE(payload->remote_name);
00632     FREE(payload->tempfile_name);
00633     FREE(payload->destfile_name);
00634     FREE(payload->content_disp_name);
00635     FREE(payload->fileurl);
00636 }
00637 
00638 /* vim: set ts=2 sw=2 noet: */