summaryrefslogtreecommitdiff
path: root/src/trg-file-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/trg-file-parser.c')
-rw-r--r--src/trg-file-parser.c201
1 files changed, 201 insertions, 0 deletions
diff --git a/src/trg-file-parser.c b/src/trg-file-parser.c
new file mode 100644
index 0000000..fbfb6aa
--- /dev/null
+++ b/src/trg-file-parser.c
@@ -0,0 +1,201 @@
+/*
+ * transmission-remote-gtk - A GTK RPC client to Transmission
+ * Copyright (C) 2011-2013 Alan Fitton
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "bencode.h"
+#include "trg-file-parser.h"
+
+static trg_files_tree_node *trg_file_parser_node_insert(trg_files_tree_node
+ * top,
+ trg_files_tree_node
+ * last,
+ be_node *
+ file_node,
+ gint index)
+{
+ be_node *file_length_node = be_dict_find(file_node, "length", BE_INT);
+ be_node *file_path_list = be_dict_find(file_node, "path", BE_LIST);
+ trg_files_tree_node *lastIter = last;
+ GList *parentList = NULL;
+ be_node *path_el_node;
+ GList *li;
+ int i;
+
+ if (!file_path_list || !file_length_node)
+ return NULL;
+
+ if (lastIter)
+ while ((lastIter = lastIter->parent))
+ parentList = g_list_prepend(parentList, lastIter);
+
+ li = parentList;
+ lastIter = top;
+
+ /* Iterate over the path list which contains each file/directory
+ * component of the path in order.
+ */
+ for (i = 0; (path_el_node = file_path_list->val.l[i]); i++) {
+ gboolean isFile = !file_path_list->val.l[i + 1];
+ trg_files_tree_node *target_node = NULL;
+
+ if (li && !isFile) {
+ trg_files_tree_node *lastPathNode = (trg_files_tree_node *) li->data;
+
+ if (!g_strcmp0(lastPathNode->name, path_el_node->val.s)) {
+ target_node = lastPathNode;
+ li = g_list_next(li);
+ } else {
+ li = NULL;
+ }
+ }
+
+ if (!target_node && lastIter && lastIter->childrenHash && !isFile)
+ target_node = g_hash_table_lookup(lastIter->childrenHash, path_el_node->val.s);
+
+ if (!target_node) {
+ target_node = g_new0(trg_files_tree_node, 1);
+ target_node->name = g_strdup(path_el_node->val.s);
+ target_node->parent = lastIter;
+ trg_files_tree_node_add_child(lastIter, target_node);
+ }
+
+ if (isFile) {
+ target_node->length = (gint64) file_length_node->val.i;
+
+ while (lastIter) {
+ lastIter->length = target_node->length;
+ lastIter = lastIter->parent;
+ }
+ }
+
+ target_node->index = isFile ? index : -1;
+ lastIter = target_node;
+ }
+
+ g_list_free(parentList);
+
+ return lastIter;
+}
+
+void trg_torrent_file_free(trg_torrent_file * t)
+{
+ trg_files_tree_node_free(t->top_node);
+ g_free(t->name);
+ g_free(t);
+}
+
+static trg_files_tree_node *trg_parse_torrent_file_nodes(be_node *
+ info_node)
+{
+ be_node *files_node = be_dict_find(info_node, "files", BE_LIST);
+ trg_files_tree_node *top_node = g_new0(trg_files_tree_node, 1);
+ trg_files_tree_node *lastNode = NULL;
+ int i;
+
+ /* Probably means single file mode. */
+ if (!files_node)
+ return NULL;
+
+ for (i = 0; files_node->val.l[i]; ++i) {
+ be_node *file_node = files_node->val.l[i];
+
+ if (!be_validate_node(file_node, BE_DICT)
+ || !(lastNode =
+ trg_file_parser_node_insert(top_node, lastNode,
+ file_node, i))) {
+ /* Unexpected format. Throw away everything, file indexes need to
+ * be correct. */
+ trg_files_tree_node_free(top_node);
+ return NULL;
+ }
+ }
+
+ return top_node;
+}
+
+trg_torrent_file *trg_parse_torrent_file(const gchar * filename)
+{
+ GError *error = NULL;
+ GMappedFile *mf;
+ be_node *top_node, *info_node, *name_node;
+ trg_torrent_file *ret = NULL;
+
+ if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
+ g_message("%s does not exist", filename);
+ return NULL;
+ }
+
+ mf = g_mapped_file_new(filename, FALSE, &error);
+
+ if (error) {
+ g_error("%s", error->message);
+ g_error_free(error);
+ g_mapped_file_unref(mf);
+ return NULL;
+ } else {
+ top_node =
+ be_decoden(g_mapped_file_get_contents(mf),
+ g_mapped_file_get_length(mf));
+ }
+
+ g_mapped_file_unref(mf);
+
+ if (!top_node) {
+ return NULL;
+ } else if (!be_validate_node(top_node, BE_DICT)) {
+ goto out;
+ }
+
+ info_node = be_dict_find(top_node, "info", BE_DICT);
+ if (!info_node)
+ goto out;
+
+ name_node = be_dict_find(info_node, "name", BE_STR);
+ if (!name_node)
+ goto out;
+
+ ret = g_new0(trg_torrent_file, 1);
+ ret->name = g_strdup(name_node->val.s);
+
+ ret->top_node = trg_parse_torrent_file_nodes(info_node);
+ if (!ret->top_node) {
+ trg_files_tree_node *file_node;
+ be_node *length_node = be_dict_find(info_node, "length", BE_INT);
+
+ if (!length_node) {
+ g_free(ret);
+ ret = NULL;
+ goto out;
+ }
+
+ file_node = g_new0(trg_files_tree_node, 1);
+ file_node->length = (gint64) (length_node->val.i);
+ file_node->name = g_strdup(ret->name);
+ ret->top_node = file_node;
+ }
+
+ out:
+ be_free(top_node);
+ return ret;
+}