diff --git a/src/main.c b/src/main.c index daaadb0a..f423df38 100644 --- a/src/main.c +++ b/src/main.c @@ -49,6 +49,7 @@ static int _tf_command_import(const char* file, int argc, char* argv[]); static int _tf_command_publish(const char* file, int argc, char* argv[]); static int _tf_command_run(const char* file, int argc, char* argv[]); static int _tf_command_sandbox(const char* file, int argc, char* argv[]); +static int _tf_command_store_blob(const char* file, int argc, char* argv[]); static int _tf_command_test(const char* file, int argc, char* argv[]); static int _tf_command_verify(const char* file, int argc, char* argv[]); static int _tf_command_usage(const char* file); @@ -66,6 +67,7 @@ const command_t k_commands[] = { { "import", _tf_command_import, "Import apps to SSB." }, { "export", _tf_command_export, "Export apps from SSB." }, { "publish", _tf_command_publish, "Append a message to a feed." }, + { "store_blob", _tf_command_store_blob, "Write a file to the blob store." }, { "verify", _tf_command_verify, "Verify a feed." }, { "test", _tf_command_test, "Test SSB." }, }; @@ -387,6 +389,96 @@ static int _tf_command_publish(const char* file, int argc, char* argv[]) 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* file_path = NULL; + bool show_usage = false; + + while (!show_usage) + { + static const struct option k_options[] = { + { "db-path", required_argument, NULL, 'd' }, + { "file", required_argument, NULL, 'f' }, + { "help", no_argument, NULL, 'h' }, + { 0 }, + }; + int c = getopt_long(argc, argv, "d:f:h", k_options, NULL); + if (c == -1) + { + break; + } + + switch (c) + { + case '?': + case 'h': + default: + show_usage = true; + break; + case 'd': + db_path = optarg; + break; + case 'f': + file_path = optarg; + break; + } + } + + if (show_usage || !file_path) + { + 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(" -f, --file file_path Path to file to add to the blob store.\n"); + tf_printf(" -h, --help Show this usage information.\n"); + return EXIT_FAILURE; + } + + char* data = NULL; + size_t size = 0; + FILE* blob_file = fopen(file_path, "rb"); + if (!blob_file) + { + tf_printf("Failed to open %s: %s.\n", file_path, strerror(errno)); + return EXIT_FAILURE; + } + + char buffer[16 * 1024]; + while (true) + { + size_t bytes = fread(buffer, 1, sizeof(buffer), blob_file); + if (bytes > 0) + { + data = tf_resize_vec(data, size + bytes); + memcpy(data + size, buffer, bytes); + size += bytes; + } + else + { + break; + } + } + if (ferror(blob_file)) + { + tf_printf("Failed to read %s: %s.\n", file_path, strerror(errno)); + fclose(blob_file); + tf_free(data); + return EXIT_FAILURE; + } + fclose(blob_file); + + char id[256]; + 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)) + { + tf_printf("%s\n", id); + } + tf_ssb_destroy(ssb); + tf_free(data); + return EXIT_SUCCESS; +} + static int _tf_command_verify(const char* file, int argc, char* argv[]) { const char* identity = NULL;