diff --git a/src/Makefile.am b/src/Makefile.am index af85edd..886e72e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES = libstorj.la -libstorj_la_SOURCES = storj.c utils.c utils.h http.c http.h uploader.c uploader.h downloader.c downloader.h bip39.c bip39.h bip39_english.h crypto.c crypto.h rs.c rs.h +libstorj_la_SOURCES = storj.c utils.c utils.h http.c http.h uploader.c uploader.h downloader.c downloader.h bip39.c bip39.h bip39_english.h crypto.c crypto.h rs.c rs.h cli_callback.c cli_callback.h libstorj_la_LIBADD = -lcurl -lnettle -ljson-c -luv -lm # The rules of thumb, when dealing with these values are: # - Always increase the revision value. diff --git a/src/cli.c b/src/cli.c index 3e29ce7..50a61b4 100644 --- a/src/cli.c +++ b/src/cli.c @@ -15,6 +15,9 @@ #endif #include "storj.h" +#include "cli_callback.c" + +//#define debug_enable #define STORJ_THREADPOOL_SIZE "64" @@ -30,27 +33,45 @@ typedef struct { extern int errno; #endif -static inline void noop() {}; #define HELP_TEXT "usage: storj [] []\n\n" \ "These are common Storj commands for various situations:\n\n" \ - "setting up users profiles\n" \ + "setting up users profiles:\n" \ " register setup a new storj bridge user\n" \ " import-keys import existing user\n" \ " export-keys export bridge user, password and " \ "encryption keys\n\n" \ - "working with buckets and files\n" \ + "working with buckets and files:\n" \ " list-buckets\n" \ " list-files \n" \ " remove-file \n" \ - " add-bucket \n" \ " remove-bucket \n" \ + " add-bucket \n" \ " list-mirrors \n\n" \ - "downloading and uploading files\n" \ - " upload-file \n" \ - " download-file \n" \ - "bridge api information\n" \ + "uploading files:\n" \ + " upload-file \n\n" \ + "downloading files:\n" \ + " download-file \n\n" \ + "bridge api information:\n" \ " get-info\n\n" \ + "Linux style CLI command support \n" \ + "===============================\n" \ + "working with buckets and files:\n" \ + " ls (lists the available buckets)\n" \ + " ls (lists the files in a bucket)\n" \ + " get-bucket-id \n" \ + " rm (to remove a file from a bucket)\n" \ + " rm (to remove a bucket)\n" \ + " mkbkt \n" \ + " lm \n\n" \ + "uploading files:\n" \ + " cp [-rR] " \ + "storj:///\n" \ + " (e.g. storj cp -[rR] /some-dir/* storj://bucketname/.)\n\n" \ + "downloading files:\n" \ + " cp [-rR] storj:/// " \ + "\n" \ + " (e.g. storj cp -[rR] storj://bucketname/ /some-dir/.)\n\n" \ "options:\n" \ " -h, --help output usage information\n" \ " -v, --version output the version number\n" \ @@ -70,6 +91,102 @@ static inline void noop() {}; #define CLI_VERSION "libstorj-2.0.0-beta2" +static int check_file_path(char *file_path) +{ + struct stat sb; + + if (stat(file_path, &sb) == -1) { + perror("stat"); + return CLI_NO_SUCH_FILE_OR_DIR; + } + + switch (sb.st_mode & S_IFMT) { + case S_IFBLK: + printf("block device\n"); + break; + case S_IFCHR: + printf("character device\n"); + break; + case S_IFDIR: + return CLI_VALID_DIR; + break; + case S_IFIFO: + printf("FIFO/pipe\n"); + break; + case S_IFLNK: + printf("symlink\n"); + break; + case S_IFREG: + return CLI_VALID_REGULAR_FILE; + break; + case S_IFSOCK: + printf("socket\n"); + break; + default: + printf("unknown?\n"); + break; + } + + #if ENABLE_FILE_DETAILS + printf("I-node number: %ld\n", (long)sb.st_ino); + + printf("Mode: %lo (octal)\n", + (unsigned long)sb.st_mode); + + printf("Link count: %ld\n", (long)sb.st_nlink); + printf("Ownership: UID=%ld GID=%ld\n", + (long)sb.st_uid, (long)sb.st_gid); + + printf("Preferred I/O block size: %ld bytes\n", + (long)sb.st_blksize); + printf("File size: %lld bytes\n", + (long long)sb.st_size); + printf("Blocks allocated: %lld\n", + (long long)sb.st_blocks); + + printf("Last status change: %s", ctime(&sb.st_ctime)); + printf("Last file access: %s", ctime(&sb.st_atime)); + printf("Last file modification: %s", ctime(&sb.st_mtime)); + #endif + + return CLI_UNKNOWN_FILE_ATTR; +} + + +static int strpos(char *str, char *sub_str) +{ + /* find first occurance of substring in string */ + char *sub_str_pos=strstr(str,sub_str); + + /* if null return -1 , otherwise return substring address - base address */ + return sub_str_pos == NULL ? -1 : (sub_str_pos - str ); +} + +static int validate_cmd_tokenize(char *cmd_str, char *str_token[]) +{ + char sub_str[] = "storj://"; + int i = 0x00; /* num of tokens */ + + int ret = strpos(cmd_str, sub_str); + if( ret == -1) { + printf("Invalid Command Entry (%d), \ntry ... storj:///\n", ret); + } + + if (ret == 0x00) { + /* start tokenizing */ + str_token[0] = strtok(cmd_str, "/"); + while (str_token[i] != NULL) { + i++; + str_token[i] = strtok(NULL, "/"); + } + } else { + i = ret; + } + + return i; +} + + static void json_logger(const char *message, int level, void *handle) { printf("{\"message\": \"%s\", \"level\": %i, \"timestamp\": %" PRIu64 "}\n", @@ -109,37 +226,6 @@ static int make_user_directory(char *path) return 0; } -static const char *get_filename_separator(const char *file_path) -{ - const char *file_name = NULL; -#ifdef _WIN32 - file_name = strrchr(file_path, '\\'); - if (!file_name) { - file_name = strrchr(file_path, '/'); - } - if (!file_name && file_path) { - file_name = file_path; - } - if (!file_name) { - return NULL; - } - if (file_name[0] == '\\' || file_name[0] == '/') { - file_name++; - } -#else - file_name = strrchr(file_path, '/'); - if (!file_name && file_path) { - file_name = file_path; - } - if (!file_name) { - return NULL; - } - if (file_name[0] == '/') { - file_name++; - } -#endif - return file_name; -} static int get_user_auth_location(char *host, char **root_dir, char **user_file) { @@ -170,24 +256,6 @@ static int get_user_auth_location(char *host, char **root_dir, char **user_file) return 0; } -static void get_input(char *line) -{ - if (fgets(line, BUFSIZ, stdin) == NULL) { - line[0] = '\0'; - } else { - int len = strlen(line); - if (len > 0) { - char *last = strrchr(line, '\n'); - if (last) { - last[0] = '\0'; - } - last = strrchr(line, '\r'); - if (last) { - last[0] = '\0'; - } - } - } -} static int generate_mnemonic(char **mnemonic) { @@ -318,271 +386,6 @@ static int get_password_verify(char *prompt, char *password, int count) } } -void close_signal(uv_handle_t *handle) -{ - ((void)0); -} - -static void file_progress(double progress, - uint64_t downloaded_bytes, - uint64_t total_bytes, - void *handle) -{ - int bar_width = 70; - - if (progress == 0 && downloaded_bytes == 0) { - printf("Preparing File..."); - fflush(stdout); - return; - } - - printf("\r["); - int pos = bar_width * progress; - for (int i = 0; i < bar_width; ++i) { - if (i < pos) { - printf("="); - } else if (i == pos) { - printf(">"); - } else { - printf(" "); - } - } - printf("] %.*f%%", 2, progress * 100); - - fflush(stdout); -} - -static void upload_file_complete(int status, storj_file_meta_t *file, void *handle) -{ - printf("\n"); - if (status != 0) { - printf("Upload failure: %s\n", storj_strerror(status)); - exit(status); - } - - printf("Upload Success! File ID: %s\n", file->id); - - storj_free_uploaded_file_info(file); - - exit(0); -} - -void upload_signal_handler(uv_signal_t *req, int signum) -{ - storj_upload_state_t *state = req->data; - storj_bridge_store_file_cancel(state); - if (uv_signal_stop(req)) { - printf("Unable to stop signal\n"); - } - uv_close((uv_handle_t *)req, close_signal); -} - -static int upload_file(storj_env_t *env, char *bucket_id, const char *file_path) -{ - FILE *fd = fopen(file_path, "r"); - - if (!fd) { - printf("Invalid file path: %s\n", file_path); - } - - const char *file_name = get_filename_separator(file_path); - - if (!file_name) { - file_name = file_path; - } - - // Upload opts env variables: - char *prepare_frame_limit = getenv("STORJ_PREPARE_FRAME_LIMIT"); - char *push_frame_limit = getenv("STORJ_PUSH_FRAME_LIMIT"); - char *push_shard_limit = getenv("STORJ_PUSH_SHARD_LIMIT"); - char *rs = getenv("STORJ_REED_SOLOMON"); - - storj_upload_opts_t upload_opts = { - .prepare_frame_limit = (prepare_frame_limit) ? atoi(prepare_frame_limit) : 1, - .push_frame_limit = (push_frame_limit) ? atoi(push_frame_limit) : 64, - .push_shard_limit = (push_shard_limit) ? atoi(push_shard_limit) : 64, - .rs = (!rs) ? true : (strcmp(rs, "false") == 0) ? false : true, - .bucket_id = bucket_id, - .file_name = file_name, - .fd = fd - }; - - uv_signal_t *sig = malloc(sizeof(uv_signal_t)); - if (!sig) { - return 1; - } - uv_signal_init(env->loop, sig); - uv_signal_start(sig, upload_signal_handler, SIGINT); - - - - storj_progress_cb progress_cb = (storj_progress_cb)noop; - if (env->log_options->level == 0) { - progress_cb = file_progress; - } - - storj_upload_state_t *state = storj_bridge_store_file(env, - &upload_opts, - NULL, - progress_cb, - upload_file_complete); - - if (!state) { - return 1; - } - - sig->data = state; - - return state->error_status; -} - -static void download_file_complete(int status, FILE *fd, void *handle) -{ - printf("\n"); - fclose(fd); - if (status) { - // TODO send to stderr - switch(status) { - case STORJ_FILE_DECRYPTION_ERROR: - printf("Unable to properly decrypt file, please check " \ - "that the correct encryption key was " \ - "imported correctly.\n\n"); - break; - default: - printf("Download failure: %s\n", storj_strerror(status)); - } - - exit(status); - } - printf("Download Success!\n"); - exit(0); -} - -void download_signal_handler(uv_signal_t *req, int signum) -{ - storj_download_state_t *state = req->data; - storj_bridge_resolve_file_cancel(state); - if (uv_signal_stop(req)) { - printf("Unable to stop signal\n"); - } - uv_close((uv_handle_t *)req, close_signal); -} - -static int download_file(storj_env_t *env, char *bucket_id, - char *file_id, char *path) -{ - FILE *fd = NULL; - - if (path) { - char user_input[BUFSIZ]; - memset(user_input, '\0', BUFSIZ); - - if(access(path, F_OK) != -1 ) { - printf("Warning: File already exists at path [%s].\n", path); - while (strcmp(user_input, "y") != 0 && strcmp(user_input, "n") != 0) - { - memset(user_input, '\0', BUFSIZ); - printf("Would you like to overwrite [%s]: [y/n] ", path); - get_input(user_input); - } - - if (strcmp(user_input, "n") == 0) { - printf("\nCanceled overwriting of [%s].\n", path); - return 1; - } - - unlink(path); - } - - fd = fopen(path, "w+"); - } else { - fd = stdout; - } - - if (fd == NULL) { - // TODO send to stderr - printf("Unable to open %s: %s\n", path, strerror(errno)); - return 1; - } - - uv_signal_t *sig = malloc(sizeof(uv_signal_t)); - uv_signal_init(env->loop, sig); - uv_signal_start(sig, download_signal_handler, SIGINT); - - storj_progress_cb progress_cb = (storj_progress_cb)noop; - if (path && env->log_options->level == 0) { - progress_cb = file_progress; - } - - storj_download_state_t *state = storj_bridge_resolve_file(env, bucket_id, - file_id, fd, NULL, - progress_cb, - download_file_complete); - if (!state) { - return 1; - } - sig->data = state; - - return state->error_status; -} - -static void list_mirrors_callback(uv_work_t *work_req, int status) -{ - assert(status == 0); - json_request_t *req = work_req->data; - - if (req->status_code != 200) { - printf("Request failed with status code: %i\n", - req->status_code); - } - - if (req->response == NULL) { - free(req); - free(work_req); - printf("Failed to list mirrors.\n"); - exit(1); - } - - int num_mirrors = json_object_array_length(req->response); - - struct json_object *shard; - struct json_object *established; - struct json_object *available; - struct json_object *item; - struct json_object *hash; - struct json_object *contract; - struct json_object *address; - struct json_object *port; - struct json_object *node_id; - - for (int i = 0; i < num_mirrors; i++) { - shard = json_object_array_get_idx(req->response, i); - json_object_object_get_ex(shard, "established", - &established); - int num_established = - json_object_array_length(established); - for (int j = 0; j < num_established; j++) { - item = json_object_array_get_idx(established, j); - if (j == 0) { - json_object_object_get_ex(item, "shardHash", - &hash); - printf("Shard %i: %s\n", i, json_object_get_string(hash)); - } - json_object_object_get_ex(item, "contract", &contract); - json_object_object_get_ex(contract, "farmer_id", &node_id); - - const char *node_id_str = json_object_get_string(node_id); - printf("\tnodeID: %s\n", node_id_str); - } - printf("\n\n"); - } - - json_object_put(req->response); - free(req->path); - free(req); - free(work_req); -} - static int import_keys(user_options_t *options) { int status = 0; @@ -807,110 +610,6 @@ static void register_callback(uv_work_t *work_req, int status) free(work_req); } -static void list_files_callback(uv_work_t *work_req, int status) -{ - int ret_status = 0; - assert(status == 0); - list_files_request_t *req = work_req->data; - - if (req->status_code == 404) { - printf("Bucket id [%s] does not exist\n", req->bucket_id); - goto cleanup; - } else if (req->status_code == 400) { - printf("Bucket id [%s] is invalid\n", req->bucket_id); - goto cleanup; - } else if (req->status_code == 401) { - printf("Invalid user credentials.\n"); - goto cleanup; - } else if (req->status_code != 200) { - printf("Request failed with status code: %i\n", req->status_code); - } - - if (req->total_files == 0) { - printf("No files for bucket.\n"); - } - - for (int i = 0; i < req->total_files; i++) { - - storj_file_meta_t *file = &req->files[i]; - - printf("ID: %s \tSize: %" PRIu64 " bytes \tDecrypted: %s \tType: %s \tCreated: %s \tName: %s\n", - file->id, - file->size, - file->decrypted ? "true" : "false", - file->mimetype, - file->created, - file->filename); - } - -cleanup: - storj_free_list_files_request(req); - free(work_req); - exit(ret_status); -} - -static void delete_file_callback(uv_work_t *work_req, int status) -{ - assert(status == 0); - json_request_t *req = work_req->data; - - if (req->status_code == 200 || req->status_code == 204) { - printf("File was successfully removed from bucket.\n"); - } else if (req->status_code == 401) { - printf("Invalid user credentials.\n"); - } else { - printf("Failed to remove file from bucket. (%i)\n", req->status_code); - } - - json_object_put(req->response); - free(req->path); - free(req); - free(work_req); -} - -static void delete_bucket_callback(uv_work_t *work_req, int status) -{ - assert(status == 0); - json_request_t *req = work_req->data; - - if (req->status_code == 200 || req->status_code == 204) { - printf("Bucket was successfully removed.\n"); - } else if (req->status_code == 401) { - printf("Invalid user credentials.\n"); - } else { - printf("Failed to destroy bucket. (%i)\n", req->status_code); - } - - json_object_put(req->response); - free(req->path); - free(req); - free(work_req); -} - -static void get_buckets_callback(uv_work_t *work_req, int status) -{ - assert(status == 0); - get_buckets_request_t *req = work_req->data; - - if (req->status_code == 401) { - printf("Invalid user credentials.\n"); - } else if (req->status_code != 200 && req->status_code != 304) { - printf("Request failed with status code: %i\n", req->status_code); - } else if (req->total_buckets == 0) { - printf("No buckets.\n"); - } - - for (int i = 0; i < req->total_buckets; i++) { - storj_bucket_meta_t *bucket = &req->buckets[i]; - printf("ID: %s \tDecrypted: %s \tCreated: %s \tName: %s\n", - bucket->id, bucket->decrypted ? "true" : "false", - bucket->created, bucket->name); - } - - storj_free_get_buckets_request(req); - free(work_req); -} - static void create_bucket_callback(uv_work_t *work_req, int status) { assert(status == 0); @@ -1042,6 +741,7 @@ static int export_keys(char *host) int main(int argc, char **argv) { int status = 0; + char temp_buff[256] = {}; static struct option cmd_options[] = { {"url", required_argument, 0, 'u'}, @@ -1050,6 +750,7 @@ int main(int argc, char **argv) {"log", required_argument, 0, 'l'}, {"debug", no_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, + {"recursive", required_argument, 0, 'r'}, {0, 0, 0, 0} }; @@ -1068,10 +769,11 @@ int main(int argc, char **argv) char *storj_bridge = getenv("STORJ_BRIDGE"); int c; int log_level = 0; + char *local_file_path = NULL; char *proxy = getenv("STORJ_PROXY"); - while ((c = getopt_long_only(argc, argv, "hdl:p:vVu:", + while ((c = getopt_long_only(argc, argv, "hdl:p:vVu:r:R:", cmd_options, &index)) != -1) { switch (c) { case 'u': @@ -1091,10 +793,18 @@ int main(int argc, char **argv) printf(CLI_VERSION "\n\n"); exit(0); break; + case 'R': + case 'r': + local_file_path = optarg; + break; case 'h': printf(HELP_TEXT); exit(0); break; + default: + exit(0); + break; + } } @@ -1167,6 +877,7 @@ int main(int argc, char **argv) char *user = NULL; char *pass = NULL; char *mnemonic = NULL; + cli_api_t *cli_api = NULL; if (strcmp(command, "get-info") == 0) { printf("Storj bridge: %s\n\n", storj_bridge); @@ -1298,7 +1009,6 @@ int main(int argc, char **argv) } else if (file_mnemonic) { free(file_mnemonic); } - } // Third, ask for authentication @@ -1364,6 +1074,30 @@ int main(int argc, char **argv) goto end_program; } + cli_api = malloc(sizeof(cli_api_t)); + + if (!cli_api) { + status = 1; + goto end_program; + } + memset(cli_api, 0x00, sizeof(*cli_api)); + + cli_api->env = env; + + #ifdef debug_enable + printf("command = %s; command_index = %d\n", command, command_index); + printf("local_file_path (req arg_ = %s\n", local_file_path); + for (int i = 0x00; i < argc; i++) + { + printf("argc = %d; argv[%d] = %s\n", argc, i, argv[i]); + } + + for (int i = 0x00; i < (argc - command_index); i++) + { + printf("argc = %d; argv[command_index+%d] = %s\n", argc, i, argv[command_index + i]); + } + #endif + if (strcmp(command, "download-file") == 0) { char *bucket_id = argv[command_index + 1]; char *file_id = argv[command_index + 2]; @@ -1375,7 +1109,11 @@ int main(int argc, char **argv) goto end_program; } - if (download_file(env, bucket_id, file_id, path)) { + memcpy(cli_api->bucket_id, bucket_id, strlen(bucket_id)); + memcpy(cli_api->file_id, file_id, strlen(file_id)); + cli_api->dst_file = path; + + if (download_file(env, bucket_id, file_id, path, cli_api)) { status = 1; goto end_program; } @@ -1389,7 +1127,10 @@ int main(int argc, char **argv) goto end_program; } - if (upload_file(env, bucket_id, path)) { + memcpy(cli_api->bucket_id, bucket_id, strlen(bucket_id)); + cli_api->dst_file = path; + + if (upload_file(env, bucket_id, path, cli_api)) { status = 1; goto end_program; } @@ -1402,8 +1143,8 @@ int main(int argc, char **argv) goto end_program; } - storj_bridge_list_files(env, bucket_id, NULL, list_files_callback); - } else if (strcmp(command, "add-bucket") == 0) { + storj_bridge_list_files(env, bucket_id, cli_api, list_files_callback); + } else if ((strcmp(command, "add-bucket") == 0) || (strcmp(command, "mkbkt") == 0x00)) { char *bucket_name = argv[command_index + 1]; if (!bucket_name) { @@ -1424,7 +1165,7 @@ int main(int argc, char **argv) goto end_program; } - storj_bridge_delete_bucket(env, bucket_id, NULL, + storj_bridge_delete_bucket(env, bucket_id, cli_api, delete_bucket_callback); } else if (strcmp(command, "remove-file") == 0) { @@ -1437,7 +1178,7 @@ int main(int argc, char **argv) goto end_program; } storj_bridge_delete_file(env, bucket_id, file_id, - NULL, delete_file_callback); + cli_api, delete_file_callback); } else if (strcmp(command, "list-buckets") == 0) { storj_bridge_get_buckets(env, NULL, get_buckets_callback); @@ -1451,8 +1192,364 @@ int main(int argc, char **argv) goto end_program; } storj_bridge_list_mirrors(env, bucket_id, file_id, - NULL, list_mirrors_callback); - } else { + cli_api, list_mirrors_callback); + } else if (strcmp(command, "cp") == 0) { + #define UPLOAD_CMD 0x00 + #define DOWNLOAD_CMD 0x01 + #define RECURSIVE_CMD 0x02 + #define NON_RECURSIVE_CMD 0x03 + + int ret = 0x00; + char *src_path = NULL; /* holds the local path */ + char *dst_path = NULL; /* holds the storj:// path */ + char *bucket_name = NULL; + int cmd_type = 0x00; /* 0-> upload and 1 -> download */ + char modified_src_path[256] = {}; /* use this buffer to store the loca-file-path, if modified */ + memset(modified_src_path, 0x00, sizeof(modified_src_path)); + char *upload_file_path = modified_src_path; + + /* cp command wrt to upload-file */ + if(local_file_path == NULL) {/* without -r[R] */ + /* hold the local path */ + src_path = argv[command_index + 0x01]; + + /* Handle the dst argument (storj:/// */ + dst_path = argv[argc - 0x01]; + + cmd_type = NON_RECURSIVE_CMD; + } else { /* with -r[R] */ + if ((strcmp(argv[1],"-r") == 0x00) || (strcmp(argv[1],"-R") == 0x00)) { + src_path = local_file_path; + + /* Handle the dst argument (storj:/// */ + dst_path = argv[argc - 0x01]; + + cmd_type = RECURSIVE_CMD; + } else { + printf("[%s][%d] Invalid command option '%s'\n", + __FUNCTION__, __LINE__, argv[1]); + goto end_program; + } + } + + if ((strcmp(src_path, argv[command_index]) == 0x00) || + (strcmp(dst_path, argv[command_index]) == 0x00) || + (strcmp(dst_path, src_path) == 0x00)) { + printf("[%s][%d] Invalid command option '%s'\n", + __FUNCTION__, __LINE__, argv[1]); + goto end_program; + } + + /* check for upload or download command */ + char sub_str[] = "storj://"; + ret = strpos(dst_path, sub_str); + + if( ret == 0x00) { /* Handle upload command*/ + if (cmd_type == NON_RECURSIVE_CMD) { + if (check_file_path(src_path) != CLI_VALID_DIR) { + local_file_path = src_path; + + bucket_name = dst_path; + } else { + printf("[%s][%d] Invalid command entry\n", + __FUNCTION__, __LINE__); + goto end_program; + } + } else if (cmd_type == RECURSIVE_CMD) { + local_file_path = src_path; + + bucket_name = dst_path; + } else { + printf("[%s][%d] Invalid command entry \n", __FUNCTION__, __LINE__); + goto end_program; + } + + cmd_type = UPLOAD_CMD; + } + else if (ret == -1) { /* Handle download command*/ + ret = strpos(src_path, sub_str); + + if (ret == 0x00) { + if ((cmd_type == NON_RECURSIVE_CMD) || (cmd_type == RECURSIVE_CMD)) { + local_file_path = dst_path; + bucket_name = src_path; + + char *dst_file_name = NULL; + dst_file_name = (char *)get_filename_separator(local_file_path); + + /* token[0]-> storj:; token[1]->bucket_name; token[2]->upload_file_name */ + char *token[0x03]; + memset(token, 0x00, sizeof(token)); + int num_of_tokens = validate_cmd_tokenize(bucket_name, token); + + /* set the bucket name from which file(s) to be downloaded */ + cli_api->bucket_name = token[1]; + + /* initialize the local folder to copy the downloaded file into */ + cli_api->file_path = local_file_path; + + /* initialize the filename to be downloaded as */ + cli_api->dst_file = dst_file_name; + + if ((argc == 0x04) || (argc == 0x05)) { /* handle non recursive operations */ + if ((token[2] == NULL) || (strcmp(token[2], "*") == 0x00)) { + if (check_file_path(local_file_path) == CLI_VALID_DIR) { + cli_download_files(cli_api); + } else { + printf("[%s][%d] Invalid '%s' dst directory !!!!!\n", + __FUNCTION__, __LINE__, local_file_path); + goto end_program; + } + } else if (token[2] != NULL) { + /* set the file to be downloaded */ + cli_api->file_name = token[2]; + + if ((check_file_path(local_file_path) == CLI_VALID_DIR) || (strcmp(dst_file_name, ".") == 0x00)) { + memset(temp_buff, 0x00, sizeof(temp_buff)); + strcat(temp_buff, local_file_path); + strcat(temp_buff, "/"); + strcat(temp_buff, token[2]); + cli_api->dst_file = temp_buff; + } else if (strlen(dst_file_name) > 0x00) { + cli_api->dst_file = local_file_path; + } else { + printf("[%s][%d] Invalid '%s' dst directory !!!!!\n", + __FUNCTION__, __LINE__, local_file_path); + goto end_program; + } + printf("[%s][%d]file %s downloaded from bucketname = %s as file %s\n", + __FUNCTION__, __LINE__, cli_api->file_name, cli_api->bucket_name, cli_api->dst_file); + cli_download_file(cli_api); + } else { + printf("[%s][%d] Invalid '%s' dst directory !!!!!\n", + __FUNCTION__, __LINE__, local_file_path); + goto end_program; + } + } else { + /* more args then needed wrong command */ + printf("[%s][%d] Valid dst filename missing !!!!!\n", __FUNCTION__, __LINE__); + goto end_program; + } + } else { + printf("[%s][%d] Invalid command entry \n", __FUNCTION__, __LINE__); + goto end_program; + } + } else { + printf("[%s][%d]Invalid Command Entry (%d) \n", + __FUNCTION__, __LINE__, ret); + goto end_program; + } + } else { + printf("[%s][%d]Invalid Command Entry (%d) \n", + __FUNCTION__, __LINE__, ret); + goto end_program; + } + + if (cmd_type == UPLOAD_CMD) { + /* handling single file copy with -r[R]: ./storj cp -r /home/kishore/libstorj/src/xxx.y storj://testbucket/yyy.x */ + /* Handle the src argument */ + /* single file to copy, make sure the files exits */ + if ((argc == 0x05) && (check_file_path(local_file_path) == CLI_VALID_REGULAR_FILE)) { + cli_api->file_name = local_file_path; + + /* Handle the dst argument (storj:/// */ + /* token[0]-> storj:; token[1]->bucket_name; token[2]->upload_file_name */ + char *token[0x03]; + memset(token,0x00, sizeof(token)); + int num_of_tokens = validate_cmd_tokenize(bucket_name, token); + + if ((num_of_tokens == 0x02) || (num_of_tokens == 0x03)) { + char *dst_file_name = NULL; + + cli_api->bucket_name = token[1]; + dst_file_name = (char *)get_filename_separator(local_file_path); + + if ((token[2] == NULL) || (strcmp(dst_file_name, token[2]) == 0x00) || + (strcmp(token[2], ".") == 0x00)) { + /* use the src list buff as temp memory to hold the dst filename */ + memset(cli_api->src_list, 0x00, sizeof(cli_api->src_list)); + strcpy(cli_api->src_list, dst_file_name); + cli_api->dst_file = cli_api->src_list; + } else { + cli_api->dst_file = token[2]; + } + cli_upload_file(cli_api); + } else { + printf("[%s][%d] Valid dst filename missing !!!!!\n", __FUNCTION__, __LINE__); + goto end_program; + } + } + else { + /* directory is being used, store it in file_path */ + cli_api->file_path = local_file_path; + + /* Handle wild character options for files selection */ + if(check_file_path(local_file_path) != CLI_VALID_DIR) { + char pwd_path[256] = { }; + memset(pwd_path, 0x00, sizeof(pwd_path)); + char *upload_list_file = pwd_path; + + /* create "/tmp/STORJ_upload_list_file.txt" upload files list based on the file path */ + if ((upload_list_file = getenv("TMPDIR")) != NULL) { + if (upload_list_file[(strlen(upload_list_file) - 1)] == '/') { + strcat(upload_list_file, "STORJ_output_list.txt"); + } else { + strcat(upload_list_file, "/STORJ_output_list.txt"); + } + + /* check the directory and create the path to upload list file */ + memset(cli_api->src_list, 0x00, sizeof(cli_api->src_list)); + memcpy(cli_api->src_list, upload_list_file, sizeof(pwd_path)); + cli_api->dst_file = cli_api->src_list; + } else { + printf("[%s][%d] Upload list file generation error!!! \n", + __FUNCTION__, __LINE__); + goto end_program; + } + + /* if local file path is a file, then just get the directory + from that */ + char *ret = NULL; + ret = strrchr(local_file_path, '/'); + memset(temp_buff, 0x00, sizeof(temp_buff)); + memcpy(temp_buff, local_file_path, ((ret - local_file_path)+1)); + + FILE *file = NULL; + /* create the file and add the list of files to be uploaded */ + if ((file = fopen(cli_api->src_list, "w")) != NULL) { + if ((strcmp(argv[1],"-r") == 0x00) || (strcmp(argv[1],"-R") == 0x00)) { + fprintf(file, "%s\n", local_file_path); + } + + for (int i = 0x01; i < ((argc - command_index) - 1); i++) { + fprintf(file, "%s\n", argv[command_index + i]); + } + } else { + printf("[%s][%d] Invalid upload src path entered\n", __FUNCTION__, __LINE__); + goto end_program; + } + fclose(file); + + cli_api->file_path = temp_buff; + } else { + /* its a valid directory so let the list of files to be uploaded be handled in + verify upload file () */ + cli_api->dst_file = NULL; + + memcpy(upload_file_path, cli_api->file_path, strlen(cli_api->file_path)); + if (upload_file_path[(strlen(upload_file_path) - 1)] != '/') { + strcat(upload_file_path, "/"); + cli_api->file_path = upload_file_path; + } + } + + /* token[0]-> storj:; token[1]->bucket_name; token[2]->upload_file_name */ + char *token[0x03]; + memset(token, 0x00, sizeof(token)); + int num_of_tokens = validate_cmd_tokenize(bucket_name, token); + + if ((num_of_tokens > 0x00) && ((num_of_tokens >= 0x02) || (num_of_tokens <= 0x03))) { + char *dst_file_name = NULL; + + cli_api->bucket_name = token[1]; + + if ((token[2] == NULL) || + (strcmp(token[2], ".") == 0x00)) { + cli_upload_files(cli_api); + } else { + printf("[%s][%d] storj://; storj:/// storj:///. !!!!!\n", __FUNCTION__, __LINE__); + goto end_program; + } + } else { + printf("[%s][%d] Valid dst filename missing !!!!!\n", __FUNCTION__, __LINE__); + goto end_program; + } + } + } + } + else if (strcmp(command, "upload-files") == 0) { + /* get the corresponding bucket id from the bucket name */ + cli_api->bucket_name = argv[command_index + 1]; + cli_api->file_path = argv[command_index + 2]; + cli_api->dst_file = NULL; + + if (!cli_api->bucket_name || !cli_api->file_name) { + printf("Missing arguments: \n"); + status = 1; + goto end_program; + } + + cli_upload_files(cli_api); + } + else if (strcmp(command, "download-files") == 0) { + /* get the corresponding bucket id from the bucket name */ + cli_api->bucket_name = argv[command_index + 1]; + cli_api->file_path = argv[command_index + 2]; + + if (!cli_api->bucket_name || !cli_api->file_path) { + printf("Missing arguments: \n"); + status = 1; + goto end_program; + } + + cli_download_files(cli_api); + } + else if (strcmp(command, "mkbkt") == 0x00) { + char *bucket_name = argv[command_index + 1]; + + if (!bucket_name) { + printf("Missing first argument: \n"); + status = 1; + goto end_program; + } + + storj_bridge_create_bucket(env, bucket_name, + NULL, create_bucket_callback); + } else if (strcmp(command, "rm") == 0) { + cli_api->bucket_name = argv[command_index + 1]; + cli_api->file_name = argv[command_index + 2]; + + if (cli_api->file_name != NULL) { + if (!cli_api->bucket_name|| !cli_api->file_name) { + printf("Missing arguments, expected: \n"); + status = 1; + goto end_program; + } + + cli_remove_file(cli_api); + } + else { + cli_remove_bucket(cli_api); + } + } + else if (strcmp(command, "ls") == 0x00) { + if (argv[command_index + 1] != NULL) { + /* bucket-name , used to list files */ + cli_api->bucket_name = argv[command_index + 1]; + + cli_list_files(cli_api); + } + else { + cli_list_buckets(cli_api); + } + } + else if (strcmp(command, "get-bucket-id") == 0) { + cli_api->bucket_name = argv[command_index + 1]; + cli_get_bucket_id(cli_api); + } + else if (strcmp(command, "lm") == 0) { + cli_api->bucket_name = argv[command_index + 1]; + cli_api->file_name = argv[command_index + 2]; + + if (!cli_api->bucket_name|| !cli_api->file_name) { + printf("Missing arguments, expected: \n"); + status = 1; + goto end_program; + } + + cli_list_mirrors(cli_api); + } else + { printf("'%s' is not a storj command. See 'storj --help'\n\n", command); status = 1; @@ -1485,5 +1582,9 @@ int main(int argc, char **argv) if (mnemonic) { free(mnemonic); } + if(cli_api){ + free(cli_api); + } + return status; } diff --git a/src/cli_callback.c b/src/cli_callback.c new file mode 100644 index 0000000..abcab95 --- /dev/null +++ b/src/cli_callback.c @@ -0,0 +1,1168 @@ +#include "cli_callback.h" + +//#define debug_enable + +#define MAX_UPLOAD_FILES 256 + +static inline void noop() {}; + +static void get_input(char *line) +{ + if (fgets(line, BUFSIZ, stdin) == NULL) { + line[0] = '\0'; + } else { + int len = strlen(line); + if (len > 0) { + char *last = strrchr(line, '\n'); + if (last) { + last[0] = '\0'; + } + last = strrchr(line, '\r'); + if (last) { + last[0] = '\0'; + } + } + } +} + +/* inserts into subject[] at position pos */ +void append(char subject[], const char insert[], int pos) +{ + char buf[256] = { }; + + /* copy at most first pos characters */ + strncpy(buf, subject, pos); + int len = strlen(buf); + + /* copy all of insert[] at the end */ + strcpy(buf + len, insert); + + /* increase the length by length of insert[] */ + len += strlen(insert); + + /* copy the rest */ + strcpy(buf + len, subject + pos); + + /* copy it back to subject */ + strcpy(subject, buf); +} + +char* replace_char(char* str, char find, char replace) +{ + char *current_pos = strchr(str, find); + while (current_pos) + { + *current_pos = replace; + append(current_pos, "_", 0); + current_pos = strchr(current_pos, find); + } + return (str); +} + +static void printdir(char *dir, int depth, FILE *src_fd, void *handle) +{ + DIR *dp; + struct dirent *entry; + struct stat statbuf; + int spaces = depth*4; + char tmp_dir[800] = {}; + char *full_path = NULL; + char *s; + char * start; + cli_api_t *cli_api = handle; + + if((dp = opendir(dir)) == NULL) { + fprintf(stderr,"cannot open directory: %s\n", dir); + return; + } + + int ret = chdir(dir); + while((entry = readdir(dp)) != NULL) + { + lstat(entry->d_name, &statbuf); + if (S_ISDIR(statbuf.st_mode)) { + /* Found a directory, but ignore . and .. */ + if (strcmp(".", entry->d_name) == 0 || + strcmp("..", entry->d_name) == 0) continue; + + /* Recurse at a new indent level */ + printdir(entry->d_name, depth + 1, src_fd, handle); + } else { + full_path = realpath(entry->d_name, NULL); + /* write to src file */ + fprintf(src_fd, "%s%s\n", "", full_path); + } + } + ret = chdir(".."); + closedir(dp); + free(full_path); +} + +static int file_exists(void *handle) +{ + struct stat sb; + gid_t st_grpid; + cli_api_t *cli_api = handle; + + FILE *src_fd, *dst_fd; + + if (stat(cli_api->file_path, &sb) == -1) { + perror("stat"); + return CLI_NO_SUCH_FILE_OR_DIR; + } + + switch (sb.st_mode & S_IFMT) + { + case S_IFBLK: + printf("block device\n"); + break; + case S_IFCHR: + printf("character device\n"); + break; + case S_IFDIR: + if((src_fd = fopen(cli_api->src_list, "w")) == NULL) { + return CLI_UPLOAD_FILE_LOG_ERR; + } + printdir(cli_api->file_path, 0, src_fd, handle); + fclose(src_fd); + return CLI_VALID_DIR; + break; + case S_IFIFO: + printf("FIFO/pipe\n"); + break; + case S_IFLNK: + printf("symlink\n"); + break; + case S_IFREG: + return CLI_VALID_REGULAR_FILE; + break; + case S_IFSOCK: + printf("socket\n"); + break; + default: + printf("unknown?\n"); + break; + } + + return CLI_UNKNOWN_FILE_ATTR; +} + +static const char *get_filename_separator(const char *file_path) +{ + const char *file_name = NULL; +#ifdef _WIN32 + file_name = strrchr(file_path, '\\'); + if (!file_name) { + file_name = strrchr(file_path, '/'); + } + if (!file_name && file_path) { + file_name = file_path; + } + if (!file_name) { + return NULL; + } + if (file_name[0] == '\\' || file_name[0] == '/') { + file_name++; + } +#else + file_name = strrchr(file_path, '/'); + if (!file_name && file_path) { + file_name = file_path; + } + if (!file_name) { + return NULL; + } + if (file_name[0] == '/') { + file_name++; + } +#endif + return file_name; +} + +static void close_signal(uv_handle_t *handle) +{ + ((void)0); +} + +static void file_progress(double progress, + uint64_t downloaded_bytes, + uint64_t total_bytes, + void *handle) +{ + int bar_width = 70; + + if (progress == 0 && downloaded_bytes == 0) { + printf("Preparing File..."); + fflush(stdout); + return; + } + + printf("\r["); + int pos = bar_width * progress; + for (int i = 0; i < bar_width; ++i) + { + if (i < pos) { + printf("="); + } + else if (i == pos) { + printf(">"); + } else { + printf(" "); + } + } + printf("] %.*f%%", 2, progress * 100); + + fflush(stdout); +} + +static void upload_file_complete(int status, storj_file_meta_t *file, void *handle) +{ + cli_api_t *cli_api = handle; + cli_api->rcvd_cmd_resp = "upload-file-resp"; + + printf("\n"); + if (status != 0) { + printf("Upload failure: %s\n", storj_strerror(status)); + exit(status); + } + + printf("Upload Success! File ID: %s\n", file->id); + + storj_free_uploaded_file_info(file); + + queue_next_cmd_req(cli_api); +} + +static void upload_signal_handler(uv_signal_t *req, int signum) +{ + storj_upload_state_t *state = req->data; + storj_bridge_store_file_cancel(state); + if (uv_signal_stop(req)) { + printf("Unable to stop signal\n"); + } + uv_close((uv_handle_t *)req, close_signal); +} + +static int upload_file(storj_env_t *env, char *bucket_id, const char *file_path, void *handle) +{ + cli_api_t *cli_api = handle; + + FILE *fd = fopen(file_path, "r"); + + if (!fd) { + printf("Invalid file path: %s\n", file_path); + exit(0); + } + + const char *file_name = get_filename_separator(file_path); + + if (cli_api->dst_file == NULL) { + if (!file_name) { + file_name = file_path; + } + } else { + file_name = cli_api->dst_file; + } + + // Upload opts env variables: + char *prepare_frame_limit = getenv("STORJ_PREPARE_FRAME_LIMIT"); + char *push_frame_limit = getenv("STORJ_PUSH_FRAME_LIMIT"); + char *push_shard_limit = getenv("STORJ_PUSH_SHARD_LIMIT"); + char *rs = getenv("STORJ_REED_SOLOMON"); + + storj_upload_opts_t upload_opts = { + .prepare_frame_limit = (prepare_frame_limit) ? atoi(prepare_frame_limit) : 1, + .push_frame_limit = (push_frame_limit) ? atoi(push_frame_limit) : 64, + .push_shard_limit = (push_shard_limit) ? atoi(push_shard_limit) : 64, + .rs = (!rs) ? true : (strcmp(rs, "false") == 0) ? false : true, + .bucket_id = bucket_id, + .file_name = file_name, + .fd = fd + }; + + uv_signal_t *sig = malloc(sizeof(uv_signal_t)); + if (!sig) { + return 1; + } + uv_signal_init(env->loop, sig); + uv_signal_start(sig, upload_signal_handler, SIGINT); + + + + storj_progress_cb progress_cb = (storj_progress_cb)noop; + if (env->log_options->level == 0) { + progress_cb = file_progress; + } + + storj_upload_state_t *state = storj_bridge_store_file(env, + &upload_opts, + handle, + progress_cb, + upload_file_complete); + + if (!state) { + return 1; + } + + sig->data = state; + + return state->error_status; +} + +static void upload_files_complete(int status, storj_file_meta_t *file, void *handle) +{ + cli_api_t *cli_api = handle; + cli_api->rcvd_cmd_resp = "upload-files-resp"; + + printf("\n"); + if (status != 0) { + printf("[%s][%d]Upload failure: %s\n", + __FUNCTION__, __LINE__, storj_strerror(status)); + } else { + printf("Upload Success! File ID: %s\n", file->id); + storj_free_uploaded_file_info(file); + } + + queue_next_cmd_req(cli_api); +} + +static int upload_files(storj_env_t *env, char *bucket_id, const char *file_path, void *handle) +{ + cli_api_t *cli_api = handle; + + FILE *fd = fopen(file_path, "r"); + + if (!fd) { + printf("[%s][%d]Invalid file : %s\n", __FUNCTION__, __LINE__, file_path); + exit(0); + } + + printf("Uploading[%d]of[%d] src file = %s as ", + cli_api->xfer_count, cli_api->total_files, file_path); + + /* replace the dir with __ */ + char *s = strstr(cli_api->src_file, cli_api->file_path); + char *start = s + strlen(cli_api->file_path); + char tmp_dir[256]; + start = replace_char(start, '/', '_'); + memset(tmp_dir, 0x00, sizeof(tmp_dir)); + strcat(tmp_dir, cli_api->file_path); + strcat(tmp_dir, start); + cli_api->dst_file = tmp_dir; + + const char *file_name = get_filename_separator(cli_api->dst_file); + + if (!file_name) { + file_name = file_path; + } + printf(" %s\n", file_name); + + // Upload opts env variables: + char *prepare_frame_limit = getenv("STORJ_PREPARE_FRAME_LIMIT"); + char *push_frame_limit = getenv("STORJ_PUSH_FRAME_LIMIT"); + char *push_shard_limit = getenv("STORJ_PUSH_SHARD_LIMIT"); + char *rs = getenv("STORJ_REED_SOLOMON"); + + storj_upload_opts_t upload_opts = { + .prepare_frame_limit = (prepare_frame_limit) ? atoi(prepare_frame_limit) : 1, + .push_frame_limit = (push_frame_limit) ? atoi(push_frame_limit) : 64, + .push_shard_limit = (push_shard_limit) ? atoi(push_shard_limit) : 64, + .rs = (!rs) ? true : (strcmp(rs, "false") == 0) ? false : true, + .bucket_id = bucket_id, + .file_name = file_name, + .fd = fd + }; + + uv_signal_t *sig = malloc(sizeof(uv_signal_t)); + if (!sig) { + return 1; + } + uv_signal_init(env->loop, sig); + uv_signal_start(sig, upload_signal_handler, SIGINT); + + storj_progress_cb progress_cb = (storj_progress_cb)noop; + if (env->log_options->level == 0) { + progress_cb = file_progress; + } + + storj_upload_state_t *state = storj_bridge_store_file(env, + &upload_opts, + handle, + progress_cb, + upload_files_complete); + + if (!state) { + return 1; + } + + sig->data = state; + + return state->error_status; +} + +static void verify_upload_files(void *handle) +{ + cli_api_t *cli_api = handle; + int total_src_files = 0x00; + int total_dst_files = 0x00; + int ret = 0x00; + + /* upload list file previously not created? */ + if (cli_api->dst_file == NULL) + { + char pwd_path[256]= {}; + memset(pwd_path, 0x00, sizeof(pwd_path)); + char *upload_list_file = pwd_path; + + /* create upload files list based on the file path */ + if ((upload_list_file = getenv("TMPDIR")) != NULL) { + if (upload_list_file[(strlen(upload_list_file) - 1)] == '/') { + strcat(upload_list_file, "STORJ_output_list.txt"); + } else { + strcat(upload_list_file, "/STORJ_output_list.txt"); + } + + /* check the directory and create the path to upload list file */ + memset(cli_api->src_list, 0x00, sizeof(cli_api->src_list)); + memcpy(cli_api->src_list, upload_list_file, sizeof(pwd_path)); + cli_api->dst_file = cli_api->src_list; + } + + /* create a upload list file src_list.txt */ + int file_attr = file_exists(handle); + } + + /* create a upload list file src_list.txt */ + cli_api->src_fd = fopen(cli_api->src_list, "r"); + + if (!cli_api->src_fd) { + printf("[%s][%d]Invalid file path: %s\n", + __FUNCTION__, __LINE__, cli_api->src_list); + exit(0); + } else { + /* count total src_list files */ + char line[MAX_UPLOAD_FILES][256]; + char *temp; + int i = 0x00; + + memset(line, 0x00, sizeof(line)); + + /* read a line from a file */ + while (fgets(line[i], sizeof(line), cli_api->src_fd) != NULL) + { + if (i <= MAX_UPLOAD_FILES) { + i++; + } else { + i = (i - 1); + printf("[%s][%d] Upload files limit set to %d \n", + __FUNCTION__, __LINE__, (MAX_UPLOAD_FILES)); + break; + } + } + + total_src_files = i; + } + + cli_api->total_files = total_src_files; + cli_api->xfer_count = 0x01; + + cli_api->rcvd_cmd_resp = "verify-upload-files-resp"; + queue_next_cmd_req(cli_api); +} + +static void download_file_complete(int status, FILE *fd, void *handle) +{ + cli_api_t *cli_api = handle; + cli_api->rcvd_cmd_resp = "download-file-resp"; + + printf("\n"); + fclose(fd); + if (status) { + // TODO send to stderr + switch(status) { + case STORJ_FILE_DECRYPTION_ERROR: + printf("Unable to properly decrypt file, please check " \ + "that the correct encryption key was " \ + "imported correctly.\n\n"); + break; + default: + printf("[%s][%d]Download failure: %s\n", + __FUNCTION__, __LINE__, storj_strerror(status)); + } + } else { + printf("Download Success!\n"); + } + + queue_next_cmd_req(cli_api); +} + +static void download_signal_handler(uv_signal_t *req, int signum) +{ + storj_download_state_t *state = req->data; + storj_bridge_resolve_file_cancel(state); + if (uv_signal_stop(req)) { + printf("Unable to stop signal\n"); + } + uv_close((uv_handle_t *)req, close_signal); +} + +static int download_file(storj_env_t *env, char *bucket_id, + char *file_id, char *path, void *handle) +{ + FILE *fd = NULL; + + if (path) { + char user_input[BUFSIZ]; + memset(user_input, '\0', BUFSIZ); + + if(access(path, F_OK) != -1 ) { + printf("Warning: File already exists at path [%s].\n", path); + while (strcmp(user_input, "y") != 0 && strcmp(user_input, "n") != 0) + { + memset(user_input, '\0', BUFSIZ); + printf("Would you like to overwrite [%s]: [y/n] ", path); + get_input(user_input); + } + + if (strcmp(user_input, "n") == 0) { + printf("\nCanceled overwriting of [%s].\n", path); + cli_api_t *cli_api = handle; + cli_api->rcvd_cmd_resp = "download-file-resp"; + queue_next_cmd_req(cli_api); + return 1; + } + + unlink(path); + } + + fd = fopen(path, "w+"); + } else { + fd = stdout; + } + + if (fd == NULL) { + // TODO send to stderr + printf("Unable to open %s: %s\n", path, strerror(errno)); + return 1; + } + + uv_signal_t *sig = malloc(sizeof(uv_signal_t)); + uv_signal_init(env->loop, sig); + uv_signal_start(sig, download_signal_handler, SIGINT); + + storj_progress_cb progress_cb = (storj_progress_cb)noop; + if (path && env->log_options->level == 0) { + progress_cb = file_progress; + } + + storj_download_state_t *state = storj_bridge_resolve_file(env, bucket_id, + file_id, fd, handle, + progress_cb, + download_file_complete); + if (!state) { + return 1; + } + sig->data = state; + + return state->error_status; +} + +static void list_mirrors_callback(uv_work_t *work_req, int status) +{ + assert(status == 0); + json_request_t *req = work_req->data; + + cli_api_t *cli_api = req->handle; + cli_api->last_cmd_req = cli_api->curr_cmd_req; + cli_api->rcvd_cmd_resp = "list-mirrors-resp"; + + if (req->status_code != 200) { + printf("Request failed with status code: %i\n", + req->status_code); + goto cleanup; + } + + if (req->response == NULL) { + free(req); + free(work_req); + printf("Failed to list mirrors.\n"); + goto cleanup; + } + + int num_mirrors = json_object_array_length(req->response); + + struct json_object *shard; + struct json_object *established; + struct json_object *available; + struct json_object *item; + struct json_object *hash; + struct json_object *contract; + struct json_object *address; + struct json_object *port; + struct json_object *node_id; + + for (int i = 0; i < num_mirrors; i++) { + shard = json_object_array_get_idx(req->response, i); + json_object_object_get_ex(shard, "established", + &established); + int num_established = + json_object_array_length(established); + for (int j = 0; j < num_established; j++) { + item = json_object_array_get_idx(established, j); + if (j == 0) { + json_object_object_get_ex(item, "shardHash", + &hash); + printf("Shard %i: %s\n", i, json_object_get_string(hash)); + } + json_object_object_get_ex(item, "contract", &contract); + json_object_object_get_ex(contract, "farmer_id", &node_id); + + const char *node_id_str = json_object_get_string(node_id); + printf("\tnodeID: %s\n", node_id_str); + } + printf("\n\n"); + } + + json_object_put(req->response); + + queue_next_cmd_req(cli_api); +cleanup: + free(req->path); + free(req); + free(work_req); +} + +static void delete_file_callback(uv_work_t *work_req, int status) +{ + assert(status == 0); + json_request_t *req = work_req->data; + + cli_api_t *cli_api = req->handle; + cli_api->last_cmd_req = cli_api->curr_cmd_req; + cli_api->rcvd_cmd_resp = "remove-file-resp"; + + if (req->status_code == 200 || req->status_code == 204) { + printf("File was successfully removed from bucket.\n"); + } else if (req->status_code == 401) { + printf("Invalid user credentials.\n"); + goto cleanup; + } else { + printf("Failed to remove file from bucket. (%i)\n", req->status_code); + goto cleanup; + } + + json_object_put(req->response); + + queue_next_cmd_req(cli_api); +cleanup: + free(req->path); + free(req); + free(work_req); +} + +static void delete_bucket_callback(uv_work_t *work_req, int status) +{ + assert(status == 0); + json_request_t *req = work_req->data; + + cli_api_t *cli_api = req->handle; + cli_api->last_cmd_req = cli_api->curr_cmd_req; + cli_api->rcvd_cmd_resp = "remove-bucket-resp"; + + if (req->status_code == 200 || req->status_code == 204) + { + printf("Bucket was successfully removed.\n"); + } + else if (req->status_code == 401) + { + printf("Invalid user credentials.\n"); + goto cleanup; + } + else + { + printf("Failed to destroy bucket. (%i)\n", req->status_code); + goto cleanup; + } + + json_object_put(req->response); + + queue_next_cmd_req(cli_api); +cleanup: + free(req->path); + free(req); + free(work_req); +} + +void get_buckets_callback(uv_work_t *work_req, int status) +{ + assert(status == 0); + get_buckets_request_t *req = work_req->data; + + if (req->status_code == 401) { + printf("Invalid user credentials.\n"); + } else if (req->status_code != 200 && req->status_code != 304) { + printf("Request failed with status code: %i\n", req->status_code); + } else if (req->total_buckets == 0) { + printf("No buckets.\n"); + } + + for (int i = 0; i < req->total_buckets; i++) { + storj_bucket_meta_t *bucket = &req->buckets[i]; + printf("ID: %s \tDecrypted: %s \tCreated: %s \tName: %s\n", + bucket->id, bucket->decrypted ? "true" : "false", + bucket->created, bucket->name); + } + + storj_free_get_buckets_request(req); + free(work_req); +} + +void get_bucket_id_callback(uv_work_t *work_req, int status) +{ + int ret_status = 0x00; + assert(status == 0); + get_bucket_id_request_t *req = work_req->data; + cli_api_t *cli_api = req->handle; + + cli_api->last_cmd_req = cli_api->curr_cmd_req; + cli_api->rcvd_cmd_resp = "get-bucket-id-resp"; + + if (req->status_code == 401) { + printf("Invalid user credentials.\n"); + goto cleanup; + } else if (req->status_code != 200 && req->status_code != 304) { + printf("Request failed with status code: %i\n", req->status_code); + goto cleanup; + } + + /* store the bucket id */ + memset(cli_api->bucket_id, 0x00, sizeof(cli_api->bucket_id)); + strcpy(cli_api->bucket_id, (char *)req->bucket_id); + printf("ID: %s \tName: %s\n", req->bucket_id, req->bucket_name); + + queue_next_cmd_req(cli_api); + +cleanup: + free(req); + free(work_req); +} + +void get_file_id_callback(uv_work_t *work_req, int status) +{ + int ret_status = 0x00; + assert(status == 0); + get_file_id_request_t *req = work_req->data; + cli_api_t *cli_api = req->handle; + + cli_api->last_cmd_req = cli_api->curr_cmd_req; + cli_api->rcvd_cmd_resp = "get-file-id-resp"; + + if (req->status_code == 401) { + printf("Invalid user credentials.\n"); + goto cleanup; + } else if (req->status_code != 200 && req->status_code != 304) { + printf("Request failed with status code: %i\n", req->status_code); + goto cleanup; + } + + /* store the bucket id */ + memset(cli_api->file_id, 0x00, sizeof(cli_api->file_id)); + strcpy(cli_api->file_id, (char *)req->file_id); + printf("ID: %s \tName: %s\n", req->file_id, req->file_name); + + queue_next_cmd_req(cli_api); + + cleanup: + free(req); + free(work_req); +} + +void list_files_callback(uv_work_t *work_req, int status) +{ + int ret_status = 0; + assert(status == 0); + list_files_request_t *req = work_req->data; + + cli_api_t *cli_api = req->handle; + cli_api->last_cmd_req = cli_api->curr_cmd_req; + cli_api->rcvd_cmd_resp = "list-files-resp"; + + if (req->status_code == 404) { + printf("Bucket id [%s] does not exist\n", req->bucket_id); + goto cleanup; + } else if (req->status_code == 400) { + printf("Bucket id [%s] is invalid\n", req->bucket_id); + goto cleanup; + } else if (req->status_code == 401) { + printf("Invalid user credentials.\n"); + goto cleanup; + } else if (req->status_code != 200) { + printf("Request failed with status code: %i\n", req->status_code); + } + + if (req->total_files == 0) { + printf("No files for bucket.\n"); + goto cleanup; + } + + + FILE *dwnld_list_fd = stdout; + if ((dwnld_list_fd = fopen("/tmp/dwnld_list.txt", "w")) == NULL) { + printf("[%s][%d] Unable to create download list file\n", + __FUNCTION__, __LINE__); + goto cleanup; + } + + for (int i = 0; i < req->total_files; i++) { + storj_file_meta_t *file = &req->files[i]; + + if ((cli_api->file_name != NULL) && + (strcmp(cli_api->file_name, file->filename)) == 0x00) { + /* store the file id */ + memset(cli_api->file_id, 0x00, sizeof(cli_api->file_id)); + strcpy(cli_api->file_id, (char *)file->id); + } + + printf("ID: %s \tSize: %" PRIu64 " bytes \tDecrypted: %s \tType: %s \tCreated: %s \tName: %s\n", + file->id, + file->size, + file->decrypted ? "true" : "false", + file->mimetype, + file->created, + file->filename); + + fprintf(dwnld_list_fd, "%s:%s\n",file->id, file->filename); + } + + cli_api->total_files = req->total_files; + cli_api->xfer_count = 0x01; + fclose(dwnld_list_fd); + queue_next_cmd_req(cli_api); + + cleanup: + + storj_free_list_files_request(req); + free(work_req); +} + +void queue_next_cmd_req(cli_api_t *cli_api) +{ + void *handle = cli_api->handle; + + #ifdef debug_enable + printf("[%s][%d]start !!!! expt resp = %s; rcvd resp = %s \n", + __FUNCTION__, __LINE__, + cli_api->excp_cmd_resp, cli_api->rcvd_cmd_resp ); + printf("[%s][%d]last cmd = %s; cur cmd = %s; next cmd = %s\n", + __FUNCTION__, __LINE__, cli_api->last_cmd_req, + cli_api->curr_cmd_req, cli_api->next_cmd_req); + #endif + + if(cli_api->excp_cmd_resp != NULL) { + if (strcmp(cli_api->excp_cmd_resp, cli_api->rcvd_cmd_resp) == 0x00) { + if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "get-file-id-req") == 0x00)) { + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "get-file-id-resp"; + + storj_bridge_get_file_id(cli_api->env, cli_api->bucket_id, + cli_api->file_name, cli_api, get_file_id_callback); + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "list-files-req") == 0x00)) { + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "list-files-resp"; + + storj_bridge_list_files(cli_api->env, cli_api->bucket_id, + cli_api, list_files_callback); + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "remove-bucket-req") == 0x00)) { + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "remove-bucket-resp"; + + storj_bridge_delete_bucket(cli_api->env, cli_api->bucket_id, + cli_api, delete_bucket_callback); + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "remove-file-req") == 0x00)) { + printf("[%s][%d]file-name = %s; file-id = %s; bucket-name = %s \n", + __FUNCTION__, __LINE__, cli_api->file_name, cli_api->file_id, + cli_api->bucket_name); + + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "remove-file-resp"; + + storj_bridge_delete_file(cli_api->env, cli_api->bucket_id, cli_api->file_id, + cli_api, delete_file_callback); + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "list-mirrors-req") == 0x00)) { + printf("[%s][%d]file-name = %s; file-id = %s; bucket-name = %s \n", + __FUNCTION__, __LINE__, cli_api->file_name, cli_api->file_id, + cli_api->bucket_name); + + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "list-mirrors-resp"; + + storj_bridge_list_mirrors(cli_api->env, cli_api->bucket_id, cli_api->file_id, + cli_api, list_mirrors_callback); + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "upload-file-req") == 0x00)) { + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "upload-file-resp"; + + upload_file(cli_api->env, cli_api->bucket_id, cli_api->file_name, cli_api); + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "verify-upload-files-req") == 0x00)) { + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "verify-upload-files-resp"; + + verify_upload_files(cli_api); + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "upload-files-req") == 0x00)) { + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->excp_cmd_resp = "upload-files-resp"; + + FILE *file = fopen(cli_api->src_list, "r"); + + char line[256][256]; + char *temp; + int i = 0x00; + memset(line, 0x00, sizeof(line)); + + if (file != NULL) { + while((fgets(line[i],sizeof(line), file)!= NULL)) {/* read a line from a file */ + temp = strrchr(line[i], '\n'); + if(temp) *temp = '\0'; + cli_api->src_file = line[i]; + i++; + if(i >= cli_api->xfer_count) { + break; + } + } + } + fclose(file); + + if (cli_api->xfer_count <= cli_api->total_files) { + /* is it the last file ? */ + if(cli_api->xfer_count == cli_api->total_files) { + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + } + + upload_files(cli_api->env, cli_api->bucket_id, cli_api->src_file, cli_api); + cli_api->xfer_count++; + } else { + printf("[%s][%d] Invalid xfer counts\n", __FUNCTION__, __LINE__); + exit(0); + } + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "download-file-req") == 0x00)) { + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "download-file-resp"; + + download_file(cli_api->env, cli_api->bucket_id, cli_api->file_id, + cli_api->dst_file, cli_api); + } else if ((cli_api->next_cmd_req != NULL) && + (strcmp(cli_api->next_cmd_req, "download-files-req") == 0x00)) { + cli_api->curr_cmd_req = cli_api->next_cmd_req; + cli_api->excp_cmd_resp = "download-file-resp"; + + FILE *file = stdout; + + if ((file = fopen("/tmp/dwnld_list.txt", "r")) != NULL) { + char line[256][256]; + char *temp; + char temp_path[1024]; + int i = 0x00; + char *token[10]; + int tk_idx= 0x00; + memset(token, 0x00, sizeof(token)); + memset(temp_path, 0x00, sizeof(temp_path)); + memset(line, 0x00, sizeof(line)); + while((fgets(line[i],sizeof(line), file)!= NULL)) {/* read a line from a file */ + temp = strrchr(line[i], '\n'); + if(temp) *temp = '\0'; + i++; + if (i >= cli_api->xfer_count) { + break; + } + } + fclose(file); + + /* start tokenizing */ + token[0] = strtok(line[i-1], ":"); + while (token[tk_idx] != NULL) { + tk_idx++; + token[tk_idx] = strtok(NULL, ":"); + } + + if(cli_api->xfer_count <= cli_api->total_files) { + /* is it the last file ? */ + if(cli_api->xfer_count == cli_api->total_files) { + cli_api->next_cmd_req = cli_api->final_cmd_req; + cli_api->final_cmd_req = NULL; + } + + memset(cli_api->file_id, 0x00, sizeof(cli_api->file_id)); + strcpy(cli_api->file_id, token[0]); + strcpy(temp_path, cli_api->file_path); + if (cli_api->file_path[(strlen(cli_api->file_path)-1)] != '/') { + strcat(temp_path, "/"); + } + strcat(temp_path, token[1]); + fprintf(stdout,"*****[%d:%d] downloading file to: %s *****\n", cli_api->xfer_count, cli_api->total_files, temp_path); + cli_api->xfer_count++; + + download_file(cli_api->env, cli_api->bucket_id, cli_api->file_id, temp_path, cli_api); + } else { + printf("[%s][%d] Invalid xfer counts\n", __FUNCTION__, __LINE__); + exit(0); + } + } + } else { + #ifdef debug_enable + printf("[%s][%d] **** ALL CLEAN & DONE *****\n", __FUNCTION__, __LINE__); + #endif + + exit(0); + } + } else { + printf("[%s][%d]Oops !!!! expt resp = %s; rcvd resp = %s \n", + __FUNCTION__, __LINE__, + cli_api->excp_cmd_resp, cli_api->rcvd_cmd_resp ); + printf("[%s][%d]last cmd = %s; cur cmd = %s; next cmd = %s\n", + __FUNCTION__, __LINE__, cli_api->last_cmd_req, + cli_api->curr_cmd_req, cli_api->next_cmd_req); + } + } else { + /* calling straight storj calls without going thru the state machine */ + exit(0); + } +} + +int cli_list_buckets(cli_api_t *cli_api) +{ + cli_api->last_cmd_req = NULL; + cli_api->curr_cmd_req = "get-bucket-id-req"; + cli_api->next_cmd_req = NULL; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "get-bucket-id-resp"; + + /* when callback returns, we store the bucket id of bucket name else null */ + return storj_bridge_get_buckets(cli_api->env, cli_api, get_buckets_callback); +} + +int cli_get_bucket_id(cli_api_t *cli_api) +{ + int ret = -1; + cli_api->last_cmd_req = NULL; + cli_api->curr_cmd_req = "get-bucket-id-req"; + cli_api->next_cmd_req = NULL; + cli_api->final_cmd_req = NULL; + cli_api->excp_cmd_resp = "get-bucket-id-resp"; + + /* when callback returns, we store the bucket id of bucket name else null */ + //return storj_bridge_get_buckets(cli_api->env, cli_api, get_bucket_id_callback); + return storj_bridge_get_bucket_id(cli_api->env, cli_api->bucket_name, cli_api, get_bucket_id_callback); +} + +int cli_get_file_id(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_get_bucket_id(cli_api); + cli_api->next_cmd_req = "get-file-id-req"; + cli_api->final_cmd_req = NULL; + + return ret; +} + +int cli_list_files(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_get_bucket_id(cli_api); + cli_api->next_cmd_req = "list-files-req"; + cli_api->final_cmd_req = NULL; + + return ret; +} + +int cli_remove_bucket(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_get_bucket_id(cli_api); + cli_api->next_cmd_req = "remove-bucket-req"; + cli_api->final_cmd_req = NULL; + + return ret; +} + +int cli_remove_file(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_get_file_id(cli_api); + cli_api->final_cmd_req = "remove-file-req"; + + return ret; +} + +int cli_list_mirrors(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_get_file_id(cli_api); + cli_api->final_cmd_req = "list-mirrors-req"; + + return ret; +} + +int cli_upload_file(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_get_bucket_id(cli_api); + cli_api->next_cmd_req = "upload-file-req"; + cli_api->final_cmd_req = NULL; + + return ret; +} + +int cli_upload_files(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_get_bucket_id(cli_api); + cli_api->next_cmd_req = "verify-upload-files-req"; + cli_api->final_cmd_req = "upload-files-req"; + + return ret; +} + +int cli_download_file(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_get_file_id(cli_api); + cli_api->final_cmd_req = "download-file-req"; + + return ret; +} + +int cli_download_files(cli_api_t *cli_api) +{ + int ret = 0x00; + ret = cli_list_files(cli_api); + cli_api->final_cmd_req = "download-files-req"; + + return ret; +} + diff --git a/src/cli_callback.h b/src/cli_callback.h new file mode 100755 index 0000000..6f0800b --- /dev/null +++ b/src/cli_callback.h @@ -0,0 +1,175 @@ +/** + * @file storjapi_callback.h + * @brief Storj callback library. + * + * Implements callback functionality that can be customised for + * end user's application + */ + +#ifndef CLI_CALLBACK_H +#define CLI_CALLBACK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "storj.h" + +#define CLI_NO_SUCH_FILE_OR_DIR 0x00 +#define CLI_VALID_REGULAR_FILE 0x01 +#define CLI_VALID_DIR 0x02 +#define CLI_UNKNOWN_FILE_ATTR 0x03 +#define CLI_UPLOAD_FILE_LOG_ERR 0x04 + +/** + * @brief A Structure for passing the User's Application info to + * Storj API. + */ +typedef struct cli_api { + storj_env_t *env; + char *bucket_name; + char bucket_id[256]; + char *file_name; + char file_id[256]; + char *file_path; /**< local upload files directory path */ + FILE *src_fd; + char src_list[256]; /**< file list ready to upload */ + char *src_file; /**< next file ready to upload */ + FILE *dst_fd; + char *dst_file; /**< next file ready to upload */ + int xfer_count; /**< # of files xferred (up/down) */ + int total_files; /**< total files to upload */ + char *last_cmd_req; /**< last command requested */ + char *curr_cmd_req; /**< cli curr command requested */ + char *next_cmd_req; /**< cli curr command requested */ + char *final_cmd_req; /**< final command in the seq */ + char *excp_cmd_resp; /**< expected cmd response */ + char *rcvd_cmd_resp; /**< received cmd response */ + int error_status; /**< command response/error status */ + storj_log_levels_t *log; + void *handle; +} cli_api_t; + +/** + * @brief Callback function listing bucket names & IDs + */ +void get_buckets_callback(uv_work_t *work_req, int status); + +/** + * @brief Callback function returning the bucket id for a given + * bucket name + */ +void get_bucket_id_callback(uv_work_t *work_req, int status); + +/** + * @brief Callback function returning the file id for a given + * file name + */ +void get_file_id_callback(uv_work_t *work_req, int status); + +/** + * @brief Storj api state machine function + */ +void queue_next_cmd_req(cli_api_t *cli_api); + +/** + * @brief Function lists the bucket names & IDs + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_list_buckets(cli_api_t *cli_api); + +/** + * @brief Function returns the corresponding bucket's id for a + * given bucket name + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_get_bucket_id(cli_api_t *cli_api); + +/** + * @brief Function to list files in a given bucket name + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_list_files(cli_api_t *cli_api); + +/** + * @brief Function to remove a given bucket name + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_remove_bucket(cli_api_t *cli_api); + +/** + * @brief Function to remove a file from a given bucket name + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_remove_file(cli_api_t *cli_api); + +/** + * @brief Function to return the node IDs for a given file for a + * given bucket name + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_list_mirrors(cli_api_t *cli_api); + +/** + * @brief Function to upload a local file into a given bucket + * name + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_upload_file(cli_api_t *cli_api); + +/** + * @brief Function to upload local files into a given bucket + * name + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_upload_files(cli_api_t *cli_api); + +/** + * @brief Function to download a file from a given bucket to a + * local folder + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_download_file(cli_api_t *cli_api); + +/** + * @brief Function to download files from a given bucket to a + * local folder + * + * @param[in] cli_api_t structure that passes user's input + * info + * @return A non-zero error value on failure and 0 on success. + */ +int cli_download_files(cli_api_t *cli_api); + +#ifdef __cplusplus +} +#endif + +#endif /* CLI_CALLBACK_H */ diff --git a/src/storj.c b/src/storj.c index 5e4f8f8..5b65a1e 100644 --- a/src/storj.c +++ b/src/storj.c @@ -1756,4 +1756,4 @@ STORJ_API int storj_bridge_register(storj_env_t *env, return STORJ_MEMORY_ERROR; } return uv_queue_work(env->loop, (uv_work_t*) work, json_request_worker, cb); -} +} \ No newline at end of file