libalpm
Arch Linux Package Manager Library
|
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: */