@@ -37,4 +37,7 @@ make | |||
- Handle C-c gracefully at any time | |||
- Rework cmd line args | |||
- Should be multiple 'menus' like `caniadd add [paths...]`, `caniadd uptime`, `caniadd watched`... | |||
- After some time passes between now and cache moddate and it's not watched, then query and update it | |||
- Update cache entry is modify is used | |||
- Pretty hashing with color and progress bars and the other fancy stuff | |||
- Write -h page, and maybe a man page too |
@@ -250,9 +250,9 @@ struct api_mylistmod_result { | |||
struct api_result { | |||
uint16_t code; | |||
union { | |||
struct api_version_result version; | |||
struct api_auth_result auth; | |||
struct api_uptime_result uptime; | |||
e(version); | |||
e(auth); | |||
e(uptime); | |||
e(mylistadd); | |||
e(encrypt); | |||
e(mylistmod); | |||
@@ -8,6 +8,8 @@ | |||
#include "ed2k.h" | |||
#include "util.h" | |||
static bool cache_did_init = false; | |||
#define sqlite_bind_goto(smt, name, type, ...) { \ | |||
int sb_idx = sqlite3_bind_parameter_index(smt, name); \ | |||
if (sb_idx == 0) { \ | |||
@@ -30,11 +32,13 @@ static const char sql_create_table[] = "CREATE TABLE IF NOT EXISTS mylist (" | |||
"fname TEXT NOT NULL," | |||
"fsize INTEGER NOT NULL," | |||
"ed2k TEXT NOT NULL," | |||
"watchdate INTEGER," | |||
"state INTEGER NOT NULL," | |||
"moddate INTEGER NOT NULL DEFAULT (strftime('%s'))," | |||
"UNIQUE (fname, fsize) )"; | |||
static const char sql_mylist_add[] = "INSERT INTO mylist " | |||
"(lid, fname, fsize, ed2k) VALUES " | |||
//"(?, ?, ?, ?)"; | |||
"(:lid, :fname, :fsize, :ed2k)"; | |||
"(lid, fname, fsize, ed2k, watchdate, state) VALUES " | |||
"(:lid, :fname, :fsize, :ed2k, :watchdate, :state)"; | |||
static const char sql_mylist_get[] = "SELECT * FROM mylist WHERE " | |||
"fsize=:fsize AND fname=:fname"; | |||
@@ -119,6 +123,7 @@ enum error cache_init() | |||
if (err != NOERR) | |||
goto fail; | |||
cache_did_init = true; | |||
return NOERR; | |||
fail: | |||
@@ -126,14 +131,21 @@ fail: | |||
return err; | |||
} | |||
bool cache_is_init() | |||
{ | |||
return cache_did_init; | |||
} | |||
void cache_free() | |||
{ | |||
cache_did_init = false; | |||
sqlite3_close(cache_db); | |||
uio_debug("Closed cache db"); | |||
} | |||
enum error cache_add(uint64_t lid, const char *fname, | |||
uint64_t fsize, const uint8_t *ed2k) | |||
uint64_t fsize, const uint8_t *ed2k, uint64_t watchdate, | |||
enum mylist_state state) | |||
{ | |||
char ed2k_str[ED2K_HASH_SIZE * 2 + 1]; | |||
sqlite3_stmt *smt; | |||
@@ -153,6 +165,8 @@ enum error cache_add(uint64_t lid, const char *fname, | |||
sqlite_bind_goto(smt, ":fname", text, fname, -1, SQLITE_STATIC); | |||
sqlite_bind_goto(smt, ":fsize", int64, fsize); | |||
sqlite_bind_goto(smt, ":ed2k", text, ed2k_str, -1, SQLITE_STATIC); | |||
sqlite_bind_goto(smt, ":watchdate", int64, watchdate); | |||
sqlite_bind_goto(smt, ":state", int64, state); | |||
sret = sqlite3_step(smt); | |||
if (sret != SQLITE_DONE) { | |||
@@ -174,7 +188,7 @@ fail: | |||
} | |||
enum error cache_get(const char *fname, uint64_t fsize, | |||
enum error cache_get(const char *fname, uint64_t fsize, enum cache_select sel, | |||
struct cache_entry *out_ce) | |||
{ | |||
sqlite3_stmt *smt; | |||
@@ -195,14 +209,45 @@ enum error cache_get(const char *fname, uint64_t fsize, | |||
if (sret == SQLITE_DONE) { | |||
uio_debug("Cache entry with size (%lu) and name (%s) not found", fsize, fname); | |||
err = ERR_CACHE_NO_EXISTS; | |||
goto fail; | |||
} else if (sret == SQLITE_ROW) { | |||
uio_debug("Found Cache entry with size (%lu) and name (%s)", fsize, fname); | |||
} else { | |||
uio_error("sqlite_step failed: %s", sqlite3_errmsg(cache_db)); | |||
err = ERR_CACHE_SQLITE; | |||
goto fail; | |||
} | |||
if (!out_ce) | |||
goto end; | |||
if (sel == 0) | |||
sel = 0xFFFFFFFF; | |||
if (sel & CACHE_S_LID) | |||
out_ce->lid = sqlite3_column_int64(smt, 0); | |||
if (sel & CACHE_S_FNAME) | |||
out_ce->fname = strdup((const char *)sqlite3_column_text(smt, 1)); | |||
if (sel & CACHE_S_FSIZE) | |||
out_ce->fsize = sqlite3_column_int64(smt, 2); | |||
if (sel & CACHE_S_ED2K) { | |||
const char *txt = (const char *)sqlite3_column_text(smt, 3); | |||
util_hex2byte(txt, out_ce->ed2k); | |||
} | |||
if (sel & CACHE_S_WATCHDATE) | |||
out_ce->wdate = sqlite3_column_int64(smt, 4); | |||
if (sel & CACHE_S_STATE) | |||
out_ce->state = sqlite3_column_int(smt, 5); | |||
if (sel & CACHE_S_MODDATE) | |||
out_ce->moddate = sqlite3_column_int64(smt, 6); | |||
end: | |||
fail: | |||
sqlite3_finalize(smt); | |||
return err; | |||
} | |||
bool cache_exists(const char *fname, uint64_t size) | |||
{ | |||
return cache_get(fname, size, 0, NULL) == NOERR; | |||
} |
@@ -5,11 +5,27 @@ | |||
#include "error.h" | |||
#include "ed2k.h" | |||
#include "api.h" | |||
enum cache_select { | |||
CACHE_S_ALL = 0, | |||
CACHE_S_LID = 1 << 0, | |||
CACHE_S_FNAME = 1 << 1, | |||
CACHE_S_FSIZE = 1 << 2, | |||
CACHE_S_ED2K = 1 << 3, | |||
CACHE_S_WATCHED = 1 << 4, | |||
CACHE_S_WATCHDATE = 1 << 5, | |||
CACHE_S_STATE = 1 << 6, | |||
CACHE_S_MODDATE = 1 << 7, | |||
}; | |||
struct cache_entry { | |||
uint64_t lid, fsize; | |||
uint64_t lid, fsize, wdate, moddate; | |||
/* free() if requested */ | |||
char *fname; | |||
uint8_t ed2k[ED2K_HASH_SIZE]; | |||
bool watched; | |||
uint16_t state; | |||
}; | |||
/* | |||
@@ -18,6 +34,11 @@ struct cache_entry { | |||
enum error cache_init(); | |||
/* | |||
* Is the cache already setup or not? | |||
*/ | |||
bool cache_is_init(); | |||
/* | |||
* Free tha cache | |||
*/ | |||
void cache_free(); | |||
@@ -26,15 +47,30 @@ void cache_free(); | |||
* Add a new mylist entry to the cache | |||
*/ | |||
enum error cache_add(uint64_t lid, const char *fname, | |||
uint64_t fsize, const uint8_t *ed2k); | |||
uint64_t fsize, const uint8_t *ed2k, uint64_t watchdate, | |||
enum mylist_state state); | |||
/* | |||
* Update an already existing cache entry | |||
*/ | |||
enum error cache_update(); | |||
/* | |||
* Get a cache entry | |||
* | |||
* sel is the columns to select | |||
* If 0, everything is selected and returned. | |||
* Can be ORed together | |||
* | |||
* out_ce can be NULL. Useful, if we only want | |||
* to check if the entry exists or not. | |||
*/ | |||
enum error cache_get(const char *fname, uint64_t size, | |||
enum error cache_get(const char *fname, uint64_t fsize, enum cache_select sel, | |||
struct cache_entry *out_ce); | |||
/* | |||
* Does an entry exists? | |||
*/ | |||
bool cache_exists(const char *fname, uint64_t size); | |||
#endif /* _CACHE_H */ |
@@ -2,6 +2,7 @@ | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <stdbool.h> | |||
#include <time.h> | |||
#include "cmd.h" | |||
#include "error.h" | |||
@@ -18,7 +19,7 @@ enum error cmd_add_cachecheck(const char *path, const struct stat *st, | |||
const char *bname = util_basename(path); | |||
enum error err; | |||
err = cache_get(bname, st->st_size, NULL); | |||
err = cache_get(bname, st->st_size, 0, NULL); | |||
if (err == NOERR) { | |||
/* We could get the entry, so it exists already */ | |||
uio_user("This file (%s) with size (%lu) already exists in cache." | |||
@@ -54,7 +55,8 @@ enum error cmd_add_apisend(const char *path, const uint8_t *hash, | |||
x->lid, x->fid, x->eid, x->aid, x->gid, x->date, x->viewdate, | |||
x->state, x->filestate, x->storage, x->source, x->other); | |||
cache_add(x->lid, util_basename(path), st->st_size, hash); | |||
cache_add(x->lid, util_basename(path), st->st_size, hash, x->viewdate, | |||
x->state); | |||
if (x->storage) | |||
free(x->storage); | |||
@@ -71,7 +73,17 @@ enum error cmd_add_apisend(const char *path, const uint8_t *hash, | |||
uio_user("Succesfully added!"); | |||
uio_debug("New mylist id is: %ld", r.mylistadd.new_id); | |||
cache_add(r.mylistadd.new_id, util_basename(path), st->st_size, hash); | |||
uint64_t wdate = 0; | |||
if (mopt->watched_set && mopt->watched) { | |||
if (mopt->wdate_set) | |||
wdate = mopt->wdate; | |||
else | |||
wdate = time(NULL); | |||
} | |||
cache_add(r.mylistadd.new_id, util_basename(path), st->st_size, hash, | |||
wdate, mopt->state_set ? mopt->state : MYLIST_STATE_INTERNAL); | |||
return NOERR; | |||
} | |||
@@ -1,17 +1,42 @@ | |||
#include <sys/stat.h> | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <stdbool.h> | |||
#include <string.h> | |||
#include "cmd.h" | |||
#include "error.h" | |||
#include "uio.h" | |||
#include "api.h" | |||
#include "config.h" | |||
#include "ed2k_util.h" | |||
#include "cache.h" | |||
#include "util.h" | |||
bool did_cache_init = false; | |||
uint64_t cmd_modify_getlid(const char *str) | |||
{ | |||
uint64_t val; | |||
const char *sep = strchr(str, '|'); | |||
struct cache_entry ce; | |||
enum error err; | |||
if (sscanf(str, "%lu", &val) != 1) | |||
return 0; | |||
if (!sep) | |||
return val; | |||
if (!cache_is_init()) { | |||
if (cache_init() != NOERR) | |||
return 0; | |||
did_cache_init = true; | |||
} | |||
err = cache_get(sep + 1, val, CACHE_S_LID, &ce); | |||
if (err != NOERR) | |||
return 0; | |||
return ce.lid; | |||
} | |||
enum error cmd_modify(void *data) | |||
{ | |||
struct api_mylistadd_opts mopt = {0}; | |||
@@ -44,11 +69,11 @@ enum error cmd_modify(void *data) | |||
for (int i = 0; i < fcount; i++) { | |||
struct api_result res; | |||
uint64_t lid; | |||
const char *arg = config_get_nonopt(i); | |||
uint64_t lid = cmd_modify_getlid(arg); | |||
if (sscanf(arg, "%lu", &lid) != 1) { | |||
uio_error("Argument '%s' is not an integer. Skipping", arg); | |||
if (lid == 0) { | |||
uio_error("Argument '%s' is not valid. Skipping", arg); | |||
continue; | |||
} | |||
@@ -60,6 +85,11 @@ enum error cmd_modify(void *data) | |||
uio_error("No mylist entry with id: '%lu'", lid); | |||
} | |||
} | |||
if (did_cache_init) { | |||
did_cache_init = false; | |||
cache_free(); | |||
} | |||
return err; | |||
} | |||
@@ -136,6 +136,9 @@ static struct conf_entry options[] = { | |||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true, | |||
.type = OTYPE_B, .handle_order = 1 }, | |||
/* Arguments are either mylist id's, or file sizes and names | |||
* in the format '<size>|<filename>'. The filename can contain | |||
* '|' characters */ | |||
{ .l_name = "modify", .s_name = 'W', | |||
.has_arg = no_argument, .set_func = config_set_bool, .in_args = true, | |||
.type = OTYPE_B, .handle_order = 1 }, | |||
@@ -16,6 +16,30 @@ void util_byte2hex(const uint8_t* bytes, size_t bytes_len, | |||
*out = '\0'; | |||
} | |||
void util_hex2byte(const char *str, uint8_t* out_bytes) | |||
{ | |||
while (*str) { | |||
if (*str >= '0' && *str <= '9') | |||
*out_bytes = (*str - '0') << 4; | |||
if (*str >= 'A' && *str <= 'F') | |||
*out_bytes = (*str - ('A' - 10)) << 4; | |||
else | |||
*out_bytes = (*str - ('a' - 10)) << 4; | |||
str++; | |||
if (*str >= '0' && *str <= '9') | |||
*out_bytes |= (*str - '0'); | |||
if (*str >= 'A' && *str <= 'F') | |||
*out_bytes |= (*str - ('A' - 10)); | |||
else | |||
*out_bytes |= (*str - ('a' - 10)); | |||
out_bytes++; | |||
str++; | |||
} | |||
} | |||
const char *util_get_home() | |||
{ | |||
const char *home_env = getenv("HOME"); | |||
@@ -21,6 +21,8 @@ | |||
void util_byte2hex(const uint8_t* bytes, size_t bytes_len, | |||
bool uppercase, char* out); | |||
/* And convert it back */ | |||
void util_hex2byte(const char *str, uint8_t* out_bytes); | |||
/* | |||
* Return the user's home directory | |||
*/ | |||