#include <sys/param.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <paths.h>
#include <poll.h>
#include <bsd_compat.h>
#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
#include "private/utils.h"
#include "private/fetch.h"
static struct fetcher fetchers [] = {
{
"tcp",
0,
tcp_open,
NULL,
fh_close,
stdio_fetch,
},
{
"ssh",
0,
ssh_open,
NULL,
fh_close,
stdio_fetch,
},
{
"pkg+https",
0,
curl_open,
NULL,
curl_cleanup,
curl_fetch,
},
{
"pkg+http",
0,
curl_open,
NULL,
curl_cleanup,
curl_fetch,
},
{
"https",
0,
curl_open,
NULL,
curl_cleanup,
curl_fetch,
},
{
"http",
0,
curl_open,
NULL,
curl_cleanup,
curl_fetch,
},
{
"file",
0,
file_open,
fh_close,
NULL,
stdio_fetch,
},
};
int
pkg_fetch_file_tmp(struct pkg_repo *repo, const char *url, char *dest,
time_t t)
{
int fd = -1;
int retcode = EPKG_FATAL;
struct fetch_item fi;
memset(&fi, 0, sizeof(struct fetch_item));
fd = mkstemp(dest);
if (fd == -1) {
pkg_emit_errno("mkstemp", dest);
return(EPKG_FATAL);
}
fi.url = url;
fi.mtime = t;
retcode = pkg_fetch_file_to_fd(repo, fd, &fi, false);
if (fi.mtime != 0) {
struct timespec ts[2] = {
{
.tv_sec = fi.mtime,
.tv_nsec = 0
},
{
.tv_sec = fi.mtime,
.tv_nsec = 0
}
};
futimens(fd, ts);
}
close(fd);
if (retcode != EPKG_OK)
unlink(dest);
return (retcode);
}
int
pkg_fetch_file(struct pkg_repo *repo, const char *url, char *dest, time_t t,
ssize_t offset, int64_t size)
{
int fd = -1;
int retcode = EPKG_FATAL;
struct fetch_item fi;
char *url_to_free = NULL;
fd = open(dest, O_CREAT|O_APPEND|O_WRONLY, 00644);
if (fd == -1) {
pkg_emit_errno("open", dest);
return(EPKG_FATAL);
}
if (repo != NULL) {
xasprintf(&url_to_free, "%s/%s", repo->url, url);
fi.url = url_to_free;
} else {
fi.url = url;
}
fi.offset = offset;
fi.size = size;
fi.mtime = t;
retcode = pkg_fetch_file_to_fd(repo, fd, &fi, false);
free(url_to_free);
if (t != 0) {
struct timespec ts[2] = {
{
.tv_sec = t,
.tv_nsec = 0
},
{
.tv_sec = t,
.tv_nsec = 0
}
};
futimens(fd, ts);
}
close(fd);
if (retcode != EPKG_OK)
unlink(dest);
return (retcode);
}
#define URL_SCHEME_PREFIX "pkg+"
static struct fetcher *
select_fetcher(const char *url)
{
struct fetcher *f;
size_t nsz;
for (size_t i = 0; i < NELEM(fetchers); i++) {
nsz = strlen(fetchers[i].scheme);
if ((strncasecmp(url, fetchers[i].scheme, nsz) == 0) &&
url[nsz] == ':') {
f = &fetchers[i];
f->timeout =
pkg_object_int(pkg_config_get("FETCH_TIMEOUT"));
return (f);
}
}
return (NULL);
}
int
pkg_fetch_file_to_fd(struct pkg_repo *repo, int dest, struct fetch_item *fi,
bool silent)
{
struct pkg_kv *kv;
kvlist_t envtorestore = vec_init();
c_charv_t envtounset = vec_init();
char *tmp;
int retcode = EPKG_OK;
struct pkg_repo *fakerepo = NULL;
size_t nsz;
pkg_dbg(PKG_DBG_FETCH, 1, "Request to fetch %s", fi->url);
if (repo == NULL) {
fakerepo = xcalloc(1, sizeof(struct pkg_repo));
fakerepo->url = xstrdup(fi->url);
fakerepo->mirror_type = NOMIRROR;
repo = fakerepo;
}
if (repo->fetcher == NULL)
repo->fetcher = select_fetcher(fi->url);
if (repo->fetcher == NULL) {
pkg_emit_error("Unknown scheme: %s", fi->url);
return (EPKG_FATAL);
}
nsz = strlen(URL_SCHEME_PREFIX);
if (strncasecmp(URL_SCHEME_PREFIX, fi->url, nsz) == 0) {
if (repo->mirror_type != SRV) {
pkg_emit_error("packagesite URL error for %s -- "
URL_SCHEME_PREFIX
":// implies SRV mirror type", fi->url);
return(EPKG_FATAL);
}
fi->url += nsz;
}
repo->silent = silent;
vec_foreach(repo->env, i) {
if ((tmp = getenv(repo->env.d[i]->key)) != NULL) {
kv = xcalloc(1, sizeof(*kv));
kv->key = xstrdup(repo->env.d[i]->key);
kv->value = xstrdup(tmp);
vec_push(&envtorestore, kv);
} else {
vec_push(&envtounset, repo->env.d[i]->key);
}
setenv(repo->env.d[i]->key, repo->env.d[i]->value, 1);
}
if ((retcode = repo->fetcher->open(repo, fi)) != EPKG_OK)
goto cleanup;
pkg_dbg(PKG_DBG_FETCH, 1, "Fetch: fetcher used: %s", repo->fetcher->scheme);
retcode = repo->fetcher->fetch(repo, dest, fi);
if (retcode == EPKG_OK)
pkg_emit_fetch_finished(fi->url);
cleanup:
vec_foreach(envtorestore, i) {
setenv(envtorestore.d[i]->key, envtorestore.d[i]->value, 1);
vec_remove_and_free(&envtorestore, i, pkg_kv_free);
}
vec_free(&envtorestore);
while (vec_len(&envtounset) > 0)
unsetenv(vec_pop(&envtounset));
vec_free(&envtounset);
if (repo->fetcher != NULL && repo->fetcher->close != NULL)
repo->fetcher->close(repo);
free(fakerepo);
if (retcode == EPKG_OK) {
struct timespec ts[2] = {
{
.tv_sec = fi->mtime,
.tv_nsec = 0
},
{
.tv_sec = fi->mtime,
.tv_nsec = 0
}
};
futimens(dest, ts);
}
return (retcode);
}