|
@@ -1,7 +1,73 @@
|
|
|
|
|
+// flip_store_github.c
|
|
|
#include <github/flip_store_github.h>
|
|
#include <github/flip_store_github.h>
|
|
|
#include <flip_storage/flip_store_storage.h>
|
|
#include <flip_storage/flip_store_storage.h>
|
|
|
|
|
+#include <stdio.h>
|
|
|
|
|
+#include <string.h>
|
|
|
|
|
+#include <stdbool.h>
|
|
|
|
|
+
|
|
|
|
|
+#define MAX_RECURSION_DEPTH 5 // maximum allowed "/" characters in repo path
|
|
|
|
|
+#define MAX_PENDING_FOLDERS 20 // maximum number of folders to process iteratively
|
|
|
|
|
+
|
|
|
|
|
+// --- Pending Folder Queue for iterative folder processing ---
|
|
|
|
|
+typedef struct
|
|
|
|
|
+{
|
|
|
|
|
+ char file_path[256]; // Folder JSON file path (downloaded folder info)
|
|
|
|
|
+ char repo[256]; // New repository path, e.g. "repo/subfolder"
|
|
|
|
|
+} PendingFolder;
|
|
|
|
|
+
|
|
|
|
|
+static PendingFolder pendingFolders[MAX_PENDING_FOLDERS];
|
|
|
|
|
+static int pendingFoldersCount = 0;
|
|
|
|
|
+
|
|
|
|
|
+static int count_char(const char *s, char c)
|
|
|
|
|
+{
|
|
|
|
|
+ int count = 0;
|
|
|
|
|
+ for (; *s; s++)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (*s == c)
|
|
|
|
|
+ {
|
|
|
|
|
+ count++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return count;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Enqueue a folder for later processing.
|
|
|
|
|
+// currentRepo is the repository path received so far (e.g. "repo" at top level)
|
|
|
|
|
+// folderName is the name of the folder (e.g. "alloc") that is appended.
|
|
|
|
|
+static bool enqueue_folder(const char *file_path, const char *currentRepo, const char *folderName)
|
|
|
|
|
+{
|
|
|
|
|
+ if (pendingFoldersCount >= MAX_PENDING_FOLDERS)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Pending folder queue full!");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ PendingFolder *pf = &pendingFolders[pendingFoldersCount++];
|
|
|
|
|
+ strncpy(pf->file_path, file_path, sizeof(pf->file_path) - 1);
|
|
|
|
|
+ pf->file_path[sizeof(pf->file_path) - 1] = '\0';
|
|
|
|
|
+ // New repo path = currentRepo + "/" + folderName.
|
|
|
|
|
+ snprintf(pf->repo, sizeof(pf->repo), "%s/%s", currentRepo, folderName);
|
|
|
|
|
+ FURI_LOG_I(TAG, "Enqueued folder: %s (file: %s)", pf->repo, pf->file_path);
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Process all enqueued folders iteratively.
|
|
|
|
|
+static void process_pending_folders(const char *author)
|
|
|
|
|
+{
|
|
|
|
|
+ int i = 0;
|
|
|
|
|
+ while (i < pendingFoldersCount)
|
|
|
|
|
+ {
|
|
|
|
|
+ PendingFolder pf = pendingFolders[i];
|
|
|
|
|
+ FURI_LOG_I(TAG, "Processing pending folder: %s", pf.repo);
|
|
|
|
|
+ if (!flip_store_parse_github_contents(pf.file_path, author, pf.repo))
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to process pending folder: %s", pf.repo);
|
|
|
|
|
+ }
|
|
|
|
|
+ i++;
|
|
|
|
|
+ }
|
|
|
|
|
+ pendingFoldersCount = 0; // Reset queue after processing.
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-// Helper to download a file from Github and save it to the storage
|
|
|
|
|
|
|
+// Helper to download a file from Github and save it to storage.
|
|
|
bool flip_store_download_github_file(
|
|
bool flip_store_download_github_file(
|
|
|
FlipperHTTP *fhttp,
|
|
FlipperHTTP *fhttp,
|
|
|
const char *filename,
|
|
const char *filename,
|
|
@@ -14,62 +80,79 @@ bool flip_store_download_github_file(
|
|
|
FURI_LOG_E(TAG, "Invalid arguments.");
|
|
FURI_LOG_E(TAG, "Invalid arguments.");
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s/%s.txt", author, repo, filename);
|
|
|
|
|
|
|
+ snprintf(fhttp->file_path, sizeof(fhttp->file_path),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s/%s.txt",
|
|
|
|
|
+ author, repo, filename);
|
|
|
fhttp->state = IDLE;
|
|
fhttp->state = IDLE;
|
|
|
fhttp->save_received_data = false;
|
|
fhttp->save_received_data = false;
|
|
|
fhttp->is_bytes_request = true;
|
|
fhttp->is_bytes_request = true;
|
|
|
|
|
|
|
|
- return flipper_http_get_request_bytes(fhttp, link, "{\"Content-Type\":\"application/octet-stream\"}");
|
|
|
|
|
|
|
+ // return flipper_http_get_request_bytes(fhttp, link, "{\"Content-Type\":\"application/octet-stream\"}");
|
|
|
|
|
+ return flipper_http_request(fhttp, BYTES, link, "{\"Content-Type\":\"application/octet-stream\"}", NULL);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bool flip_store_get_github_contents(FlipperHTTP *fhttp, const char *author, const char *repo)
|
|
|
|
|
|
|
+static bool save_directory(const char *dir)
|
|
|
{
|
|
{
|
|
|
- // Create Initial directory
|
|
|
|
|
Storage *storage = furi_record_open(RECORD_STORAGE);
|
|
Storage *storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
+ if (!storage_common_exists(storage, dir) && storage_common_mkdir(storage, dir) != FSE_OK)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to create directory %s", dir);
|
|
|
|
|
+ furi_record_close(RECORD_STORAGE);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ furi_record_close(RECORD_STORAGE);
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
+bool flip_store_get_github_contents(FlipperHTTP *fhttp, const char *author, const char *repo)
|
|
|
|
|
+{
|
|
|
|
|
+ // Create Initial directories
|
|
|
char dir[256];
|
|
char dir[256];
|
|
|
|
|
|
|
|
// create a data directory: /ext/apps_data/flip_store/data
|
|
// create a data directory: /ext/apps_data/flip_store/data
|
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data");
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data");
|
|
|
- storage_common_mkdir(storage, dir);
|
|
|
|
|
|
|
+ save_directory(STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data");
|
|
|
|
|
|
|
|
// create a data directory for the author: /ext/apps_data/flip_store/data/author
|
|
// create a data directory for the author: /ext/apps_data/flip_store/data/author
|
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s", author);
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s", author);
|
|
|
- storage_common_mkdir(storage, dir);
|
|
|
|
|
|
|
+ save_directory(dir);
|
|
|
|
|
|
|
|
- // example path: /ext/apps_data/flip_store/data/author/info.json
|
|
|
|
|
- snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/info.json", author);
|
|
|
|
|
|
|
+ // info path: /ext/apps_data/flip_store/data/author/info.json
|
|
|
|
|
+ snprintf(fhttp->file_path, sizeof(fhttp->file_path),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/info.json",
|
|
|
|
|
+ author);
|
|
|
|
|
|
|
|
// create a data directory for the repo: /ext/apps_data/flip_store/data/author/repo
|
|
// create a data directory for the repo: /ext/apps_data/flip_store/data/author/repo
|
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s", author, repo);
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s", author, repo);
|
|
|
- storage_common_mkdir(storage, dir);
|
|
|
|
|
|
|
+ save_directory(dir);
|
|
|
|
|
|
|
|
- // example path: /ext/apps_data/flip_store/author
|
|
|
|
|
|
|
+ // create author folder: /ext/apps_data/flip_store/author
|
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s", author);
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s", author);
|
|
|
- storage_common_mkdir(storage, dir);
|
|
|
|
|
|
|
+ save_directory(dir);
|
|
|
|
|
|
|
|
- // example path: /ext/apps_data/flip_store/author/repo
|
|
|
|
|
|
|
+ // create repo folder: /ext/apps_data/flip_store/author/repo
|
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s", author, repo);
|
|
snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s", author, repo);
|
|
|
- storage_common_mkdir(storage, dir);
|
|
|
|
|
-
|
|
|
|
|
- furi_record_close(RECORD_STORAGE);
|
|
|
|
|
|
|
+ save_directory(dir);
|
|
|
|
|
|
|
|
// get the contents of the repo
|
|
// get the contents of the repo
|
|
|
char link[256];
|
|
char link[256];
|
|
|
snprintf(link, sizeof(link), "https://api.github.com/repos/%s/%s/contents", author, repo);
|
|
snprintf(link, sizeof(link), "https://api.github.com/repos/%s/%s/contents", author, repo);
|
|
|
fhttp->save_received_data = true;
|
|
fhttp->save_received_data = true;
|
|
|
- return flipper_http_get_request_with_headers(fhttp, link, "{\"Content-Type\":\"application/json\"}");
|
|
|
|
|
|
|
+ // return flipper_http_get_request_with_headers(fhttp, link, "{\"Content-Type\":\"application/json\"}");
|
|
|
|
|
+ return flipper_http_request(fhttp, GET, link, "{\"Content-Type\":\"application/json\"}", NULL);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-#include <stdio.h>
|
|
|
|
|
-#include <string.h>
|
|
|
|
|
-#include <stdbool.h>
|
|
|
|
|
-
|
|
|
|
|
-// Assuming necessary headers and definitions for FuriString, FURI_LOG, etc., are included.
|
|
|
|
|
-
|
|
|
|
|
|
|
+// Recursively (but now iteratively, via queue) parse GitHub contents.
|
|
|
|
|
+// 'repo' is the repository path relative to the author’s root.
|
|
|
bool flip_store_parse_github_contents(char *file_path, const char *author, const char *repo)
|
|
bool flip_store_parse_github_contents(char *file_path, const char *author, const char *repo)
|
|
|
{
|
|
{
|
|
|
|
|
+ // Check recursion depth by counting '/' characters.
|
|
|
|
|
+ if (count_char(repo, '/') >= MAX_RECURSION_DEPTH)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_I(TAG, "Max recursion depth reached for repo path: %s", repo);
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
FURI_LOG_I(TAG, "Parsing Github contents from %s - %s.", author, repo);
|
|
FURI_LOG_I(TAG, "Parsing Github contents from %s - %s.", author, repo);
|
|
|
if (!file_path || !author || !repo)
|
|
if (!file_path || !author || !repo)
|
|
|
{
|
|
{
|
|
@@ -77,7 +160,7 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Load JSON data from file
|
|
|
|
|
|
|
+ // Load JSON data from file.
|
|
|
FuriString *git_data = flipper_http_load_from_file(file_path);
|
|
FuriString *git_data = flipper_http_load_from_file(file_path);
|
|
|
if (git_data == NULL)
|
|
if (git_data == NULL)
|
|
|
{
|
|
{
|
|
@@ -85,7 +168,7 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Allocate a new FuriString to hold the entire JSON structure
|
|
|
|
|
|
|
+ // Wrap the JSON array in an object for easier parsing.
|
|
|
FuriString *git_data_str = furi_string_alloc();
|
|
FuriString *git_data_str = furi_string_alloc();
|
|
|
if (!git_data_str)
|
|
if (!git_data_str)
|
|
|
{
|
|
{
|
|
@@ -93,16 +176,13 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
furi_string_free(git_data);
|
|
furi_string_free(git_data);
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // Construct the full JSON string
|
|
|
|
|
furi_string_cat_str(git_data_str, "{\"json_data\":");
|
|
furi_string_cat_str(git_data_str, "{\"json_data\":");
|
|
|
furi_string_cat(git_data_str, git_data);
|
|
furi_string_cat(git_data_str, git_data);
|
|
|
furi_string_cat_str(git_data_str, "}");
|
|
furi_string_cat_str(git_data_str, "}");
|
|
|
- furi_string_free(git_data); // Free the original git_data as it's now part of git_data_str
|
|
|
|
|
|
|
+ furi_string_free(git_data);
|
|
|
|
|
|
|
|
- // Check available memory
|
|
|
|
|
- const size_t additional_bytes = strlen("{\"json_data\":") + 1; // +1 for the closing "}"
|
|
|
|
|
- if (memmgr_get_free_heap() < furi_string_size(git_data_str) + additional_bytes)
|
|
|
|
|
|
|
+ const size_t additional_bytes = strlen("{\"json_data\":") + 1;
|
|
|
|
|
+ if (memmgr_heap_get_max_free_block() < furi_string_size(git_data_str) + additional_bytes)
|
|
|
{
|
|
{
|
|
|
FURI_LOG_E(TAG, "Not enough memory to allocate git_data_str.");
|
|
FURI_LOG_E(TAG, "Not enough memory to allocate git_data_str.");
|
|
|
furi_string_free(git_data_str);
|
|
furi_string_free(git_data_str);
|
|
@@ -110,16 +190,14 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int file_count = 0;
|
|
int file_count = 0;
|
|
|
- char dir[512]; // Increased size to accommodate longer paths if necessary
|
|
|
|
|
- FURI_LOG_I(TAG, "Looping through Github files.");
|
|
|
|
|
- FURI_LOG_I(TAG, "Available memory: %d bytes", memmgr_get_free_heap());
|
|
|
|
|
|
|
+ int folder_count = 0;
|
|
|
|
|
+ char dir[256];
|
|
|
|
|
+
|
|
|
|
|
+ FURI_LOG_I(TAG, "Looping through Github files/folders. Available memory: %d bytes", memmgr_heap_get_max_free_block());
|
|
|
|
|
|
|
|
- // Get the C-string and its length for processing
|
|
|
|
|
char *data = (char *)furi_string_get_cstr(git_data_str);
|
|
char *data = (char *)furi_string_get_cstr(git_data_str);
|
|
|
size_t data_len = furi_string_size(git_data_str);
|
|
size_t data_len = furi_string_size(git_data_str);
|
|
|
-
|
|
|
|
|
- size_t pos = 0; // Current position in the data string
|
|
|
|
|
- // Locate the start of the JSON array
|
|
|
|
|
|
|
+ size_t pos = 0;
|
|
|
char *array_start = strchr(data, '[');
|
|
char *array_start = strchr(data, '[');
|
|
|
if (!array_start)
|
|
if (!array_start)
|
|
|
{
|
|
{
|
|
@@ -127,28 +205,25 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
furi_string_free(git_data_str);
|
|
furi_string_free(git_data_str);
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
- pos = array_start - data; // Update position to the start of the array
|
|
|
|
|
|
|
+ pos = array_start - data;
|
|
|
size_t brace_count = 0;
|
|
size_t brace_count = 0;
|
|
|
size_t obj_start = 0;
|
|
size_t obj_start = 0;
|
|
|
- bool in_string = false; // To handle braces inside strings
|
|
|
|
|
|
|
+ bool in_string = false;
|
|
|
|
|
|
|
|
while (pos < data_len && file_count < MAX_GITHUB_FILES)
|
|
while (pos < data_len && file_count < MAX_GITHUB_FILES)
|
|
|
{
|
|
{
|
|
|
char current = data[pos];
|
|
char current = data[pos];
|
|
|
-
|
|
|
|
|
- // Toggle in_string flag if a quote is found (handling escaped quotes)
|
|
|
|
|
if (current == '"' && (pos == 0 || data[pos - 1] != '\\'))
|
|
if (current == '"' && (pos == 0 || data[pos - 1] != '\\'))
|
|
|
{
|
|
{
|
|
|
in_string = !in_string;
|
|
in_string = !in_string;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
if (!in_string)
|
|
if (!in_string)
|
|
|
{
|
|
{
|
|
|
if (current == '{')
|
|
if (current == '{')
|
|
|
{
|
|
{
|
|
|
if (brace_count == 0)
|
|
if (brace_count == 0)
|
|
|
{
|
|
{
|
|
|
- obj_start = pos; // Potential start of a JSON object
|
|
|
|
|
|
|
+ obj_start = pos;
|
|
|
}
|
|
}
|
|
|
brace_count++;
|
|
brace_count++;
|
|
|
}
|
|
}
|
|
@@ -159,7 +234,6 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
{
|
|
{
|
|
|
size_t obj_end = pos;
|
|
size_t obj_end = pos;
|
|
|
size_t obj_length = obj_end - obj_start + 1;
|
|
size_t obj_length = obj_end - obj_start + 1;
|
|
|
- // Extract the JSON object substring
|
|
|
|
|
char *obj_str = malloc(obj_length + 1);
|
|
char *obj_str = malloc(obj_length + 1);
|
|
|
if (!obj_str)
|
|
if (!obj_str)
|
|
|
{
|
|
{
|
|
@@ -167,21 +241,19 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
strncpy(obj_str, data + obj_start, obj_length);
|
|
strncpy(obj_str, data + obj_start, obj_length);
|
|
|
- obj_str[obj_length] = '\0'; // Null-terminate
|
|
|
|
|
|
|
+ obj_str[obj_length] = '\0';
|
|
|
|
|
|
|
|
FuriString *json_data_array = furi_string_alloc();
|
|
FuriString *json_data_array = furi_string_alloc();
|
|
|
- furi_string_set(json_data_array, obj_str); // Set the string to the allocated memory
|
|
|
|
|
- free(obj_str); // Free the temporary C-string
|
|
|
|
|
-
|
|
|
|
|
|
|
+ furi_string_set(json_data_array, obj_str);
|
|
|
|
|
+ free(obj_str);
|
|
|
if (!json_data_array)
|
|
if (!json_data_array)
|
|
|
{
|
|
{
|
|
|
FURI_LOG_E(TAG, "Failed to initialize json_data_array.");
|
|
FURI_LOG_E(TAG, "Failed to initialize json_data_array.");
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- FURI_LOG_I(TAG, "Loaded json data array value %d. Available memory: %d bytes", file_count, memmgr_get_free_heap());
|
|
|
|
|
-
|
|
|
|
|
- // Extract "type" field
|
|
|
|
|
|
|
+ FURI_LOG_I(TAG, "Loaded json data object #%d. Available memory: %d bytes",
|
|
|
|
|
+ file_count, memmgr_heap_get_max_free_block());
|
|
|
FuriString *type = get_json_value_furi("type", json_data_array);
|
|
FuriString *type = get_json_value_furi("type", json_data_array);
|
|
|
if (!type)
|
|
if (!type)
|
|
|
{
|
|
{
|
|
@@ -189,18 +261,94 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
furi_string_free(json_data_array);
|
|
furi_string_free(json_data_array);
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // Skip non-file types (e.g., directories)
|
|
|
|
|
|
|
+ // If not a file, assume it is a folder.
|
|
|
if (strcmp(furi_string_get_cstr(type), "file") != 0)
|
|
if (strcmp(furi_string_get_cstr(type), "file") != 0)
|
|
|
{
|
|
{
|
|
|
|
|
+ FuriString *name = get_json_value_furi("name", json_data_array);
|
|
|
|
|
+ if (!name)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to get name.");
|
|
|
|
|
+ furi_string_free(type);
|
|
|
|
|
+ furi_string_free(json_data_array);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ // skip undesired folders (e.g. ".git").
|
|
|
|
|
+ if (strcmp(furi_string_get_cstr(name), ".git") == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_I(TAG, "Skipping folder %s.", furi_string_get_cstr(name));
|
|
|
|
|
+ furi_string_free(name);
|
|
|
|
|
+ furi_string_free(type);
|
|
|
|
|
+ furi_string_free(json_data_array);
|
|
|
|
|
+ size_t remaining_length = data_len - (obj_end + 1);
|
|
|
|
|
+ memmove(data + obj_start, data + obj_end + 1, remaining_length + 1);
|
|
|
|
|
+ data_len -= (obj_end + 1 - obj_start);
|
|
|
|
|
+ pos = obj_start;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ FuriString *url = get_json_value_furi("url", json_data_array);
|
|
|
|
|
+ if (!url)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to get url.");
|
|
|
|
|
+ furi_string_free(name);
|
|
|
|
|
+ furi_string_free(type);
|
|
|
|
|
+ furi_string_free(json_data_array);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Create the folder on storage.
|
|
|
|
|
+ snprintf(dir, sizeof(dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s/%s",
|
|
|
|
|
+ author, repo, furi_string_get_cstr(name));
|
|
|
|
|
+ save_directory(dir);
|
|
|
|
|
+
|
|
|
|
|
+ snprintf(dir, sizeof(dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/%s",
|
|
|
|
|
+ author, repo, furi_string_get_cstr(name));
|
|
|
|
|
+ save_directory(dir);
|
|
|
|
|
+
|
|
|
|
|
+ // Save folder JSON for later downloading its contents.
|
|
|
|
|
+ snprintf(dir, sizeof(dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/folder%d.json",
|
|
|
|
|
+ author, repo, folder_count);
|
|
|
|
|
+ FuriString *json = furi_string_alloc();
|
|
|
|
|
+ if (!json)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to allocate json.");
|
|
|
|
|
+ furi_string_free(name);
|
|
|
|
|
+ furi_string_free(url);
|
|
|
|
|
+ furi_string_free(type);
|
|
|
|
|
+ furi_string_free(json_data_array);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ furi_string_cat_str(json, "{\"name\":\"");
|
|
|
|
|
+ furi_string_cat(json, name);
|
|
|
|
|
+ furi_string_cat_str(json, "\",\"link\":\"");
|
|
|
|
|
+ furi_string_cat(json, url);
|
|
|
|
|
+ furi_string_cat_str(json, "\"}");
|
|
|
|
|
+
|
|
|
|
|
+ if (!save_char_with_path(dir, furi_string_get_cstr(json)))
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to save folder json.");
|
|
|
|
|
+ }
|
|
|
|
|
+ FURI_LOG_I(TAG, "Saved folder %s.", furi_string_get_cstr(name));
|
|
|
|
|
+ // Enqueue the folder instead of recursing.
|
|
|
|
|
+ enqueue_folder(dir, repo, furi_string_get_cstr(name));
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(name);
|
|
|
|
|
+ furi_string_free(url);
|
|
|
furi_string_free(type);
|
|
furi_string_free(type);
|
|
|
|
|
+ furi_string_free(json);
|
|
|
furi_string_free(json_data_array);
|
|
furi_string_free(json_data_array);
|
|
|
- pos = obj_end + 1; // Move past this object
|
|
|
|
|
|
|
+ folder_count++;
|
|
|
|
|
+
|
|
|
|
|
+ size_t remaining_length = data_len - (obj_end + 1);
|
|
|
|
|
+ memmove(data + obj_start, data + obj_end + 1, remaining_length + 1);
|
|
|
|
|
+ data_len -= (obj_end + 1 - obj_start);
|
|
|
|
|
+ pos = obj_start;
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
furi_string_free(type);
|
|
furi_string_free(type);
|
|
|
|
|
|
|
|
- // Extract "download_url" and "name"
|
|
|
|
|
|
|
+ // Process file: extract download_url and name.
|
|
|
FuriString *download_url = get_json_value_furi("download_url", json_data_array);
|
|
FuriString *download_url = get_json_value_furi("download_url", json_data_array);
|
|
|
if (!download_url)
|
|
if (!download_url)
|
|
|
{
|
|
{
|
|
@@ -208,7 +356,6 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
furi_string_free(json_data_array);
|
|
furi_string_free(json_data_array);
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
FuriString *name = get_json_value_furi("name", json_data_array);
|
|
FuriString *name = get_json_value_furi("name", json_data_array);
|
|
|
if (!name)
|
|
if (!name)
|
|
|
{
|
|
{
|
|
@@ -217,11 +364,11 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
furi_string_free(download_url);
|
|
furi_string_free(download_url);
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
furi_string_free(json_data_array);
|
|
furi_string_free(json_data_array);
|
|
|
- FURI_LOG_I(TAG, "Received name and download_url. Available memory: %d bytes", memmgr_get_free_heap());
|
|
|
|
|
|
|
|
|
|
- // Create JSON to save
|
|
|
|
|
|
|
+ FURI_LOG_I(TAG, "Received file %s and download_url. Available memory: %d bytes",
|
|
|
|
|
+ furi_string_get_cstr(name), memmgr_heap_get_max_free_block());
|
|
|
|
|
+
|
|
|
FuriString *json = furi_string_alloc();
|
|
FuriString *json = furi_string_alloc();
|
|
|
if (!json)
|
|
if (!json)
|
|
|
{
|
|
{
|
|
@@ -230,54 +377,67 @@ bool flip_store_parse_github_contents(char *file_path, const char *author, const
|
|
|
furi_string_free(name);
|
|
furi_string_free(name);
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
furi_string_cat_str(json, "{\"name\":\"");
|
|
furi_string_cat_str(json, "{\"name\":\"");
|
|
|
furi_string_cat(json, name);
|
|
furi_string_cat(json, name);
|
|
|
furi_string_cat_str(json, "\",\"link\":\"");
|
|
furi_string_cat_str(json, "\",\"link\":\"");
|
|
|
furi_string_cat(json, download_url);
|
|
furi_string_cat(json, download_url);
|
|
|
furi_string_cat_str(json, "\"}");
|
|
furi_string_cat_str(json, "\"}");
|
|
|
|
|
|
|
|
- FURI_LOG_I(TAG, "Created json. Available memory: %d bytes", memmgr_get_free_heap());
|
|
|
|
|
-
|
|
|
|
|
- // Save the JSON to the data folder: /ext/apps_data/flip_store/data/author/repo/fileX.json
|
|
|
|
|
- snprintf(dir, sizeof(dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/file%d.json", author, repo, file_count);
|
|
|
|
|
|
|
+ snprintf(dir, sizeof(dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/file%d.json",
|
|
|
|
|
+ author, repo, file_count);
|
|
|
if (!save_char_with_path(dir, furi_string_get_cstr(json)))
|
|
if (!save_char_with_path(dir, furi_string_get_cstr(json)))
|
|
|
{
|
|
{
|
|
|
- FURI_LOG_E(TAG, "Failed to save json.");
|
|
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to save file json.");
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
FURI_LOG_I(TAG, "Saved file %s.", furi_string_get_cstr(name));
|
|
FURI_LOG_I(TAG, "Saved file %s.", furi_string_get_cstr(name));
|
|
|
|
|
|
|
|
- // Free allocated resources
|
|
|
|
|
furi_string_free(name);
|
|
furi_string_free(name);
|
|
|
furi_string_free(download_url);
|
|
furi_string_free(download_url);
|
|
|
furi_string_free(json);
|
|
furi_string_free(json);
|
|
|
-
|
|
|
|
|
file_count++;
|
|
file_count++;
|
|
|
|
|
|
|
|
- // This can be expensive for large strings; consider memory constraints
|
|
|
|
|
size_t remaining_length = data_len - (obj_end + 1);
|
|
size_t remaining_length = data_len - (obj_end + 1);
|
|
|
- memmove(data + obj_start, data + obj_end + 1, remaining_length + 1); // +1 to include null terminator
|
|
|
|
|
|
|
+ memmove(data + obj_start, data + obj_end + 1, remaining_length + 1);
|
|
|
data_len -= (obj_end + 1 - obj_start);
|
|
data_len -= (obj_end + 1 - obj_start);
|
|
|
- pos = obj_start; // Reset position to the start of the modified string
|
|
|
|
|
|
|
+ pos = obj_start;
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
pos++;
|
|
pos++;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- furi_string_free(git_data_str);
|
|
|
|
|
-
|
|
|
|
|
- // Save file count
|
|
|
|
|
|
|
+ // Save file count.
|
|
|
char file_count_str[16];
|
|
char file_count_str[16];
|
|
|
snprintf(file_count_str, sizeof(file_count_str), "%d", file_count);
|
|
snprintf(file_count_str, sizeof(file_count_str), "%d", file_count);
|
|
|
- char file_count_dir[512]; // Increased size for longer paths
|
|
|
|
|
- snprintf(file_count_dir, sizeof(file_count_dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/file_count.txt", author);
|
|
|
|
|
|
|
+ char file_count_dir[256];
|
|
|
|
|
+ snprintf(file_count_dir, sizeof(file_count_dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/file_count.txt", author);
|
|
|
|
|
+ if (!save_char_with_path(file_count_dir, file_count_str))
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to save file count.");
|
|
|
|
|
+ furi_string_free(git_data_str);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- FURI_LOG_I(TAG, "Successfully parsed %d files.", file_count);
|
|
|
|
|
- return save_char_with_path(file_count_dir, file_count_str);
|
|
|
|
|
|
|
+ // Save folder count.
|
|
|
|
|
+ char folder_count_str[16];
|
|
|
|
|
+ snprintf(folder_count_str, sizeof(folder_count_str), "%d", folder_count);
|
|
|
|
|
+ char folder_count_dir[256];
|
|
|
|
|
+ snprintf(folder_count_dir, sizeof(folder_count_dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/folder_count.txt",
|
|
|
|
|
+ author, repo);
|
|
|
|
|
+ if (!save_char_with_path(folder_count_dir, folder_count_str))
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to save folder count.");
|
|
|
|
|
+ furi_string_free(git_data_str);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ furi_string_free(git_data_str);
|
|
|
|
|
+ FURI_LOG_I(TAG, "Successfully parsed %d files and %d folders.", file_count, folder_count);
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool flip_store_install_all_github_files(FlipperHTTP *fhttp, const char *author, const char *repo)
|
|
bool flip_store_install_all_github_files(FlipperHTTP *fhttp, const char *author, const char *repo)
|
|
@@ -289,9 +449,11 @@ bool flip_store_install_all_github_files(FlipperHTTP *fhttp, const char *author,
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
fhttp->state = RECEIVING;
|
|
fhttp->state = RECEIVING;
|
|
|
- // get the file count
|
|
|
|
|
- char file_count_dir[256]; // /ext/apps_data/flip_store/data/author/file_count.txt
|
|
|
|
|
- snprintf(file_count_dir, sizeof(file_count_dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/file_count.txt", author);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // --- Install files first ---
|
|
|
|
|
+ char file_count_dir[256];
|
|
|
|
|
+ snprintf(file_count_dir, sizeof(file_count_dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/file_count.txt", author);
|
|
|
FuriString *file_count = flipper_http_load_from_file(file_count_dir);
|
|
FuriString *file_count = flipper_http_load_from_file(file_count_dir);
|
|
|
if (file_count == NULL)
|
|
if (file_count == NULL)
|
|
|
{
|
|
{
|
|
@@ -301,20 +463,22 @@ bool flip_store_install_all_github_files(FlipperHTTP *fhttp, const char *author,
|
|
|
int count = atoi(furi_string_get_cstr(file_count));
|
|
int count = atoi(furi_string_get_cstr(file_count));
|
|
|
furi_string_free(file_count);
|
|
furi_string_free(file_count);
|
|
|
|
|
|
|
|
- // install all files
|
|
|
|
|
- char file_dir[256]; // /ext/apps_data/flip_store/data/author/repo/file.json
|
|
|
|
|
|
|
+ char file_dir[256];
|
|
|
FURI_LOG_I(TAG, "Installing %d files.", count);
|
|
FURI_LOG_I(TAG, "Installing %d files.", count);
|
|
|
for (int i = 0; i < count; i++)
|
|
for (int i = 0; i < count; i++)
|
|
|
{
|
|
{
|
|
|
- snprintf(file_dir, sizeof(file_dir), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/file%d.json", author, repo, i);
|
|
|
|
|
- FURI_LOG_I(TAG, "Loading file %s. Available memory: %d bytes", file_dir, memmgr_get_free_heap());
|
|
|
|
|
- FuriString *file = flipper_http_load_from_file_with_limit(file_dir, 512);
|
|
|
|
|
|
|
+ snprintf(file_dir, sizeof(file_dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/file%d.json",
|
|
|
|
|
+ author, repo, i);
|
|
|
|
|
+
|
|
|
|
|
+ FuriString *file = flipper_http_load_from_file(file_dir);
|
|
|
if (!file)
|
|
if (!file)
|
|
|
{
|
|
{
|
|
|
FURI_LOG_E(TAG, "Failed to load file.");
|
|
FURI_LOG_E(TAG, "Failed to load file.");
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
FURI_LOG_I(TAG, "Loaded file %s.", file_dir);
|
|
FURI_LOG_I(TAG, "Loaded file %s.", file_dir);
|
|
|
|
|
+
|
|
|
FuriString *name = get_json_value_furi("name", file);
|
|
FuriString *name = get_json_value_furi("name", file);
|
|
|
if (!name)
|
|
if (!name)
|
|
|
{
|
|
{
|
|
@@ -332,37 +496,53 @@ bool flip_store_install_all_github_files(FlipperHTTP *fhttp, const char *author,
|
|
|
}
|
|
}
|
|
|
furi_string_free(file);
|
|
furi_string_free(file);
|
|
|
|
|
|
|
|
- bool fetch_file()
|
|
|
|
|
- {
|
|
|
|
|
- return flip_store_download_github_file(fhttp, furi_string_get_cstr(name), author, repo, furi_string_get_cstr(link));
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ FURI_LOG_I(TAG, "Downloading file %s", furi_string_get_cstr(name));
|
|
|
|
|
|
|
|
- bool parse()
|
|
|
|
|
|
|
+ // fetch_file callback
|
|
|
|
|
+ bool fetch_file_result = false;
|
|
|
{
|
|
{
|
|
|
- // remove .txt from the filename
|
|
|
|
|
- char current_file_path[512];
|
|
|
|
|
- char new_file_path[512];
|
|
|
|
|
- snprintf(current_file_path, sizeof(current_file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s/%s.txt", author, repo, furi_string_get_cstr(name));
|
|
|
|
|
- snprintf(new_file_path, sizeof(new_file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s/%s", author, repo, furi_string_get_cstr(name));
|
|
|
|
|
- Storage *storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
- if (!storage_file_exists(storage, current_file_path))
|
|
|
|
|
|
|
+ bool fetch_file()
|
|
|
{
|
|
{
|
|
|
- FURI_LOG_E(TAG, "Failed to download file.");
|
|
|
|
|
- furi_record_close(RECORD_STORAGE);
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ return flip_store_download_github_file(
|
|
|
|
|
+ fhttp,
|
|
|
|
|
+ furi_string_get_cstr(name),
|
|
|
|
|
+ author,
|
|
|
|
|
+ repo,
|
|
|
|
|
+ furi_string_get_cstr(link));
|
|
|
}
|
|
}
|
|
|
- if (storage_common_rename(storage, current_file_path, new_file_path) != FSE_OK)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // parse_file callback
|
|
|
|
|
+ bool parse_file()
|
|
|
{
|
|
{
|
|
|
- FURI_LOG_E(TAG, "Failed to rename file.");
|
|
|
|
|
|
|
+ char current_file_path[256];
|
|
|
|
|
+ char new_file_path[256];
|
|
|
|
|
+ snprintf(current_file_path, sizeof(current_file_path),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s/%s.txt",
|
|
|
|
|
+ author, repo, furi_string_get_cstr(name));
|
|
|
|
|
+ snprintf(new_file_path, sizeof(new_file_path),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/%s/%s/%s",
|
|
|
|
|
+ author, repo, furi_string_get_cstr(name));
|
|
|
|
|
+ Storage *storage = furi_record_open(RECORD_STORAGE);
|
|
|
|
|
+ if (!storage_file_exists(storage, current_file_path))
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to download file.");
|
|
|
|
|
+ furi_record_close(RECORD_STORAGE);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (storage_common_rename(storage, current_file_path, new_file_path) != FSE_OK)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to rename file.");
|
|
|
|
|
+ furi_record_close(RECORD_STORAGE);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
furi_record_close(RECORD_STORAGE);
|
|
furi_record_close(RECORD_STORAGE);
|
|
|
- return false;
|
|
|
|
|
|
|
+ return true;
|
|
|
}
|
|
}
|
|
|
- furi_record_close(RECORD_STORAGE);
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ fetch_file_result = flipper_http_process_response_async(fhttp, fetch_file, parse_file);
|
|
|
}
|
|
}
|
|
|
- // download the file and wait until it is downloaded
|
|
|
|
|
- FURI_LOG_I(TAG, "Downloading file %s", furi_string_get_cstr(name));
|
|
|
|
|
- if (!flipper_http_process_response_async(fhttp, fetch_file, parse))
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if (!fetch_file_result)
|
|
|
{
|
|
{
|
|
|
FURI_LOG_E(TAG, "Failed to download file.");
|
|
FURI_LOG_E(TAG, "Failed to download file.");
|
|
|
furi_string_free(name);
|
|
furi_string_free(name);
|
|
@@ -374,5 +554,95 @@ bool flip_store_install_all_github_files(FlipperHTTP *fhttp, const char *author,
|
|
|
furi_string_free(link);
|
|
furi_string_free(link);
|
|
|
}
|
|
}
|
|
|
fhttp->state = IDLE;
|
|
fhttp->state = IDLE;
|
|
|
|
|
+
|
|
|
|
|
+ // --- Now install folders ---
|
|
|
|
|
+ char folder_count_dir[256];
|
|
|
|
|
+ snprintf(folder_count_dir, sizeof(folder_count_dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/folder_count.txt",
|
|
|
|
|
+ author, repo);
|
|
|
|
|
+ FuriString *folder_count = flipper_http_load_from_file(folder_count_dir);
|
|
|
|
|
+ if (folder_count == NULL)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to load folder count.");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ count = atoi(furi_string_get_cstr(folder_count));
|
|
|
|
|
+ furi_string_free(folder_count);
|
|
|
|
|
+
|
|
|
|
|
+ char folder_dir[256];
|
|
|
|
|
+ FURI_LOG_I(TAG, "Installing %d folders.", count);
|
|
|
|
|
+ for (int i = 0; i < count; i++)
|
|
|
|
|
+ {
|
|
|
|
|
+ snprintf(folder_dir, sizeof(folder_dir),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/folder%d.json",
|
|
|
|
|
+ author, repo, i);
|
|
|
|
|
+
|
|
|
|
|
+ FuriString *folder = flipper_http_load_from_file(folder_dir);
|
|
|
|
|
+ if (!folder)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to load folder.");
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ FURI_LOG_I(TAG, "Loaded folder %s.", folder_dir);
|
|
|
|
|
+
|
|
|
|
|
+ FuriString *name = get_json_value_furi("name", folder);
|
|
|
|
|
+ if (!name)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to get name.");
|
|
|
|
|
+ furi_string_free(folder);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ FuriString *link = get_json_value_furi("link", folder);
|
|
|
|
|
+ if (!link)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to get link.");
|
|
|
|
|
+ furi_string_free(folder);
|
|
|
|
|
+ furi_string_free(name);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ furi_string_free(folder);
|
|
|
|
|
+
|
|
|
|
|
+ FURI_LOG_I(TAG, "Downloading folder %s", furi_string_get_cstr(name));
|
|
|
|
|
+
|
|
|
|
|
+ // fetch_folder callback
|
|
|
|
|
+ bool fetch_folder_result = false;
|
|
|
|
|
+ {
|
|
|
|
|
+ bool fetch_folder()
|
|
|
|
|
+ {
|
|
|
|
|
+ fhttp->save_received_data = true;
|
|
|
|
|
+ snprintf(fhttp->file_path, sizeof(fhttp->file_path),
|
|
|
|
|
+ STORAGE_EXT_PATH_PREFIX "/apps_data/flip_store/data/%s/%s/folder%d.json",
|
|
|
|
|
+ author, repo, i);
|
|
|
|
|
+ // return flipper_http_get_request_with_headers(
|
|
|
|
|
+ // fhttp,
|
|
|
|
|
+ // furi_string_get_cstr(link),
|
|
|
|
|
+ // "{\"Content-Type\":\"application/json\"}");
|
|
|
|
|
+ return flipper_http_request(fhttp, GET, furi_string_get_cstr(link), "{\"Content-Type\":\"application/json\"}", NULL);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // parse_folder callback (just enqueue)
|
|
|
|
|
+ bool parse_folder()
|
|
|
|
|
+ {
|
|
|
|
|
+ return enqueue_folder(fhttp->file_path, repo, furi_string_get_cstr(name));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fetch_folder_result = flipper_http_process_response_async(fhttp, fetch_folder, parse_folder);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!fetch_folder_result)
|
|
|
|
|
+ {
|
|
|
|
|
+ FURI_LOG_E(TAG, "Failed to download folder.");
|
|
|
|
|
+ furi_string_free(name);
|
|
|
|
|
+ furi_string_free(link);
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ FURI_LOG_I(TAG, "Downloaded folder %s", furi_string_get_cstr(name));
|
|
|
|
|
+ furi_string_free(name);
|
|
|
|
|
+ furi_string_free(link);
|
|
|
|
|
+ }
|
|
|
|
|
+ fhttp->state = IDLE;
|
|
|
|
|
+
|
|
|
|
|
+ // Finally, process all pending (enqueued) folders iteratively.
|
|
|
|
|
+ process_pending_folders(author);
|
|
|
return true;
|
|
return true;
|
|
|
-}
|
|
|
|
|
|
|
+}
|