diff --git a/src/main.c b/src/main.c index f423df38..bee69ff0 100644 --- a/src/main.c +++ b/src/main.c @@ -25,6 +25,7 @@ #if defined(__linux__) #include +#include #endif #if defined(__APPLE__) @@ -41,7 +42,102 @@ struct backtrace_state* g_backtrace_state; -const char* k_db_path_default = "db.sqlite"; +#if !defined(TARGET_OS_IPHONE) +static const char* _get_db_path() +{ + const char* k_db_path_default = "db.sqlite"; +#if defined(__linux__) + if (stat(k_db_path_default, &(struct stat) { 0 }) == 0) + { + return tf_strdup(k_db_path_default); + } + else + { + char buffer[32]; + + char* data_home = NULL; + size_t size = sizeof(buffer); + int r = uv_os_getenv("XDG_DATA_HOME", buffer, &size); + if (r == 0 || r == UV_ENOBUFS) + { + size++; + data_home = alloca(size); + if (uv_os_getenv("XDG_DATA_HOME", data_home, &size) != 0) + { + data_home = NULL; + } + } + + if (!data_home) + { + size = sizeof(buffer); + r = uv_os_getenv("HOME", buffer, &size); + if (r == 0 || r == UV_ENOBUFS) + { + size++; + char* home = alloca(size); + r = uv_os_getenv("HOME", home, &size); + if (r == 0) + { + size = snprintf(NULL, 0, "%s/.local/share", home) + 1; + data_home = alloca(size); + snprintf(data_home, size, "%s/.local/share", home); + } + } + } + + if (data_home) + { + size = snprintf(NULL, 0, "%s/tildefriends/db.sqlite", data_home) + 1; + char* path = alloca(size); + snprintf(path, size, "%s/tildefriends/db.sqlite", data_home); + return tf_strdup(path); + } + } +#endif + return tf_strdup(k_db_path_default); +} + +static void _create_directories_for_file(const char* path, int mode) +{ + if (stat(path, &(struct stat) { 0 }) == 0) + { + /* It already exists. OK. */ + return; + } + + size_t length = strlen(path) + 1; + char* copy = alloca(length); + memcpy(copy, path, length); +#if defined(_WIN32) + for (char* c = copy; *c; c++) + { + if (*c == '\\') + { + *c = '/'; + } + } +#endif + char* slash = copy; + while (slash) + { + slash = strchr(slash + 1, '/'); + if (slash) + { + *slash = '\0'; +#if defined(_WIN32) + if (mkdir(copy) == 0) +#else + if (mkdir(copy, mode) == 0) +#endif + { + tf_printf("Created directory %s.\n", copy); + } + *slash = '/'; + } + } +} +#endif #if !TARGET_OS_IPHONE && !defined(__ANDROID__) static int _tf_command_export(const char* file, int argc, char* argv[]); @@ -131,7 +227,8 @@ static int _tf_command_test(const char* file, int argc, char* argv[]) static int _tf_command_import(const char* file, int argc, char* argv[]) { const char* user = "import"; - const char* db_path = k_db_path_default; + const char* default_db_path = _get_db_path(); + const char* db_path = default_db_path; bool show_usage = false; while (!show_usage) @@ -169,11 +266,13 @@ static int _tf_command_import(const char* file, int argc, char* argv[]) tf_printf("\n%s import [options] [paths...]\n\n", file); tf_printf("options:\n"); tf_printf(" -u, --user user User into whose account apps will be imported (default: \"import\").\n"); - tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); + tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path); tf_printf(" -h, --help Show this usage information.\n"); + tf_free((void*)default_db_path); return EXIT_FAILURE; } + _create_directories_for_file(db_path, 0700); tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); if (optind < argc) { @@ -189,13 +288,15 @@ static int _tf_command_import(const char* file, int argc, char* argv[]) tf_ssb_import(ssb, user, "apps"); } tf_ssb_destroy(ssb); + tf_free((void*)default_db_path); return EXIT_SUCCESS; } static int _tf_command_export(const char* file, int argc, char* argv[]) { const char* user = "core"; - const char* db_path = k_db_path_default; + const char* default_db_path = _get_db_path(); + const char* db_path = default_db_path; bool show_usage = false; while (!show_usage) @@ -233,10 +334,11 @@ static int _tf_command_export(const char* file, int argc, char* argv[]) tf_printf("\n%s export [options] [paths...]\n\n", file); tf_printf("options:\n"); tf_printf(" -u, --user user User from whose account apps will be exported (default: \"core\").\n"); - tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); + tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path); tf_printf(" -h, --help Show this usage information.\n"); tf_printf("\n"); tf_printf("paths Paths of apps to export (example: /~core/ssb /~user/app).\n"); + tf_free((void*)default_db_path); return EXIT_FAILURE; } @@ -271,6 +373,7 @@ static int _tf_command_export(const char* file, int argc, char* argv[]) } } tf_ssb_destroy(ssb); + tf_free((void*)default_db_path); return EXIT_SUCCESS; } @@ -298,7 +401,8 @@ static int _tf_command_publish(const char* file, int argc, char* argv[]) { const char* user = NULL; const char* identity = NULL; - const char* db_path = k_db_path_default; + const char* default_db_path = _get_db_path(); + const char* db_path = default_db_path; const char* content = NULL; bool show_usage = false; @@ -346,14 +450,16 @@ static int _tf_command_publish(const char* file, int argc, char* argv[]) tf_printf("options:\n"); tf_printf(" -u, --user user User owning identity with which to publish.\n"); tf_printf(" -i, --id identity Identity with which to publish message.\n"); - tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); + tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path); tf_printf(" -c, --content json JSON content of message to publish.\n"); tf_printf(" -h, --help Show this usage information.\n"); + tf_free((void*)default_db_path); return EXIT_FAILURE; } int result = EXIT_FAILURE; tf_printf("Posting %s as account %s belonging to %s...\n", content, identity, user); + _create_directories_for_file(db_path, 0700); tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); uint8_t private_key[512] = { 0 }; if (tf_ssb_db_identity_get_private_key(ssb, user, identity, private_key, sizeof(private_key))) @@ -386,12 +492,14 @@ static int _tf_command_publish(const char* file, int argc, char* argv[]) tf_printf("Did not find private key for identity %s belonging to %s.\n", identity, user); } tf_ssb_destroy(ssb); + tf_free((void*)default_db_path); return result; } static int _tf_command_store_blob(const char* file, int argc, char* argv[]) { - const char* db_path = k_db_path_default; + const char* default_db_path = _get_db_path(); + const char* db_path = default_db_path; const char* file_path = NULL; bool show_usage = false; @@ -429,9 +537,10 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[]) { tf_printf("\n%s store_blob [options]\n\n", file); tf_printf("options:\n"); - tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); + tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path); tf_printf(" -f, --file file_path Path to file to add to the blob store.\n"); tf_printf(" -h, --help Show this usage information.\n"); + tf_free((void*)default_db_path); return EXIT_FAILURE; } @@ -441,6 +550,7 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[]) if (!blob_file) { tf_printf("Failed to open %s: %s.\n", file_path, strerror(errno)); + tf_free((void*)default_db_path); return EXIT_FAILURE; } @@ -464,11 +574,13 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[]) tf_printf("Failed to read %s: %s.\n", file_path, strerror(errno)); fclose(blob_file); tf_free(data); + tf_free((void*)default_db_path); return EXIT_FAILURE; } fclose(blob_file); char id[256]; + _create_directories_for_file(db_path, 0700); tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); if (tf_ssb_db_blob_store(ssb, (const uint8_t*)data, size, id, sizeof(id), NULL)) { @@ -476,13 +588,15 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[]) } tf_ssb_destroy(ssb); tf_free(data); + tf_free((void*)default_db_path); return EXIT_SUCCESS; } static int _tf_command_verify(const char* file, int argc, char* argv[]) { const char* identity = NULL; - const char* db_path = k_db_path_default; + const char* default_db_path = _get_db_path(); + const char* db_path = default_db_path; bool show_usage = false; while (!show_usage) @@ -520,8 +634,9 @@ static int _tf_command_verify(const char* file, int argc, char* argv[]) tf_printf("\n%s import [options] [paths...]\n\n", file); tf_printf("options:\n"); tf_printf(" -i, --identity identity Identity to verify.\n"); - tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); + tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path); tf_printf(" -h, --help Show this usage information.\n"); + tf_free((void*)default_db_path); return EXIT_FAILURE; } @@ -529,6 +644,7 @@ static int _tf_command_verify(const char* file, int argc, char* argv[]) tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); bool verified = tf_ssb_db_verify(ssb, identity); tf_ssb_destroy(ssb); + tf_free((void*)default_db_path); return verified ? EXIT_SUCCESS : EXIT_FAILURE; } #endif @@ -673,13 +789,14 @@ static void _shed_privileges() static int _tf_command_run(const char* file, int argc, char* argv[]) { + const char* default_db_path = _get_db_path(); tf_run_args_t args = { .count = 1, .script = "core/core.js", .http_port = 12345, .https_port = 12346, .ssb_port = 8008, - .db_path = k_db_path_default, + .db_path = default_db_path, }; bool show_usage = false; @@ -764,7 +881,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) tf_printf(" -b, --ssb-port port Port on which to run SSB (default: 8008, 0 disables).\n"); tf_printf(" -p, --http-port port Port on which to run Tilde Friends web server (default: 12345).\n"); tf_printf(" -q, --https-port port Port on which to run secure Tilde Friends web server (default: 12346).\n"); - tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", k_db_path_default); + tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", default_db_path); tf_printf(" -k, --ssb-network-key key SSB network key to use.\n"); tf_printf(" -n, --count count Number of instances to run.\n"); tf_printf(" -a, --args args Arguments of the format key=value,foo=bar,verbose=true.\n"); @@ -772,6 +889,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) tf_printf(" -z, --zip path Zip archive from which to load files.\n"); tf_printf(" -v, --verbose Log raw messages.\n"); tf_printf(" -h, --help Show this usage information.\n"); + tf_free((void*)default_db_path); return EXIT_FAILURE; } @@ -780,6 +898,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) setpgid(0, 0); #endif + _create_directories_for_file(args.db_path, 0700); if (args.count == 1) { result = _tf_run_task(&args, 0); @@ -807,6 +926,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) tf_free(data); tf_free(threads); } + tf_free((void*)default_db_path); return result; } @@ -1088,7 +1208,7 @@ void tf_run_thread_start(const char* zip_path) .http_port = 12345, .https_port = 12346, .ssb_port = 8008, - .db_path = k_db_path_default, + .db_path = "db.sqlite", .one_proc = true, .zip = zip_path, };