Sélectionner une révision Git
auto4_lua_dialog.cpp
-
Thomas Goyne a rédigé
Originally committed to SVN as r5611.
Thomas Goyne a rédigéOriginally committed to SVN as r5611.
curl.c 10,23 Kio
#define _POSIX_C_SOURCE 200809L
#include <lektor/repo.h>
#include <lektor/database.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
static volatile unsigned int curl_init = false;
#define URL_MAX_LEN 1024
#define DEFAULT_URL "https://kurisu.iiens.net"
#define GET_ID_JSON DEFAULT_URL "/api_karas.php?id=%ld"
#define GET_ID_FILE DEFAULT_URL "/download.php?id=%ld"
struct memory {
void *mem;
size_t size;
};
struct file {
const char *path;
int fd;
};
static size_t
write_mem__(char *data, size_t size, size_t nmem, void *user)
{
size_t realsize = size * nmem;
struct memory *mem = (struct memory *) user;
void *ptr = realloc(mem->mem, mem->size + realsize);
if (ptr == NULL) {
fprintf(stderr, " ! write_mem__: not enough memory (realloc returned NULL)\n");
return 0;
}
mem->mem = ptr;
memcpy(((uint8_t *) mem->mem) + mem->size, data, realsize);
mem->size += realsize;
return realsize;
}
static size_t
write_disk__(char *data, size_t size, size_t nmem, void *user)
{
ssize_t realsize = size * nmem;
struct file *file = (struct file *) user;
if (write(file->fd, data, realsize) != realsize) {
fprintf(stderr, " ! write_disk__: failed to write to '%s'\n", file->path);
return 0;
}
return realsize;
}
int
repo_new(struct lkt_repo *const repo_, const char *name_, const char *url_)
{
if (!curl_init) {
curl_global_init(CURL_GLOBAL_ALL);
curl_init = 1;
} else
++curl_init;
const size_t init_size = 30;
uint64_t *calloc1 = calloc(init_size, sizeof(uint64_t));
if (!calloc1) {
fprintf(stderr, " ! repo_new: Out of memory\n");
return ENOMEM;
}
uint64_t *calloc2 = calloc(init_size, sizeof(uint64_t));
if (!calloc2) {
free(calloc1);
fprintf(stderr, " ! repo_new: Out of memory\n");
return ENOMEM;
}
struct lkt_repo repo = {
.name = name_,
.base_url = url_,
.get_id_json = GET_ID_JSON,
.get_id_file = GET_ID_FILE,
.get_all_json = DEFAULT_URL "/api_karas.php",
.kara_dir = "/home/kara/", // TODO
.version = 1,
};
memcpy(repo_, &repo, sizeof(struct lkt_repo));
return 0;
}
void
repo_free(struct lkt_repo *const repo)
{
--curl_init;
free((void *) repo->base_url);
free((void *) repo->kara_dir);
if (!curl_init)
curl_global_cleanup();
}
int
safe_json_get_string(struct json_object *jobj, const char *key, char *content, const size_t len)
{
int ret = 1;
const char *got;
struct json_object *field;
if (!json_object_object_get_ex(jobj, key, &field)) {
fprintf(stderr, " ! safe_json_get_string: No object with key %s in json\n", key);
goto err;
}
got = json_object_get_string(field);
if (!got) {
fprintf(stderr, " ! safe_json_get_string: Got a NULL for key %s, may be an error\n", key);
goto err;
}
strncpy(content, got, len - 1);
content[len - 1] = 0;
ret = 0;
err:
return ret;
}
int
safe_json_get_int32(struct json_object *json, const char *key, int32_t *ret)
{
struct json_object *field;
if (!json_object_object_get_ex(json, key, &field)) {
fprintf(stderr, " . safe_json_get_int32: No object with key '%s' in json, error\n", key);
goto err;
}
errno = 0;
*ret = json_object_get_int(field);
if (errno == EINVAL) {
fprintf(stderr, " . __handle_got_json: Invalid integer for field with key 'song_number'\n");
goto err;
}
if (*ret == INT32_MAX || *ret == INT32_MIN) {
fprintf(stderr, " . __handle_got_json: Out of bound integer for field with key 'song_number'\n");
goto err;
}
return 0;
err:
return 1;
}
int
repo_get_alljson_sync(struct lkt_repo *const repo, struct json_object **json)
{
if (!json) {
fprintf(stderr, " ! repo_get_alljson_sync: Invalid argument\n");
return 1;
}
CURL *curl_handle;
CURLcode res;
int ret = 1;
struct memory file = {
.mem = NULL,
.size = 0.
};
curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, repo->get_all_json);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
res = curl_easy_perform(curl_handle);
if (res != CURLE_OK) {
fprintf(stderr, " ! repo_get_alljson_sync: curl_easy_perform failed: %s\n",
curl_easy_strerror(res));
free(file.mem);
goto err;
}
*json = json_tokener_parse(file.mem);
ret = 0;
err:
curl_easy_cleanup(curl_handle);
return ret;
}
int
repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata *mdt)
{
CURL *curl_handle;
CURLcode res;
struct json_object *jobj, *field;
int ret = 1, err = 0;
if (!mdt)
fprintf(stderr, " ! repo_get_id: invalid argument\n");
char *url = calloc(URL_MAX_LEN, sizeof(char));
if (!url) {
fprintf(stderr, " ! repo_get_id: out of memory\n");
return ENOMEM;
}
struct memory file = {
.mem = NULL,
.size = 0,
};
memset(url, 0, URL_MAX_LEN * sizeof(char));
snprintf(url, URL_MAX_LEN - 1, repo->get_id_json, id);
url[URL_MAX_LEN - 1] = 0;
curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
res = curl_easy_perform(curl_handle);
if (res != CURLE_OK) {
fprintf(stderr, " ! repo_get_id: curl_easy_perform failed: %s\n", curl_easy_strerror(res));
free(url);
free(file.mem);
curl_easy_cleanup(curl_handle);
return 1;
}
curl_easy_cleanup(curl_handle);
/* Read the json. */
jobj = json_tokener_parse(file.mem);
if (json_object_object_get_ex(jobj, "message", &field)) {
fprintf(stderr, " * repo_get_id: Kara with id %lu not found, message is: %s\n",
id, json_object_get_string(field));
goto err;
}
fprintf(stderr, " . repo_get_id: Got kara with id %lu\n", id);
err |= safe_json_get_string(jobj, "song_name", mdt->song_name, LEKTOR_TAG_MAX);
err |= safe_json_get_string(jobj, "source_name", mdt->source_name, LEKTOR_TAG_MAX);
err |= safe_json_get_string(jobj, "category", mdt->category, LEKTOR_TAG_MAX);
err |= safe_json_get_string(jobj, "language", mdt->language, LEKTOR_TAG_MAX);
err |= safe_json_get_string(jobj, "author_name", mdt->author_name, LEKTOR_TAG_MAX);
err |= safe_json_get_string(jobj, "song_type", mdt->song_type, LEKTOR_TAG_MAX);
errno = 0;
if (!json_object_object_get_ex(jobj, "song_number", &field)) {
fprintf(stderr, " * repo_get_id: No object with key 'song_number' in json, error\n");
goto err;
}
mdt->song_number = json_object_get_int(field);
if (errno == EINVAL) {
fprintf(stderr, " * repo_get_id: Invalid integer for field with key 'song_number'\n");
goto err;
}
if (mdt->song_number == INT32_MAX || mdt->song_number == INT32_MIN) {
fprintf(stderr, " * repo_get_id: Out of bound integer for field with key 'song_number'\n");
goto err;
}
ret = 0;
err:
free(url);
json_object_put(jobj); /* Delete object. */
return ret;
}
int
repo_download_id_sync(struct lkt_repo *const repo, sqlite3 *db, const uint64_t id, const char *kara_path,
struct kara_metadata *mdt_ret)
{
if (!kara_path) {
fprintf(stderr, " ! repo_download_id_sync: Can't download kara to a NULL path\n");
return 1;
}
struct kara_metadata mdt;
CURL *curl_handle;
int ret = 1, fd;
char *url = calloc(URL_MAX_LEN, sizeof(char));
char *ct;
if (!url) {
fprintf(stderr, " ! repo_download_id_sync: Out of memory\n");
errno = ENOMEM;
return ENOMEM;
}
if (repo_get_id(repo, id, mdt_ret ? mdt_ret : &mdt)) {
fprintf(stderr, " ! repo_download_id_sync: Failed to get kara metadata from kurisu\n");
goto err_no_curl;
}
errno = 0;
fd = open(kara_path,
O_WRONLY | O_APPEND | O_CREAT | O_EXCL | O_NOFOLLOW,
S_IRUSR | S_IWUSR);
if (fd < 0) {
if (errno == EEXIST)
fprintf(stderr, " ! repo_download_id_sync: file '%s' already exists\n", kara_path);
else
fprintf(stderr, " ! repo_download_id_sync: could not open file '%s'\n", kara_path);
goto err_no_curl;
}
/* Download the kara... (TODO) */
struct file file = {
.path = kara_path,
.fd = fd,
};
memset(url, 0, URL_MAX_LEN * sizeof(char));
snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, id);
url[URL_MAX_LEN - 1] = 0;
curl_handle = curl_easy_init();
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_disk__);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
ret = curl_easy_perform(curl_handle);
if (ret != CURLE_OK) {
fprintf(stderr, " ! repo_download_id_sync: curl_easy_perform failed: %s\n",
curl_easy_strerror(ret));
goto err;
}
if (CURLE_OK == ( ret = curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &ct)))
fprintf(stderr, " . repo_download_id_sync: Content-Type is '%s'\n", ct);
else {
fprintf(stderr, " ! repo_download_id_sync: failed to get Content-Type: %s\n",
curl_easy_strerror(ret));
goto err;
}
if (!db) {
fprintf(stderr, " . repo_download_id_sync: Skip database update here\n");
goto no_db_update;
}
if (! database_update_add(db, kara_path, mdt_ret ? mdt_ret : &mdt, id, true)) {
fprintf(stderr, " ! repo_download_id_sync: Failed to add kara to database\n");
goto err;
}
no_db_update:
ret = 0;
err:
curl_easy_cleanup(curl_handle);
err_no_curl:
free(url);
return ret;
}