Compare commits
3 Commits
9a00b28279
...
09c8a57a78
Author | SHA1 | Date | |
---|---|---|---|
09c8a57a78 | |||
605f47f761 | |||
6cf62ab039 |
@ -34,7 +34,6 @@ make
|
||||
- Make deleting from mylist possible, with
|
||||
- Name regexes,
|
||||
- If file is not found at a scan
|
||||
- Use api\_cmd style in api\_encrypt\_init
|
||||
- Buffer up mylistadd api cmds when waiting for ratelimit
|
||||
- Handle C-c gracefully at any time
|
||||
- Write -h page, and maybe a man page too
|
||||
|
422
src/api.c
422
src/api.c
@ -1,5 +1,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <printf.h>
|
||||
#include <time.h>
|
||||
@ -15,6 +16,7 @@
|
||||
#include "config.h"
|
||||
#include "ed2k.h"
|
||||
#include "util.h"
|
||||
#include "globals.h"
|
||||
|
||||
/* Needed, bcuz of custom %B format */
|
||||
#pragma GCC diagnostic push
|
||||
@ -30,19 +32,13 @@
|
||||
#error "No monotonic clock"
|
||||
#endif
|
||||
|
||||
#define MS_TO_TIMESPEC(ts, ms) { \
|
||||
ts->tv_sec = ms / 1000; \
|
||||
ts->tv_nsec = (ms % 1000) * 1000000; \
|
||||
}
|
||||
|
||||
#define MS_TO_TIMESPEC_L(ts, ms) { \
|
||||
ts.tv_sec = ms / 1000; \
|
||||
ts.tv_nsec = (ms % 1000) * 1000000; \
|
||||
}
|
||||
|
||||
static enum error api_cmd_base(char buffer[API_BUFSIZE],
|
||||
struct api_result *res, const char *fmt, ...)
|
||||
__attribute__((format (printf, 3, 4)));
|
||||
static enum error api_cmd_logout(struct api_result *res);
|
||||
static enum error api_cmd_auth(const char *uname, const char *pass,
|
||||
struct api_result *res);
|
||||
static enum error api_cmd_encrypt(const char *uname, struct api_result *res);
|
||||
|
||||
static bool api_authed = false;
|
||||
static char api_session[API_SMAXSIZE] = {0}; /* No escaping is needed */
|
||||
@ -51,12 +47,14 @@ static bool api_encryption = false;
|
||||
|
||||
static pthread_t api_ka_thread = 0;
|
||||
static pthread_mutex_t api_work_mx;
|
||||
static bool api_ka_now = false; /* Are we doing keepalive now? */
|
||||
|
||||
static struct timespec api_last_packet = {0}; /* Last packet time */
|
||||
static int32_t api_packet_count = 0; /* Only increment */
|
||||
//static int32_t api_fast_packet_count = 0; /* Increment or decrement */
|
||||
|
||||
/* For some commands, we need a global retry counter */
|
||||
static int32_t api_g_retry_count = 0;
|
||||
|
||||
static int api_escaped_string(FILE *io, const struct printf_info *info,
|
||||
const void *const *args)
|
||||
{
|
||||
@ -93,41 +91,44 @@ static int api_escaped_sring_info(const struct printf_info *info, size_t n,
|
||||
|
||||
static enum error api_init_encrypt(const char *api_key, const char *uname)
|
||||
{
|
||||
char buffer[API_BUFSIZE];
|
||||
MD5Context md5_ctx;
|
||||
char *salt_start = buffer + 4 /* 209 [salt here] ... */, *salt_end;
|
||||
ssize_t r_len, salt_len;
|
||||
struct api_result res;
|
||||
enum error err;
|
||||
size_t salt_len;
|
||||
|
||||
if (net_send(buffer, snprintf(buffer, sizeof(buffer),
|
||||
"ENCRYPT user=%s&type=1", uname)) == -1) {
|
||||
return ERR_API_COMMFAIL;
|
||||
}
|
||||
r_len = net_read(buffer, sizeof(buffer));
|
||||
|
||||
if (strncmp(buffer, "209", 3) != 0) {
|
||||
uio_error("We expected 209 response, but got: %.*s",
|
||||
(int)r_len, buffer);
|
||||
if (api_cmd_encrypt(uname, &res) != NOERR)
|
||||
return ERR_API_ENCRYPTFAIL;
|
||||
}
|
||||
|
||||
salt_end = strchr(salt_start, ' ');
|
||||
if (!salt_end) {
|
||||
uio_error("Cannot find space after salt in response");
|
||||
return ERR_API_ENCRYPTFAIL;
|
||||
if (res.code != 209) {
|
||||
err = ERR_API_ENCRYPTFAIL;
|
||||
switch (res.code) {
|
||||
case 309:
|
||||
uio_error("You'r API key is not defined. Define it here: "
|
||||
"http://anidb.net/perl-bin/animedb.pl?show=profile");
|
||||
break;
|
||||
case 509:
|
||||
uio_error("No such encryption type. Maybe client is outdated?");
|
||||
break;
|
||||
case 394:
|
||||
uio_error("No user with name: '%s' found by AniDB", uname);
|
||||
break;
|
||||
default:
|
||||
uio_error("Unknown encrypt failure: %ld", res.code);
|
||||
}
|
||||
salt_len = salt_end - salt_start;
|
||||
return err;
|
||||
}
|
||||
salt_len = strlen(res.encrypt.salt);
|
||||
|
||||
md5Init(&md5_ctx);
|
||||
md5Update(&md5_ctx, (uint8_t*)api_key, strlen(api_key));
|
||||
md5Update(&md5_ctx, (uint8_t*)salt_start, salt_len);
|
||||
md5Update(&md5_ctx, (uint8_t*)res.encrypt.salt, salt_len);
|
||||
md5Finalize(&md5_ctx);
|
||||
memcpy(e_key, md5_ctx.digest, sizeof(e_key));
|
||||
|
||||
#if 1
|
||||
char *buffpos = buffer;
|
||||
for (int i = 0; i < 16; i++)
|
||||
buffpos += sprintf(buffpos, "%02x", e_key[i]);
|
||||
uio_debug("Encryption key is: '%s'", buffer);
|
||||
char bf[sizeof(e_key) * 2 + 1];
|
||||
util_byte2hex(e_key, sizeof(e_key), false, bf);
|
||||
uio_debug("Encryption key is: '%s'", bf);
|
||||
#endif
|
||||
|
||||
api_encryption = true;
|
||||
@ -181,16 +182,29 @@ static size_t api_decrypt(char *buffer, size_t data_len)
|
||||
return ret_len;
|
||||
}
|
||||
|
||||
static enum error api_auth(const char* uname, const char *passw)
|
||||
static enum error api_auth()
|
||||
{
|
||||
struct api_result res;
|
||||
enum error err = NOERR;
|
||||
char **uname, **passw;
|
||||
|
||||
if (config_get("username", (void**)&uname) != NOERR) {
|
||||
uio_error("Username is not specified, but it is required!");
|
||||
return ERR_OPT_REQUIRED;
|
||||
}
|
||||
if (config_get("password", (void**)&passw) != NOERR) {
|
||||
uio_error("Password is not specified, but it is required!");
|
||||
return ERR_OPT_REQUIRED;
|
||||
}
|
||||
/*
|
||||
* We could try passing in a session key, if we are executing
|
||||
* a login in response to a nologin or inv session error code?
|
||||
*/
|
||||
|
||||
if (!api_encryption)
|
||||
uio_warning("Logging in without encryption!");
|
||||
if (api_cmd_auth(uname, passw, &res) != NOERR) {
|
||||
if (api_cmd_auth(*uname, *passw, &res) != NOERR)
|
||||
return ERR_API_AUTH_FAIL;
|
||||
}
|
||||
|
||||
switch (res.code) {
|
||||
case 201:
|
||||
@ -213,12 +227,6 @@ static enum error api_auth(const char* uname, const char *passw)
|
||||
uio_error("Client is banned :( Reason: %s", res.auth.banned_reason);
|
||||
free(res.auth.banned_reason);
|
||||
break;
|
||||
case 505:
|
||||
uio_error("Illegal input or access denied");
|
||||
break;
|
||||
case 601:
|
||||
uio_error("AniDB out of service");
|
||||
break;
|
||||
default:
|
||||
uio_error("Unknown error: %hu", res.code);
|
||||
break;
|
||||
@ -296,13 +304,11 @@ void *api_keepalive_main(void *arg)
|
||||
* Could be replaced with a pthread_cleanup_push ? */
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
pthread_mutex_lock(&api_work_mx);
|
||||
api_ka_now = true;
|
||||
|
||||
uio_debug("G'moooooning! Is it time to keep our special connection alive?");
|
||||
|
||||
api_keepalive(&ka_time);
|
||||
uio_debug("Next wakey-wakey in %ld seconds", ka_time.tv_sec);
|
||||
api_ka_now = false;
|
||||
pthread_mutex_unlock(&api_work_mx);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
}
|
||||
@ -328,7 +334,7 @@ enum error api_clock_init()
|
||||
enum error api_init(bool auth)
|
||||
{
|
||||
enum error err = NOERR;
|
||||
const char **api_key, **uname, **passwd;
|
||||
const char **api_key, **uname;
|
||||
|
||||
err = api_clock_init();
|
||||
if (err != NOERR)
|
||||
@ -347,6 +353,7 @@ enum error api_init(bool auth)
|
||||
}
|
||||
err = api_init_encrypt(*api_key, *uname);
|
||||
if (err != NOERR) {
|
||||
if (err != ERR_SHOULD_EXIT)
|
||||
uio_error("Cannot init api encryption");
|
||||
goto fail;
|
||||
}
|
||||
@ -361,26 +368,24 @@ enum error api_init(bool auth)
|
||||
}
|
||||
|
||||
if (auth) {
|
||||
if (config_get("username", (void**)&uname) != NOERR) {
|
||||
uio_error("Username is not specified, but it is required!");
|
||||
err = ERR_OPT_REQUIRED;
|
||||
goto fail;
|
||||
}
|
||||
if (config_get("password", (void**)&passwd) != NOERR) {
|
||||
uio_error("Password is not specified, but it is required!");
|
||||
err = ERR_OPT_REQUIRED;
|
||||
goto fail;
|
||||
}
|
||||
err = api_auth(*uname, *passwd);
|
||||
pthread_mutexattr_t attr;
|
||||
int mxres;
|
||||
|
||||
err = api_auth();
|
||||
if (err != NOERR)
|
||||
goto fail;
|
||||
|
||||
/* Only do keep alive if we have a session */
|
||||
if (pthread_mutex_init(&api_work_mx, NULL) != 0) {
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
mxres = pthread_mutex_init(&api_work_mx, NULL);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
if (mxres != 0) {
|
||||
uio_error("Cannot create mutex");
|
||||
err = ERR_THRD;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pthread_create(&api_ka_thread, NULL, api_keepalive_main, NULL) != 0) {
|
||||
uio_error("Cannot create api keepalive thread");
|
||||
err = ERR_THRD;
|
||||
@ -409,10 +414,12 @@ void api_free()
|
||||
uio_error("Cannot cancel api keepalive thread");
|
||||
} else {
|
||||
int je = pthread_join(api_ka_thread, NULL);
|
||||
if (je != 0) {
|
||||
if (je != 0)
|
||||
uio_error("Cannot join api keepalive thread: %s",
|
||||
strerror(je));
|
||||
}
|
||||
else
|
||||
uio_debug("Keepalive thread ended");
|
||||
|
||||
if (pthread_mutex_destroy(&api_work_mx) != 0)
|
||||
uio_error("Cannot destroy api work mutex");
|
||||
}
|
||||
@ -473,21 +480,33 @@ static void api_ratelimit()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the written byte count
|
||||
* Or -1 on error, and -2 if errno was EINTR
|
||||
*/
|
||||
static ssize_t api_send(char *buffer, size_t data_len, size_t buf_size)
|
||||
{
|
||||
ssize_t read_len;
|
||||
int en;
|
||||
|
||||
api_ratelimit();
|
||||
uio_debug("{Api}: Sending: %.*s", (int)data_len, buffer);
|
||||
if (api_encryption)
|
||||
data_len = api_encrypt(buffer, data_len);
|
||||
|
||||
if (net_send(buffer, data_len) == -1) {
|
||||
uio_error("Cannot send data: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
en = net_send(buffer, data_len);
|
||||
if (en < 0)
|
||||
return en;
|
||||
|
||||
read_len = net_read(buffer, buf_size);
|
||||
if (read_len < 0) {
|
||||
uio_error("!!! BAD PLACE EINTR !!! report pls");
|
||||
return read_len; /* This could lead so some problems if we also want to
|
||||
log out. If we hit this, the msg got sent, but we
|
||||
couldn't read the response. That means, in the
|
||||
logout call, this msg's data will be read
|
||||
Let's see if this ever comes up */
|
||||
}
|
||||
api_ratelimit_sent();
|
||||
|
||||
if (api_encryption)
|
||||
@ -558,15 +577,81 @@ static char *api_get_field_mod(char *buffer, int32_t field_num)
|
||||
}
|
||||
#endif
|
||||
|
||||
enum error api_cmd_version(struct api_result *res)
|
||||
/* Basically convert remote codes into local error codes */
|
||||
static enum error api_cmd_base_errorc(long code, char buffer[API_BUFSIZE])
|
||||
{
|
||||
char buffer[API_BUFSIZE] = "VERSION";
|
||||
size_t res_len = api_send(buffer, strlen(buffer), sizeof(buffer));
|
||||
long code;
|
||||
enum error err = NOERR;
|
||||
pthread_mutex_lock(&api_work_mx);
|
||||
switch (code) {
|
||||
case APICODE_ILLEGAL_INPUT_OR_ACCESS_DENIED:
|
||||
uio_error("Got unretryable error code: %d", code);
|
||||
return ERR_API_INVCOMM;
|
||||
case APICODE_BANNED:
|
||||
{
|
||||
char *ls;
|
||||
size_t ll;
|
||||
|
||||
if (res_len == -1) {
|
||||
api_get_line(buffer, 2, &ls, &ll);
|
||||
uio_error("Banned: %.*s", (int)ll, ls);
|
||||
return ERR_API_BANNED;
|
||||
}
|
||||
case APICODE_UNKNOWN_COMMAND:
|
||||
uio_error("The sent command is unknown");
|
||||
return ERR_API_CMD_UNK;
|
||||
case APICODE_INTERNAL_SERVER_ERROR:
|
||||
uio_error("Internal server error!");
|
||||
return ERR_API_INT_SRV;
|
||||
case APICODE_ANIDB_OUT_OF_SERVICE:
|
||||
uio_error("AniDB is currently out of service");
|
||||
return ERR_API_OOS;
|
||||
case APICODE_SERVER_BUSY:
|
||||
uio_warning("Server is busy rn, trying again later");
|
||||
return ERR_API_SRV_BUSY;
|
||||
case APICODE_TIMEOUT:
|
||||
uio_debug("Timed out, retrying");
|
||||
return ERR_API_TIMEOUT;
|
||||
case APICODE_LOGIN_FIRST:
|
||||
uio_error("This command required AUTH");
|
||||
return ERR_API_NOLOGIN;
|
||||
case APICODE_ACCESS_DENIED:
|
||||
uio_error("Access is denied for this info");
|
||||
return ERR_API_AXX_DENIED;
|
||||
case APICODE_INVALID_SESSION:
|
||||
uio_error("The login session is invalid");
|
||||
return ERR_API_INV_SESSION;
|
||||
default:
|
||||
/* Not an error, or at least not a base error */
|
||||
return NOERR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Base for all api_cmd's. This will also execute the default
|
||||
* error code handers, like 505, 555, 604...
|
||||
* If success, res.code will be filled out
|
||||
*/
|
||||
static enum error api_cmd_base(char buffer[API_BUFSIZE], struct api_result *res,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
int send_len;
|
||||
enum error err = NOERR;
|
||||
va_list ap;
|
||||
int retry_count = 0;
|
||||
|
||||
va_start(ap, fmt);
|
||||
send_len = vsnprintf(buffer, API_BUFSIZE, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
pthread_mutex_lock(&api_work_mx);
|
||||
api_g_retry_count = 0;
|
||||
|
||||
while (retry_count < API_MAX_TRYAGAIN &&
|
||||
api_g_retry_count < API_MAX_TRYAGAIN) {
|
||||
long code;
|
||||
ssize_t res_len = api_send(buffer, send_len, API_BUFSIZE);
|
||||
|
||||
if (res_len < 0) {
|
||||
if (res_len == -2 && should_exit)
|
||||
err = ERR_SHOULD_EXIT;
|
||||
else
|
||||
err = ERR_API_COMMFAIL;
|
||||
goto end;
|
||||
}
|
||||
@ -576,8 +661,86 @@ enum error api_cmd_version(struct api_result *res)
|
||||
err = ERR_API_RESP_INVALID;
|
||||
goto end;
|
||||
}
|
||||
res->code = code;
|
||||
|
||||
if (code == 998) {
|
||||
err = api_cmd_base_errorc(code, buffer);
|
||||
if (err == ERR_API_OOS || err == ERR_API_SRV_BUSY ||
|
||||
err == ERR_API_TIMEOUT) {
|
||||
struct timespec ts;
|
||||
|
||||
MS_TO_TIMESPEC_L(ts, API_TRYAGAIN_TIME);
|
||||
retry_count++;
|
||||
uio_debug("Retry after %ld ms (%d/%d)", API_TRYAGAIN_TIME,
|
||||
retry_count, API_MAX_TRYAGAIN);
|
||||
if (nanosleep(&ts, NULL) == -1) {
|
||||
if (errno == EINTR && should_exit) {
|
||||
err = ERR_SHOULD_EXIT;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (err == ERR_API_NOLOGIN || err == ERR_API_INV_SESSION) {
|
||||
api_g_retry_count++;
|
||||
if (api_g_retry_count < API_MAX_TRYAGAIN) {
|
||||
uio_debug("Let's try loggin in agane");
|
||||
api_authed = false; /* We got logged out probably */
|
||||
err = api_auth(); /* -> will call this function */
|
||||
if (api_g_retry_count < API_MAX_TRYAGAIN)
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
};
|
||||
if (retry_count >= API_MAX_TRYAGAIN ||
|
||||
api_g_retry_count >= API_MAX_TRYAGAIN) {
|
||||
uio_debug("Max retry count reached");
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
pthread_mutex_unlock(&api_work_mx);
|
||||
return err;
|
||||
}
|
||||
|
||||
static enum error api_cmd_encrypt(const char *uname, struct api_result *res)
|
||||
{
|
||||
char buffer[API_BUFSIZE];
|
||||
enum error err;
|
||||
|
||||
/* Usernames can't contain '&' */
|
||||
err = api_cmd_base(buffer, res, "ENCRYPT user=%s&type=1", uname);
|
||||
if (err != NOERR)
|
||||
return err;
|
||||
|
||||
if (res->code == APICODE_ENCRYPTION_ENABLED) {
|
||||
char *fs;
|
||||
size_t fl;
|
||||
bool gfl = api_get_field(buffer, 2, &fs, &fl);
|
||||
|
||||
assert(gfl);
|
||||
(void)gfl;
|
||||
assert(sizeof(res->encrypt.salt) > fl);
|
||||
memcpy(res->encrypt.salt, fs, fl);
|
||||
res->encrypt.salt[fl] = '\0';
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
enum error api_cmd_version(struct api_result *res)
|
||||
{
|
||||
char buffer[API_BUFSIZE];
|
||||
enum error err;
|
||||
|
||||
err = api_cmd_base(buffer, res, "VERSION");
|
||||
if (err != NOERR)
|
||||
return err;
|
||||
|
||||
if (res->code == APICODE_VERSION) {
|
||||
char *ver_start;
|
||||
size_t ver_len;
|
||||
bool glr = api_get_line(buffer, 2, &ver_start, &ver_len);
|
||||
@ -588,37 +751,24 @@ enum error api_cmd_version(struct api_result *res)
|
||||
memcpy(res->version.version_str, ver_start, ver_len);
|
||||
res->version.version_str[ver_len] = '\0';
|
||||
}
|
||||
res->code = (uint16_t)code;
|
||||
|
||||
end:
|
||||
pthread_mutex_unlock(&api_work_mx);
|
||||
return err;
|
||||
}
|
||||
|
||||
static enum error api_cmd_auth(const char *uname, const char *pass,
|
||||
struct api_result *res)
|
||||
{
|
||||
pthread_mutex_lock(&api_work_mx);
|
||||
char buffer[API_BUFSIZE];
|
||||
long code;
|
||||
size_t res_len = api_send(buffer, snprintf(buffer, sizeof(buffer),
|
||||
"AUTH user=%s&pass=%B&protover=" API_VERSION "&client=caniadd&"
|
||||
"clientver=" PROG_VERSION "&enc=UTF-8", uname, pass),
|
||||
sizeof(buffer));
|
||||
enum error err = NOERR;
|
||||
enum error err;
|
||||
|
||||
if (res_len == -1) {
|
||||
err = ERR_API_COMMFAIL;
|
||||
goto end;
|
||||
}
|
||||
err = api_cmd_base(buffer, res, "AUTH user=%s&pass=%B&protover="
|
||||
API_VERSION "&client=caniadd&clientver=" PROG_VERSION
|
||||
"&enc=UTF-8", uname, pass);
|
||||
if (err != NOERR)
|
||||
return err;
|
||||
|
||||
code = api_res_code(buffer);
|
||||
if (code == -1) {
|
||||
err = ERR_API_RESP_INVALID;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (code == 200 || code == 201) {
|
||||
if (res->code == APICODE_LOGIN_ACCEPTED ||
|
||||
res->code == APICODE_LOGIN_ACCEPTED_NEW_VERSION) {
|
||||
char *sess;
|
||||
size_t sess_len;
|
||||
bool gfr = api_get_field(buffer, 2, &sess, &sess_len);
|
||||
@ -628,7 +778,7 @@ static enum error api_cmd_auth(const char *uname, const char *pass,
|
||||
assert(sess_len < sizeof(res->auth.session_key));
|
||||
memcpy(res->auth.session_key, sess, sess_len);
|
||||
res->auth.session_key[sess_len] = '\0';
|
||||
} else if (code == 504) {
|
||||
} else if (res->code == APICODE_CLIENT_BANNED) {
|
||||
char *reason;
|
||||
size_t reason_len;
|
||||
bool gfr = api_get_field(buffer, 5, &reason, &reason_len);
|
||||
@ -637,64 +787,32 @@ static enum error api_cmd_auth(const char *uname, const char *pass,
|
||||
(void)gfr;
|
||||
res->auth.banned_reason = strndup(reason, reason_len);
|
||||
}
|
||||
res->code = (uint16_t)code;
|
||||
|
||||
end:
|
||||
pthread_mutex_unlock(&api_work_mx);
|
||||
return err;
|
||||
}
|
||||
|
||||
static enum error api_cmd_logout(struct api_result *res)
|
||||
{
|
||||
pthread_mutex_lock(&api_work_mx);
|
||||
char buffer[API_BUFSIZE];
|
||||
size_t res_len = api_send(buffer, snprintf(buffer, sizeof(buffer),
|
||||
"LOGOUT s=%s", api_session), sizeof(buffer));
|
||||
long code;
|
||||
enum error err = NOERR;
|
||||
enum error err;
|
||||
|
||||
if (res_len == -1) {
|
||||
err = ERR_API_COMMFAIL;
|
||||
goto end;
|
||||
}
|
||||
err = api_cmd_base(buffer, res, "LOGOUT s=%s", api_session);
|
||||
if (err != NOERR)
|
||||
return err;
|
||||
|
||||
code = api_res_code(buffer);
|
||||
if (code == -1) {
|
||||
err = ERR_API_RESP_INVALID;
|
||||
goto end;
|
||||
}
|
||||
|
||||
res->code = (uint16_t)code;
|
||||
|
||||
end:
|
||||
pthread_mutex_unlock(&api_work_mx);
|
||||
return err;
|
||||
}
|
||||
|
||||
enum error api_cmd_uptime(struct api_result *res)
|
||||
{
|
||||
/* If mutex is not already locked from the keepalive thread */
|
||||
/* Or we could use a recursive mutex? */
|
||||
if (!api_ka_now)
|
||||
pthread_mutex_lock(&api_work_mx);
|
||||
char buffer[API_BUFSIZE];
|
||||
size_t res_len = api_send(buffer, snprintf(buffer, sizeof(buffer),
|
||||
"UPTIME s=%s", api_session), sizeof(buffer));
|
||||
long code;
|
||||
enum error err = NOERR;
|
||||
enum error err;
|
||||
|
||||
if (res_len == -1) {
|
||||
err = ERR_API_COMMFAIL;
|
||||
goto end;
|
||||
}
|
||||
err = api_cmd_base(buffer, res, "UPTIME s=%s", api_session);
|
||||
if (err != NOERR)
|
||||
return err;
|
||||
|
||||
code = api_res_code(buffer);
|
||||
if (code == -1) {
|
||||
err = ERR_API_RESP_INVALID;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (code == 208) {
|
||||
if (res->code == APICODE_UPTIME) {
|
||||
char *ls;
|
||||
size_t ll;
|
||||
bool glf = api_get_line(buffer, 2, &ls, &ll);
|
||||
@ -704,11 +822,6 @@ enum error api_cmd_uptime(struct api_result *res)
|
||||
res->uptime.ms = strtol(ls, NULL, 10);
|
||||
}
|
||||
|
||||
res->code = (uint16_t)code;
|
||||
|
||||
end:
|
||||
if (!api_ka_now)
|
||||
pthread_mutex_unlock(&api_work_mx);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -717,30 +830,17 @@ enum error api_cmd_mylistadd(int64_t size, const uint8_t *hash,
|
||||
{
|
||||
char buffer[API_BUFSIZE];
|
||||
char hash_str[ED2K_HASH_SIZE * 2 + 1];
|
||||
size_t res_len;
|
||||
enum error err = NOERR;
|
||||
long code;
|
||||
pthread_mutex_lock(&api_work_mx);
|
||||
|
||||
util_byte2hex(hash, ED2K_HASH_SIZE, false, hash_str);
|
||||
/* Wiki says file size is 4 bytes, but no way that's true lol */
|
||||
res_len = api_send(buffer, snprintf(buffer, sizeof(buffer),
|
||||
err = api_cmd_base(buffer, res,
|
||||
"MYLISTADD s=%s&size=%ld&ed2k=%s&state=%hu&viewed=%d",
|
||||
api_session, size, hash_str, ml_state, watched),
|
||||
sizeof(buffer));
|
||||
api_session, size, hash_str, ml_state, watched);
|
||||
if (err != NOERR)
|
||||
return err;
|
||||
|
||||
if (res_len == -1) {
|
||||
err = ERR_API_COMMFAIL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
code = api_res_code(buffer);
|
||||
if (code == -1) {
|
||||
err = ERR_API_RESP_INVALID;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (code == 210) {
|
||||
if (res->code == APICODE_MYLIST_ENTRY_ADDED) {
|
||||
char *ls, id_str[12];
|
||||
size_t ll;
|
||||
bool glr = api_get_line(buffer, 2, &ls, &ll);
|
||||
@ -754,7 +854,7 @@ enum error api_cmd_mylistadd(int64_t size, const uint8_t *hash,
|
||||
/* Wiki says these id's are 4 bytes, which is untrue...
|
||||
* that page may be a little out of date (or they just
|
||||
* expect us to use common sense lmao */
|
||||
} else if (code == 310) {
|
||||
} else if (res->code == APICODE_FILE_ALREADY_IN_MYLIST) {
|
||||
/* {int4 lid}|{int4 fid}|{int4 eid}|{int4 aid}|{int4 gid}|
|
||||
* {int4 date}|{int2 state}|{int4 viewdate}|{str storage}|
|
||||
* {str source}|{str other}|{int2 filestate} */
|
||||
@ -802,10 +902,6 @@ enum error api_cmd_mylistadd(int64_t size, const uint8_t *hash,
|
||||
}
|
||||
}
|
||||
|
||||
res->code = (uint16_t)code;
|
||||
|
||||
end:
|
||||
pthread_mutex_unlock(&api_work_mx);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
165
src/api.h
165
src/api.h
@ -11,9 +11,11 @@
|
||||
#define API_BUFSIZE 1400
|
||||
/* Session key maximum size, including '\0' */
|
||||
#define API_SMAXSIZE 16
|
||||
/* Encryption salt maximum size, including '\0' */
|
||||
#define API_SALTMAXSIZE 16
|
||||
|
||||
/* The session timeout in miliseconds */
|
||||
#define API_TIMEOUT 30 * 60 * 1000
|
||||
|
||||
/* How many miliseconds to wait between sends */
|
||||
#define API_SENDWAIT 2 * 1000
|
||||
/* The number of packets that are exccempt from the ratelimit */
|
||||
@ -23,6 +25,163 @@
|
||||
/* After this many packets has been sent, use the longterm ratelimit */
|
||||
#define API_LONGTERM_PACKETS 100
|
||||
|
||||
/* How much to wait between try agains? in ms */
|
||||
#define API_TRYAGAIN_TIME 60 * 1000
|
||||
#define API_MAX_TRYAGAIN 5
|
||||
|
||||
enum api_code {
|
||||
APICODE_LOGIN_ACCEPTED = 200,
|
||||
APICODE_LOGIN_ACCEPTED_NEW_VERSION = 201,
|
||||
APICODE_LOGGED_OUT = 203,
|
||||
APICODE_RESOURCE = 205,
|
||||
APICODE_STATS = 206,
|
||||
APICODE_TOP = 207,
|
||||
APICODE_UPTIME = 208,
|
||||
APICODE_ENCRYPTION_ENABLED = 209,
|
||||
APICODE_MYLIST_ENTRY_ADDED = 210,
|
||||
APICODE_MYLIST_ENTRY_DELETED = 211,
|
||||
APICODE_ADDED_FILE = 214,
|
||||
APICODE_ADDED_STREAM = 215,
|
||||
APICODE_EXPORT_QUEUED = 217,
|
||||
APICODE_EXPORT_CANCELLED = 218,
|
||||
APICODE_ENCODING_CHANGED = 219,
|
||||
APICODE_FILE = 220,
|
||||
APICODE_MYLIST = 221,
|
||||
APICODE_MYLIST_STATS = 222,
|
||||
APICODE_WISHLIST = 223,
|
||||
APICODE_NOTIFICATION = 224,
|
||||
APICODE_GROUP_STATUS = 225,
|
||||
APICODE_WISHLIST_ENTRY_ADDED = 226,
|
||||
APICODE_WISHLIST_ENTRY_DELETED = 227,
|
||||
APICODE_WISHLIST_ENTRY_UPDATED = 228,
|
||||
APICODE_MULTIPLE_WISHLIST = 229,
|
||||
APICODE_ANIME = 230,
|
||||
APICODE_ANIME_BEST_MATCH = 231,
|
||||
APICODE_RANDOM_ANIME = 232,
|
||||
APICODE_ANIME_DESCRIPTION = 233,
|
||||
APICODE_REVIEW = 234,
|
||||
APICODE_CHARACTER = 235,
|
||||
APICODE_SONG = 236,
|
||||
APICODE_ANIMETAG = 237,
|
||||
APICODE_CHARACTERTAG = 238,
|
||||
APICODE_EPISODE = 240,
|
||||
APICODE_UPDATED = 243,
|
||||
APICODE_TITLE = 244,
|
||||
APICODE_CREATOR = 245,
|
||||
APICODE_NOTIFICATION_ENTRY_ADDED = 246,
|
||||
APICODE_NOTIFICATION_ENTRY_DELETED = 247,
|
||||
APICODE_NOTIFICATION_ENTRY_UPDATE = 248,
|
||||
APICODE_MULTIPLE_NOTIFICATION = 249,
|
||||
APICODE_GROUP = 250,
|
||||
APICODE_CATEGORY = 251,
|
||||
APICODE_BUDDY_LIST = 253,
|
||||
APICODE_BUDDY_STATE = 254,
|
||||
APICODE_BUDDY_ADDED = 255,
|
||||
APICODE_BUDDY_DELETED = 256,
|
||||
APICODE_BUDDY_ACCEPTED = 257,
|
||||
APICODE_BUDDY_DENIED = 258,
|
||||
APICODE_VOTED = 260,
|
||||
APICODE_VOTE_FOUND = 261,
|
||||
APICODE_VOTE_UPDATED = 262,
|
||||
APICODE_VOTE_REVOKED = 263,
|
||||
APICODE_HOT_ANIME = 265,
|
||||
APICODE_RANDOM_RECOMMENDATION = 266,
|
||||
APICODE_RANDOM_SIMILAR = 267,
|
||||
APICODE_NOTIFICATION_ENABLED = 270,
|
||||
APICODE_NOTIFYACK_SUCCESSFUL_MESSAGE = 281,
|
||||
APICODE_NOTIFYACK_SUCCESSFUL_NOTIFICATION = 282,
|
||||
APICODE_NOTIFICATION_STATE = 290,
|
||||
APICODE_NOTIFYLIST = 291,
|
||||
APICODE_NOTIFYGET_MESSAGE = 292,
|
||||
APICODE_NOTIFYGET_NOTIFY = 293,
|
||||
APICODE_SENDMESSAGE_SUCCESSFUL = 294,
|
||||
APICODE_USER_ID = 295,
|
||||
APICODE_CALENDAR = 297,
|
||||
|
||||
APICODE_PONG = 300,
|
||||
APICODE_AUTHPONG = 301,
|
||||
APICODE_NO_SUCH_RESOURCE = 305,
|
||||
APICODE_API_PASSWORD_NOT_DEFINED = 309,
|
||||
APICODE_FILE_ALREADY_IN_MYLIST = 310,
|
||||
APICODE_MYLIST_ENTRY_EDITED = 311,
|
||||
APICODE_MULTIPLE_MYLIST_ENTRIES = 312,
|
||||
APICODE_WATCHED = 313,
|
||||
APICODE_SIZE_HASH_EXISTS = 314,
|
||||
APICODE_INVALID_DATA = 315,
|
||||
APICODE_STREAMNOID_USED = 316,
|
||||
APICODE_EXPORT_NO_SUCH_TEMPLATE = 317,
|
||||
APICODE_EXPORT_ALREADY_IN_QUEUE = 318,
|
||||
APICODE_EXPORT_NO_EXPORT_QUEUED_OR_IS_PROCESSING = 319,
|
||||
APICODE_NO_SUCH_FILE = 320,
|
||||
APICODE_NO_SUCH_ENTRY = 321,
|
||||
APICODE_MULTIPLE_FILES_FOUND = 322,
|
||||
APICODE_NO_SUCH_WISHLIST = 323,
|
||||
APICODE_NO_SUCH_NOTIFICATION = 324,
|
||||
APICODE_NO_GROUPS_FOUND = 325,
|
||||
APICODE_NO_SUCH_ANIME = 330,
|
||||
APICODE_NO_SUCH_DESCRIPTION = 333,
|
||||
APICODE_NO_SUCH_REVIEW = 334,
|
||||
APICODE_NO_SUCH_CHARACTER = 335,
|
||||
APICODE_NO_SUCH_SONG = 336,
|
||||
APICODE_NO_SUCH_ANIMETAG = 337,
|
||||
APICODE_NO_SUCH_CHARACTERTAG = 338,
|
||||
APICODE_NO_SUCH_EPISODE = 340,
|
||||
APICODE_NO_SUCH_UPDATES = 343,
|
||||
APICODE_NO_SUCH_TITLES = 344,
|
||||
APICODE_NO_SUCH_CREATOR = 345,
|
||||
APICODE_NO_SUCH_GROUP = 350,
|
||||
APICODE_NO_SUCH_CATEGORY = 351,
|
||||
APICODE_BUDDY_ALREADY_ADDED = 355,
|
||||
APICODE_NO_SUCH_BUDDY = 356,
|
||||
APICODE_BUDDY_ALREADY_ACCEPTED = 357,
|
||||
APICODE_BUDDY_ALREADY_DENIED = 358,
|
||||
APICODE_NO_SUCH_VOTE = 360,
|
||||
APICODE_INVALID_VOTE_TYPE = 361,
|
||||
APICODE_INVALID_VOTE_VALUE = 362,
|
||||
APICODE_PERMVOTE_NOT_ALLOWED = 363,
|
||||
APICODE_ALREADY_PERMVOTED = 364,
|
||||
APICODE_HOT_ANIME_EMPTY = 365,
|
||||
APICODE_RANDOM_RECOMMENDATION_EMPTY = 366,
|
||||
APICODE_RANDOM_SIMILAR_EMPTY = 367,
|
||||
APICODE_NOTIFICATION_DISABLED = 370,
|
||||
APICODE_NO_SUCH_ENTRY_MESSAGE = 381,
|
||||
APICODE_NO_SUCH_ENTRY_NOTIFICATION = 382,
|
||||
APICODE_NO_SUCH_MESSAGE = 392,
|
||||
APICODE_NO_SUCH_NOTIFY = 393,
|
||||
APICODE_NO_SUCH_USER = 394,
|
||||
APICODE_CALENDAR_EMPTY = 397,
|
||||
APICODE_NO_CHANGES = 399,
|
||||
|
||||
APICODE_NOT_LOGGED_IN = 403,
|
||||
APICODE_NO_SUCH_MYLIST_FILE = 410,
|
||||
APICODE_NO_SUCH_MYLIST_ENTRY = 411,
|
||||
APICODE_MYLIST_UNAVAILABLE = 412,
|
||||
|
||||
APICODE_LOGIN_FAILED = 500,
|
||||
APICODE_LOGIN_FIRST = 501,
|
||||
APICODE_ACCESS_DENIED = 502,
|
||||
APICODE_CLIENT_VERSION_OUTDATED = 503,
|
||||
APICODE_CLIENT_BANNED = 504,
|
||||
APICODE_ILLEGAL_INPUT_OR_ACCESS_DENIED = 505,
|
||||
APICODE_INVALID_SESSION = 506,
|
||||
APICODE_NO_SUCH_ENCRYPTION_TYPE = 509,
|
||||
APICODE_ENCODING_NOT_SUPPORTED = 519,
|
||||
APICODE_BANNED = 555,
|
||||
APICODE_UNKNOWN_COMMAND = 598,
|
||||
|
||||
APICODE_INTERNAL_SERVER_ERROR = 600,
|
||||
APICODE_ANIDB_OUT_OF_SERVICE = 601,
|
||||
APICODE_SERVER_BUSY = 602,
|
||||
APICODE_NO_DATA = 603,
|
||||
APICODE_TIMEOUT = 604,
|
||||
APICODE_API_VIOLATION = 666,
|
||||
|
||||
APICODE_PUSHACK_CONFIRMED = 701,
|
||||
APICODE_NO_SUCH_PACKET_PENDING = 702,
|
||||
|
||||
APICODE_VERSION = 998,
|
||||
};
|
||||
|
||||
enum mylist_state {
|
||||
MYLIST_STATE_UNKNOWN = 0,
|
||||
MYLIST_STATE_INTERNAL,
|
||||
@ -54,6 +213,9 @@ struct api_auth_result {
|
||||
char *banned_reason;
|
||||
};
|
||||
};
|
||||
struct api_encrypt_result {
|
||||
char salt[API_SALTMAXSIZE];
|
||||
};
|
||||
struct api_uptime_result {
|
||||
int32_t ms;
|
||||
};
|
||||
@ -78,6 +240,7 @@ struct api_result {
|
||||
struct api_auth_result auth;
|
||||
struct api_uptime_result uptime;
|
||||
e(mylistadd);
|
||||
e(encrypt);
|
||||
};
|
||||
};
|
||||
#undef e
|
||||
|
@ -1,17 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "error.h"
|
||||
#include "uio.h"
|
||||
#include "cmd.h"
|
||||
#include "globals.h"
|
||||
|
||||
bool should_exit = false;
|
||||
|
||||
static void signal_handler(int signum, siginfo_t *info, void *ctx)
|
||||
{
|
||||
should_exit = true;
|
||||
printf("\033[0GGot C-c. Press again to force exit\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int exit_code = EXIT_SUCCESS;
|
||||
enum error err = config_parse(argc, argv);
|
||||
enum error err;
|
||||
struct sigaction sact = {
|
||||
.sa_flags = SA_SIGINFO | SA_RESETHAND,
|
||||
//.sa_flags = SA_SIGINFO,
|
||||
.sa_sigaction = signal_handler,
|
||||
};
|
||||
|
||||
if (sigaction(SIGINT, &sact, NULL) != 0) {
|
||||
uio_error("Cannot set up signal handler: %s", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
err = config_parse(argc, argv);
|
||||
if (err == ERR_OPT_EXIT)
|
||||
return EXIT_SUCCESS;
|
||||
else if (err != NOERR)
|
||||
@ -20,7 +42,9 @@ int main(int argc, char **argv)
|
||||
//config_dump();
|
||||
|
||||
err = cmd_main();
|
||||
if (err != NOERR)
|
||||
if (err == ERR_SHOULD_EXIT)
|
||||
uio_debug("Exiting as requested orz");
|
||||
else if (err != NOERR)
|
||||
exit_code = EXIT_FAILURE;
|
||||
|
||||
config_free();
|
||||
|
@ -3,10 +3,12 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ftw.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "ed2k.h"
|
||||
#include "ed2k_util.h"
|
||||
#include "uio.h"
|
||||
#include "globals.h"
|
||||
|
||||
static struct ed2k_util_opts l_opts;
|
||||
|
||||
@ -15,11 +17,13 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize,
|
||||
{
|
||||
unsigned char buf[blksize], hash[ED2K_HASH_SIZE];
|
||||
struct ed2k_ctx ed2k;
|
||||
enum error err;
|
||||
FILE *f;
|
||||
size_t read_len;
|
||||
int en;
|
||||
|
||||
if (l_opts.pre_hash_fn) {
|
||||
enum error err = l_opts.pre_hash_fn(file_path, st, l_opts.data);
|
||||
err = l_opts.pre_hash_fn(file_path, st, l_opts.data);
|
||||
if (err == ED2KUTIL_DONTHASH)
|
||||
return NOERR;
|
||||
else if (err != NOERR)
|
||||
@ -28,24 +32,52 @@ static enum error ed2k_util_hash(const char *file_path, blksize_t blksize,
|
||||
|
||||
f = fopen(file_path, "rb");
|
||||
if (!f) {
|
||||
uio_error("Failed to open file: %s (%s)", file_path, strerror(errno));
|
||||
en = errno;
|
||||
|
||||
uio_error("Failed to open file: %s (%s)", file_path, strerror(en));
|
||||
if (en == EINTR && should_exit)
|
||||
return ERR_SHOULD_EXIT;
|
||||
else
|
||||
return ERR_ED2KUTIL_FS;
|
||||
}
|
||||
|
||||
ed2k_init(&ed2k);
|
||||
read_len = fread(buf, 1, sizeof(buf), f);
|
||||
while (read_len > 0) {
|
||||
/* From my test, fread wont return anything special on signal interrupt */
|
||||
while (read_len > 0 && !should_exit) {
|
||||
ed2k_update(&ed2k, buf, read_len);
|
||||
read_len = fread(buf, 1, sizeof(buf), f);
|
||||
}
|
||||
// TODO check if eof or error
|
||||
if (should_exit) {
|
||||
err = ERR_SHOULD_EXIT;
|
||||
goto fail;
|
||||
}
|
||||
if (ferror(f)) { /* Loop stopped bcuz of error, not EOF */
|
||||
uio_error("Failure while reading file");
|
||||
err = ERR_ED2KUTIL_FS;
|
||||
goto fail;
|
||||
}
|
||||
assert(feof(f));
|
||||
|
||||
ed2k_final(&ed2k, hash);
|
||||
fclose(f);
|
||||
if (fclose(f) != 0) {
|
||||
en = errno;
|
||||
|
||||
uio_debug("Fclose failed: %s", strerror(en));
|
||||
if (en == EINTR && should_exit)
|
||||
return ERR_SHOULD_EXIT;
|
||||
else
|
||||
return ERR_ED2KUTIL_FS;
|
||||
}
|
||||
|
||||
if (l_opts.post_hash_fn)
|
||||
return l_opts.post_hash_fn(file_path, hash, st, l_opts.data);
|
||||
return NOERR;
|
||||
|
||||
fail:
|
||||
if (f) /* We can't get a 2nd interrupt now */
|
||||
fclose(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ed2k_util_walk(const char *fpath, const struct stat *sb,
|
||||
|
12
src/error.h
12
src/error.h
@ -35,6 +35,16 @@
|
||||
E(ERR_API_LOGOUT) /* Logout failed */ \
|
||||
E(ERR_API_PRINTFFUNC) /* New printf function registration failed */ \
|
||||
E(ERR_API_CLOCK) /* Some error with clocks */ \
|
||||
E(ERR_API_INVCOMM) /* Invalid command or command arguments */ \
|
||||
E(ERR_API_BANNED) /* Got banned */ \
|
||||
E(ERR_API_CMD_UNK) /* Unknown command */ \
|
||||
E(ERR_API_INT_SRV) /* Internal server error */ \
|
||||
E(ERR_API_OOS) /* AniDB is out of service rn */ \
|
||||
E(ERR_API_SRV_BUSY) /* Server is too busy, try again later */ \
|
||||
E(ERR_API_TIMEOUT) /* Timed out, delay and resubmit */ \
|
||||
E(ERR_API_NOLOGIN) /* Login is required for this command */ \
|
||||
E(ERR_API_AXX_DENIED) /* Access is denied */ \
|
||||
E(ERR_API_INV_SESSION) /* Session is invalid */ \
|
||||
\
|
||||
E(ERR_CACHE_SQLITE) /* Generic sqlite error code */ \
|
||||
E(ERR_CACHE_EXISTS) /* Entry already exists, as determined by lid */ \
|
||||
@ -45,6 +55,8 @@
|
||||
E(ERR_THRD) /* Generic pthread error */ \
|
||||
\
|
||||
E(ERR_LIBEVENT) /* There are some problem with a libevent function */ \
|
||||
\
|
||||
E(ERR_SHOULD_EXIT) /* Probably got a C-c, program should exit now */ \
|
||||
E(_ERR_COUNT) \
|
||||
|
||||
|
||||
|
7
src/globals.h
Normal file
7
src/globals.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef _GLOBALS_H
|
||||
#define _GLOBALS_H
|
||||
#include <stdbool.h>
|
||||
|
||||
extern bool should_exit;
|
||||
|
||||
#endif /* _GLOBALS_H */
|
12
src/net.c
12
src/net.c
@ -248,7 +248,11 @@ ssize_t net_send(const void *msg, size_t msg_len)
|
||||
{
|
||||
ssize_t w_len = send(net_socket, msg, msg_len, 0);
|
||||
if (w_len == -1) {
|
||||
uio_error("{net} Send failed: %s", strerror(errno));
|
||||
int en = errno;
|
||||
|
||||
uio_error("{net} Send failed: %s", strerror(en));
|
||||
if (en == EINTR)
|
||||
return -2;
|
||||
return -1;
|
||||
}
|
||||
return w_len;
|
||||
@ -258,8 +262,14 @@ ssize_t net_read(void* out_data, size_t read_size)
|
||||
{
|
||||
ssize_t read = recv(net_socket, out_data, read_size, 0);
|
||||
if (read == -1) {
|
||||
int en = errno;
|
||||
|
||||
uio_error("{net} Read failed: %s", strerror(errno));
|
||||
if (en == EINTR)
|
||||
return -2;
|
||||
return -1;
|
||||
}
|
||||
if (read == read_size)
|
||||
uio_warning("{net} Data may have been discarded!");
|
||||
return read;
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ enum error net_init();
|
||||
|
||||
/*
|
||||
* Send and read data to and from the api
|
||||
* Returns the number of bytes sent/read or -1 on error
|
||||
* If the error is EINTR, it returns -2
|
||||
*/
|
||||
ssize_t net_send(const void *msg, size_t msg_len);
|
||||
ssize_t net_read(void *out_data, size_t read_size);
|
||||
|
10
src/util.h
10
src/util.h
@ -4,6 +4,16 @@
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MS_TO_TIMESPEC(ts, ms) { \
|
||||
ts->tv_sec = ms / 1000; \
|
||||
ts->tv_nsec = (ms % 1000) * 1000000; \
|
||||
}
|
||||
|
||||
#define MS_TO_TIMESPEC_L(ts, ms) { \
|
||||
ts.tv_sec = ms / 1000; \
|
||||
ts.tv_nsec = (ms % 1000) * 1000000; \
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert bytes to a hex string
|
||||
* out needs to be at least (bytes_len * 2 + 1) bytes
|
||||
|
Loading…
Reference in New Issue
Block a user