4 * Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
5 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/socket.h> /* setsockopt, SO_KEEPALIVE */
28 #include <sys/types.h>
32 #ifdef HAVE_NETINET_IN_H
33 #include <netinet/in.h> /* IPPROTO_TCP */
35 #ifdef HAVE_NETINET_TCP_H
36 #include <netinet/tcp.h> /* TCP_KEEPINTVL, TCP_KEEPIDLE */
40 #include <curl/curl.h>
45 #include "alpm_list.h"
52 static const char *get_filename(const char *url
)
54 char *filename
= strrchr(url
, '/');
55 if(filename
!= NULL
) {
61 static char *get_fullpath(const char *path
, const char *filename
,
65 /* len = localpath len + filename len + suffix len + null */
66 size_t len
= strlen(path
) + strlen(filename
) + strlen(suffix
) + 1;
67 MALLOC(filepath
, len
, return NULL
);
68 snprintf(filepath
, len
, "%s%s%s", path
, filename
, suffix
);
73 static CURL
*get_libcurl_handle(alpm_handle_t
*handle
)
76 curl_global_init(CURL_GLOBAL_SSL
);
77 handle
->curl
= curl_easy_init();
84 ABORT_OVER_MAXFILESIZE
87 static int dload_interrupted
;
88 static void inthandler(int UNUSED signum
)
90 dload_interrupted
= ABORT_SIGINT
;
93 static int dload_progress_cb(void *file
, double dltotal
, double dlnow
,
94 double UNUSED ultotal
, double UNUSED ulnow
)
96 struct dload_payload
*payload
= (struct dload_payload
*)file
;
97 off_t current_size
, total_size
;
99 /* SIGINT sent, abort by alerting curl */
100 if(dload_interrupted
) {
104 current_size
= payload
->initial_size
+ (off_t
)dlnow
;
106 /* is our filesize still under any set limit? */
107 if(payload
->max_size
&& current_size
> payload
->max_size
) {
108 dload_interrupted
= ABORT_OVER_MAXFILESIZE
;
112 /* none of what follows matters if the front end has no callback */
113 if(payload
->handle
->dlcb
== NULL
) {
117 total_size
= payload
->initial_size
+ (off_t
)dltotal
;
119 if(DOUBLE_EQ(dltotal
, 0.0) || payload
->prevprogress
== total_size
) {
123 /* initialize the progress bar here to avoid displaying it when
124 * a repo is up to date and nothing gets downloaded */
125 if(payload
->prevprogress
== 0) {
126 payload
->handle
->dlcb(payload
->remote_name
, 0, (off_t
)dltotal
);
129 payload
->handle
->dlcb(payload
->remote_name
, current_size
, total_size
);
131 payload
->prevprogress
= current_size
;
136 static int curl_gethost(const char *url
, char *buffer
, size_t buf_len
)
141 if(strncmp(url
, "file://", 7) == 0) {
145 p
= strstr(url
, "//");
149 p
+= 2; /* jump over the found // */
150 hostlen
= strcspn(p
, "/");
152 /* there might be a user:pass@ on the URL. hide it. avoid using memrchr()
153 * for portability concerns. */
160 if(*q
== '@' && p
!= q
) {
161 hostlen
-= q
- p
+ 1;
166 if(hostlen
> buf_len
- 1) {
167 /* buffer overflow imminent */
170 memcpy(buffer
, p
, hostlen
);
171 buffer
[hostlen
] = '\0';
176 static int utimes_long(const char *path
, long seconds
)
179 struct timeval tv
[2];
180 memset(&tv
, 0, sizeof(tv
));
181 tv
[0].tv_sec
= tv
[1].tv_sec
= seconds
;
182 return utimes(path
, tv
);
187 /* prefix to avoid possible future clash with getumask(3) */
188 static mode_t
_getumask(void)
190 mode_t mask
= umask(0);
195 static size_t dload_parseheader_cb(void *ptr
, size_t size
, size_t nmemb
, void *user
)
197 size_t realsize
= size
* nmemb
;
198 const char *fptr
, *endptr
= NULL
;
199 const char * const cd_header
= "Content-Disposition:";
200 const char * const fn_key
= "filename=";
201 struct dload_payload
*payload
= (struct dload_payload
*)user
;
203 if(_alpm_raw_ncmp(cd_header
, ptr
, strlen(cd_header
)) == 0) {
204 if((fptr
= strstr(ptr
, fn_key
))) {
205 fptr
+= strlen(fn_key
);
207 /* find the end of the field, which is either a semi-colon, or the end of
208 * the data. As per curl_easy_setopt(3), we cannot count on headers being
209 * null terminated, so we look for the closing \r\n */
210 endptr
= fptr
+ strcspn(fptr
, ";\r\n") - 1;
213 if(*fptr
== '"' && *endptr
== '"') {
218 STRNDUP(payload
->content_disp_name
, fptr
, endptr
- fptr
+ 1,
219 RET_ERR(payload
->handle
, ALPM_ERR_MEMORY
, realsize
));
226 static int dload_sockopt_cb(void *userdata
, curl_socket_t curlfd
,
227 curlsocktype purpose
)
229 alpm_handle_t
*handle
= userdata
;
232 /* this whole method is to prevent FTP control connections from going sour
233 * during a long data transfer; crappy firewalls love to drop otherwise idle
234 * connections if there is no traffic. */
235 if(purpose
!= CURLSOCKTYPE_IPCXN
) {
239 /* don't abort operation if any setsockopt fails, just log to debug */
240 if(setsockopt(curlfd
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&optval
,
241 sizeof(optval
)) < 0) {
242 _alpm_log(handle
, ALPM_LOG_DEBUG
,
243 "Failed to set SO_KEEPALIVE on fd %d\n", curlfd
);
248 if(setsockopt(curlfd
, IPPROTO_TCP
, TCP_KEEPIDLE
, (void *)&optval
,
249 sizeof(optval
)) < 0) {
250 _alpm_log(handle
, ALPM_LOG_DEBUG
,
251 "Failed to set TCP_KEEPIDLE on fd %d\n", curlfd
);
256 if(setsockopt(curlfd
, IPPROTO_TCP
, TCP_KEEPINTVL
, (void *)&optval
,
257 sizeof(optval
)) < 0) {
258 _alpm_log(handle
, ALPM_LOG_DEBUG
,
259 "Failed to set TCP_KEEPINTVL on fd %d\n", curlfd
);
267 static void curl_set_handle_opts(struct dload_payload
*payload
,
268 CURL
*curl
, char *error_buffer
)
270 alpm_handle_t
*handle
= payload
->handle
;
271 const char *useragent
= getenv("HTTP_USER_AGENT");
274 /* the curl_easy handle is initialized with the alpm handle, so we only need
275 * to reset the handle's parameters for each time it's used. */
276 curl_easy_reset(curl
);
277 curl_easy_setopt(curl
, CURLOPT_URL
, payload
->fileurl
);
278 curl_easy_setopt(curl
, CURLOPT_FAILONERROR
, 1L);
279 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, error_buffer
);
280 curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, 10L);
281 curl_easy_setopt(curl
, CURLOPT_FILETIME
, 1L);
282 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 0L);
283 curl_easy_setopt(curl
, CURLOPT_FOLLOWLOCATION
, 1L);
284 curl_easy_setopt(curl
, CURLOPT_PROGRESSFUNCTION
, dload_progress_cb
);
285 curl_easy_setopt(curl
, CURLOPT_PROGRESSDATA
, (void *)payload
);
286 curl_easy_setopt(curl
, CURLOPT_LOW_SPEED_LIMIT
, 1024L);
287 curl_easy_setopt(curl
, CURLOPT_LOW_SPEED_TIME
, 10L);
288 curl_easy_setopt(curl
, CURLOPT_HEADERFUNCTION
, dload_parseheader_cb
);
289 curl_easy_setopt(curl
, CURLOPT_WRITEHEADER
, (void *)payload
);
290 curl_easy_setopt(curl
, CURLOPT_NETRC
, CURL_NETRC_OPTIONAL
);
291 curl_easy_setopt(curl
, CURLOPT_SOCKOPTFUNCTION
, dload_sockopt_cb
);
292 curl_easy_setopt(curl
, CURLOPT_SOCKOPTDATA
, (void *)handle
);
294 _alpm_log(handle
, ALPM_LOG_DEBUG
, "url: %s\n", payload
->fileurl
);
296 if(payload
->max_size
) {
297 _alpm_log(handle
, ALPM_LOG_DEBUG
, "maxsize: %jd\n",
298 (intmax_t)payload
->max_size
);
299 curl_easy_setopt(curl
, CURLOPT_MAXFILESIZE_LARGE
,
300 (curl_off_t
)payload
->max_size
);
303 if(useragent
!= NULL
) {
304 curl_easy_setopt(curl
, CURLOPT_USERAGENT
, useragent
);
307 if(!payload
->allow_resume
&& !payload
->force
&& payload
->destfile_name
&&
308 stat(payload
->destfile_name
, &st
) == 0) {
309 /* start from scratch, but only download if our local is out of date. */
310 curl_easy_setopt(curl
, CURLOPT_TIMECONDITION
, CURL_TIMECOND_IFMODSINCE
);
311 curl_easy_setopt(curl
, CURLOPT_TIMEVALUE
, (long)st
.st_mtime
);
312 _alpm_log(handle
, ALPM_LOG_DEBUG
,
313 "using time condition: %lu\n", (long)st
.st_mtime
);
314 } else if(stat(payload
->tempfile_name
, &st
) == 0 && payload
->allow_resume
) {
315 /* a previous partial download exists, resume from end of file. */
316 payload
->tempfile_openmode
= "ab";
317 curl_easy_setopt(curl
, CURLOPT_RESUME_FROM_LARGE
, (curl_off_t
)st
.st_size
);
318 _alpm_log(handle
, ALPM_LOG_DEBUG
,
319 "tempfile found, attempting continuation from %jd bytes\n",
320 (intmax_t)st
.st_size
);
321 payload
->initial_size
= st
.st_size
;
325 static void mask_signal(int signal
, void (*handler
)(int),
326 struct sigaction
*origaction
)
328 struct sigaction newaction
;
330 newaction
.sa_handler
= handler
;
331 sigemptyset(&newaction
.sa_mask
);
332 newaction
.sa_flags
= 0;
334 sigaction(signal
, NULL
, origaction
);
335 sigaction(signal
, &newaction
, NULL
);
338 static void unmask_signal(int signal
, struct sigaction sa
)
340 sigaction(signal
, &sa
, NULL
);
343 static FILE *create_tempfile(struct dload_payload
*payload
, const char *localpath
)
350 /* create a random filename, which is opened with O_EXCL */
351 len
= strlen(localpath
) + 14 + 1;
352 MALLOC(randpath
, len
, RET_ERR(payload
->handle
, ALPM_ERR_MEMORY
, NULL
));
353 snprintf(randpath
, len
, "%salpmtmp.XXXXXX", localpath
);
354 if((fd
= mkstemp(randpath
)) == -1 ||
355 fchmod(fd
, ~(_getumask()) & 0666) ||
356 !(fp
= fdopen(fd
, payload
->tempfile_openmode
))) {
359 _alpm_log(payload
->handle
, ALPM_LOG_ERROR
,
360 _("failed to create temporary file for download\n"));
363 /* fp now points to our alpmtmp.XXXXXX */
364 free(payload
->tempfile_name
);
365 payload
->tempfile_name
= randpath
;
366 free(payload
->remote_name
);
367 STRDUP(payload
->remote_name
, strrchr(randpath
, '/') + 1,
368 RET_ERR(payload
->handle
, ALPM_ERR_MEMORY
, NULL
));
373 /* RFC1123 states applications should support this length */
374 #define HOSTNAME_SIZE 256
376 static int curl_download_internal(struct dload_payload
*payload
,
377 const char *localpath
, char **final_file
)
382 char hostname
[HOSTNAME_SIZE
];
383 char error_buffer
[CURL_ERROR_SIZE
] = {0};
385 long timecond
, respcode
= 0, remote_time
= -1;
386 double remote_size
, bytes_dl
;
387 struct sigaction orig_sig_pipe
, orig_sig_int
;
388 /* shortcut to our handle within the payload */
389 alpm_handle_t
*handle
= payload
->handle
;
390 CURL
*curl
= get_libcurl_handle(handle
);
391 handle
->pm_errno
= 0;
393 /* make sure these are NULL */
394 FREE(payload
->tempfile_name
);
395 FREE(payload
->destfile_name
);
396 FREE(payload
->content_disp_name
);
398 payload
->tempfile_openmode
= "wb";
399 if(!payload
->remote_name
) {
400 STRDUP(payload
->remote_name
, get_filename(payload
->fileurl
),
401 RET_ERR(handle
, ALPM_ERR_MEMORY
, -1));
403 if(curl_gethost(payload
->fileurl
, hostname
, sizeof(hostname
)) != 0) {
404 _alpm_log(handle
, ALPM_LOG_ERROR
, _("url '%s' is invalid\n"), payload
->fileurl
);
405 RET_ERR(handle
, ALPM_ERR_SERVER_BAD_URL
, -1);
408 if(strlen(payload
->remote_name
) > 0 && strcmp(payload
->remote_name
, ".sig") != 0) {
409 payload
->destfile_name
= get_fullpath(localpath
, payload
->remote_name
, "");
410 payload
->tempfile_name
= get_fullpath(localpath
, payload
->remote_name
, ".part");
411 if(!payload
->destfile_name
|| !payload
->tempfile_name
) {
415 /* URL doesn't contain a filename, so make a tempfile. We can't support
416 * resuming this kind of download; partial transfers will be destroyed */
417 payload
->unlink_on_fail
= 1;
419 localf
= create_tempfile(payload
, localpath
);
425 curl_set_handle_opts(payload
, curl
, error_buffer
);
428 localf
= fopen(payload
->tempfile_name
, payload
->tempfile_openmode
);
430 handle
->pm_errno
= ALPM_ERR_RETRIEVE
;
431 _alpm_log(handle
, ALPM_LOG_ERROR
,
432 _("could not open file %s: %s\n"),
433 payload
->tempfile_name
, strerror(errno
));
438 _alpm_log(handle
, ALPM_LOG_DEBUG
,
439 "opened tempfile for download: %s (%s)\n", payload
->tempfile_name
,
440 payload
->tempfile_openmode
);
442 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, localf
);
444 /* Ignore any SIGPIPE signals. With libcurl, these shouldn't be happening,
445 * but better safe than sorry. Store the old signal handler first. */
446 mask_signal(SIGPIPE
, SIG_IGN
, &orig_sig_pipe
);
447 mask_signal(SIGINT
, &inthandler
, &orig_sig_int
);
449 /* perform transfer */
450 payload
->curlerr
= curl_easy_perform(curl
);
451 _alpm_log(handle
, ALPM_LOG_DEBUG
, "curl returned error %d from transfer\n",
454 /* disconnect relationships from the curl handle for things that might go out
455 * of scope, but could still be touched on connection teardown. This really
456 * only applies to FTP transfers. See FS#26327 for an example. */
457 curl_easy_setopt(curl
, CURLOPT_NOPROGRESS
, 1L);
458 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, (char *)NULL
);
460 /* was it a success? */
461 switch(payload
->curlerr
) {
463 /* get http/ftp response code */
464 curl_easy_getinfo(curl
, CURLINFO_RESPONSE_CODE
, &respcode
);
465 _alpm_log(handle
, ALPM_LOG_DEBUG
, "response code: %ld\n", respcode
);
466 if(respcode
>= 400) {
467 payload
->unlink_on_fail
= 1;
468 /* non-translated message is same as libcurl */
469 snprintf(error_buffer
, sizeof(error_buffer
),
470 "The requested URL returned error: %ld", respcode
);
471 _alpm_log(handle
, ALPM_LOG_ERROR
,
472 _("failed retrieving file '%s' from %s : %s\n"),
473 payload
->remote_name
, hostname
, error_buffer
);
477 case CURLE_ABORTED_BY_CALLBACK
:
478 /* handle the interrupt accordingly */
479 if(dload_interrupted
== ABORT_OVER_MAXFILESIZE
) {
480 payload
->curlerr
= CURLE_FILESIZE_EXCEEDED
;
481 handle
->pm_errno
= ALPM_ERR_LIBCURL
;
482 /* use the 'size exceeded' message from libcurl */
483 _alpm_log(handle
, ALPM_LOG_ERROR
,
484 _("failed retrieving file '%s' from %s : %s\n"),
485 payload
->remote_name
, hostname
,
486 curl_easy_strerror(CURLE_FILESIZE_EXCEEDED
));
490 /* delete zero length downloads */
491 if(fstat(fileno(localf
), &st
) == 0 && st
.st_size
== 0) {
492 payload
->unlink_on_fail
= 1;
494 if(!payload
->errors_ok
) {
495 handle
->pm_errno
= ALPM_ERR_LIBCURL
;
496 _alpm_log(handle
, ALPM_LOG_ERROR
,
497 _("failed retrieving file '%s' from %s : %s\n"),
498 payload
->remote_name
, hostname
, error_buffer
);
500 _alpm_log(handle
, ALPM_LOG_DEBUG
,
501 "failed retrieving file '%s' from %s : %s\n",
502 payload
->remote_name
, hostname
, error_buffer
);
507 /* retrieve info about the state of the transfer */
508 curl_easy_getinfo(curl
, CURLINFO_FILETIME
, &remote_time
);
509 curl_easy_getinfo(curl
, CURLINFO_CONTENT_LENGTH_DOWNLOAD
, &remote_size
);
510 curl_easy_getinfo(curl
, CURLINFO_SIZE_DOWNLOAD
, &bytes_dl
);
511 curl_easy_getinfo(curl
, CURLINFO_CONDITION_UNMET
, &timecond
);
512 curl_easy_getinfo(curl
, CURLINFO_EFFECTIVE_URL
, &effective_url
);
514 /* time condition was met and we didn't download anything. we need to
515 * clean up the 0 byte .part file that's left behind. */
516 if(timecond
== 1 && DOUBLE_EQ(bytes_dl
, 0)) {
517 _alpm_log(handle
, ALPM_LOG_DEBUG
, "file met time condition\n");
519 unlink(payload
->tempfile_name
);
523 /* remote_size isn't necessarily the full size of the file, just what the
524 * server reported as remaining to download. compare it to what curl reported
525 * as actually being transferred during curl_easy_perform() */
526 if(!DOUBLE_EQ(remote_size
, -1) && !DOUBLE_EQ(bytes_dl
, -1) &&
527 !DOUBLE_EQ(bytes_dl
, remote_size
)) {
528 handle
->pm_errno
= ALPM_ERR_RETRIEVE
;
529 _alpm_log(handle
, ALPM_LOG_ERROR
, _("%s appears to be truncated: %jd/%jd bytes\n"),
530 payload
->remote_name
, (intmax_t)bytes_dl
, (intmax_t)remote_size
);
534 if(payload
->content_disp_name
) {
535 /* content-disposition header has a better name for our file */
536 free(payload
->destfile_name
);
537 payload
->destfile_name
= get_fullpath(localpath
, payload
->content_disp_name
, "");
539 const char *effective_filename
= strrchr(effective_url
, '/');
540 if(effective_filename
&& strlen(effective_filename
) > 2) {
541 effective_filename
++;
543 /* if destfile was never set, we wrote to a tempfile. even if destfile is
544 * set, we may have followed some redirects and the effective url may
545 * have a better suggestion as to what to name our file. in either case,
546 * refactor destfile to this newly derived name. */
547 if(!payload
->destfile_name
|| strcmp(effective_filename
,
548 strrchr(payload
->destfile_name
, '/') + 1) != 0) {
549 free(payload
->destfile_name
);
550 payload
->destfile_name
= get_fullpath(localpath
, effective_filename
, "");
560 utimes_long(payload
->tempfile_name
, remote_time
);
564 const char *realname
= payload
->tempfile_name
;
565 if(payload
->destfile_name
) {
566 realname
= payload
->destfile_name
;
567 if(rename(payload
->tempfile_name
, payload
->destfile_name
)) {
568 _alpm_log(handle
, ALPM_LOG_ERROR
, _("could not rename %s to %s (%s)\n"),
569 payload
->tempfile_name
, payload
->destfile_name
, strerror(errno
));
573 if(ret
!= -1 && final_file
) {
574 STRDUP(*final_file
, strrchr(realname
, '/') + 1,
575 RET_ERR(handle
, ALPM_ERR_MEMORY
, -1));
579 if((ret
== -1 || dload_interrupted
) && payload
->unlink_on_fail
&&
580 payload
->tempfile_name
) {
581 unlink(payload
->tempfile_name
);
584 /* restore the old signal handlers */
585 unmask_signal(SIGINT
, orig_sig_int
);
586 unmask_signal(SIGPIPE
, orig_sig_pipe
);
587 /* if we were interrupted, trip the old handler */
588 if(dload_interrupted
) {
596 /** Download a file given by a URL to a local directory.
597 * Does not overwrite an existing file if the download fails.
598 * @param payload the payload context
599 * @param localpath the directory to save the file in
600 * @param final_file the real name of the downloaded file (may be NULL)
601 * @return 0 on success, -1 on error (pm_errno is set accordingly if errors_ok == 0)
603 int _alpm_download(struct dload_payload
*payload
, const char *localpath
,
606 alpm_handle_t
*handle
= payload
->handle
;
608 if(handle
->fetchcb
== NULL
) {
610 return curl_download_internal(payload
, localpath
, final_file
);
612 RET_ERR(handle
, ALPM_ERR_EXTERNAL_DOWNLOAD
, -1);
615 int ret
= handle
->fetchcb(payload
->fileurl
, localpath
, payload
->force
);
616 if(ret
== -1 && !payload
->errors_ok
) {
617 RET_ERR(handle
, ALPM_ERR_EXTERNAL_DOWNLOAD
, -1);
623 static char *filecache_find_url(alpm_handle_t
*handle
, const char *url
)
625 const char *basename
= strrchr(url
, '/');
627 if(basename
== NULL
) {
632 if(basename
== '\0') {
636 return _alpm_filecache_find(handle
, basename
);
639 /** Fetch a remote pkg. */
640 char SYMEXPORT
*alpm_fetch_pkgurl(alpm_handle_t
*handle
, const char *url
)
643 const char *cachedir
;
644 char *final_file
= NULL
;
645 struct dload_payload payload
;
648 CHECK_HANDLE(handle
, return NULL
);
649 ASSERT(url
, RET_ERR(handle
, ALPM_ERR_WRONG_ARGS
, NULL
));
651 /* find a valid cache dir to download to */
652 cachedir
= _alpm_filecache_setup(handle
);
654 memset(&payload
, 0, sizeof(struct dload_payload
));
656 /* attempt to find the file in our pkgcache */
657 filepath
= filecache_find_url(handle
, url
);
658 if(filepath
== NULL
) {
659 STRDUP(payload
.fileurl
, url
, RET_ERR(handle
, ALPM_ERR_MEMORY
, NULL
));
660 payload
.allow_resume
= 1;
661 payload
.handle
= handle
;
663 /* download the file */
664 ret
= _alpm_download(&payload
, cachedir
, &final_file
);
665 _alpm_dload_payload_reset(&payload
);
667 _alpm_log(handle
, ALPM_LOG_WARNING
, _("failed to download %s\n"), url
);
671 _alpm_log(handle
, ALPM_LOG_DEBUG
, "successfully downloaded %s\n", url
);
674 /* attempt to download the signature */
675 if(ret
== 0 && (handle
->siglevel
& ALPM_SIG_PACKAGE
)) {
676 char *sig_filepath
, *sig_final_file
= NULL
;
679 len
= strlen(url
) + 5;
680 MALLOC(payload
.fileurl
, len
, RET_ERR(handle
, ALPM_ERR_MEMORY
, NULL
));
681 snprintf(payload
.fileurl
, len
, "%s.sig", url
);
683 sig_filepath
= filecache_find_url(handle
, payload
.fileurl
);
684 if(sig_filepath
== NULL
) {
685 payload
.handle
= handle
;
687 payload
.errors_ok
= (handle
->siglevel
& ALPM_SIG_PACKAGE_OPTIONAL
);
689 /* set hard upper limit of 16KiB */
690 payload
.max_size
= 16 * 1024;
692 ret
= _alpm_download(&payload
, cachedir
, &sig_final_file
);
693 if(ret
== -1 && !payload
.errors_ok
) {
694 _alpm_log(handle
, ALPM_LOG_WARNING
,
695 _("failed to download %s\n"), payload
.fileurl
);
696 /* Warn now, but don't return NULL. We will fail later during package
698 } else if(ret
== 0) {
699 _alpm_log(handle
, ALPM_LOG_DEBUG
,
700 "successfully downloaded %s\n", payload
.fileurl
);
702 FREE(sig_final_file
);
705 _alpm_dload_payload_reset(&payload
);
708 /* we should be able to find the file the second time around */
709 if(filepath
== NULL
) {
710 filepath
= _alpm_filecache_find(handle
, final_file
);
717 void _alpm_dload_payload_reset(struct dload_payload
*payload
)
719 ASSERT(payload
, return);
721 FREE(payload
->remote_name
);
722 FREE(payload
->tempfile_name
);
723 FREE(payload
->destfile_name
);
724 FREE(payload
->content_disp_name
);
725 FREE(payload
->fileurl
);
728 /* vim: set ts=2 sw=2 noet: */