diff --git a/README.md b/README.md index d4dea1c..b1ef8e1 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/api.h b/src/api.h index 0519893..9d43a9b 100644 --- a/src/api.h +++ b/src/api.h @@ -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); diff --git a/src/cache.c b/src/cache.c index 326cf94..f49f495 100644 --- a/src/cache.c +++ b/src/cache.c @@ -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; +} diff --git a/src/cache.h b/src/cache.h index 0e2c5de..8fdb84a 100644 --- a/src/cache.h +++ b/src/cache.h @@ -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 */ diff --git a/src/cmd_add.c b/src/cmd_add.c index ac73af4..4121c0e 100644 --- a/src/cmd_add.c +++ b/src/cmd_add.c @@ -2,6 +2,7 @@ #include #include #include +#include #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; } diff --git a/src/cmd_modify.c b/src/cmd_modify.c index c89a040..3d0a5db 100644 --- a/src/cmd_modify.c +++ b/src/cmd_modify.c @@ -1,17 +1,42 @@ -#include #include #include #include +#include #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; } diff --git a/src/config.c b/src/config.c index 19882d6..ec7cf24 100644 --- a/src/config.c +++ b/src/config.c @@ -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 '|'. 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 }, diff --git a/src/util.c b/src/util.c index dd4a1ae..5904aac 100644 --- a/src/util.c +++ b/src/util.c @@ -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"); diff --git a/src/util.h b/src/util.h index c0a926f..bfd5fe8 100644 --- a/src/util.h +++ b/src/util.h @@ -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 */