1 /* GepubArchive
2  *
3  * Copyright (C) 2011 Daniel Garcia <danigm@wadobo.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <config.h>
21 #include <libxml/parser.h>
22 #include <libxml/tree.h>
23 #include <archive.h>
24 #include <archive_entry.h>
25 
26 #include "gepub-archive.h"
27 #include "gepub-utils.h"
28 
29 #define BUFZISE 1024
30 
31 struct _GepubArchive {
32     GObject parent;
33 
34     struct archive *archive;
35     gchar *path;
36 };
37 
38 struct _GepubArchiveClass {
39     GObjectClass parent_class;
40 };
41 
G_DEFINE_TYPE(GepubArchive,gepub_archive,G_TYPE_OBJECT)42 G_DEFINE_TYPE (GepubArchive, gepub_archive, G_TYPE_OBJECT)
43 
44 static gboolean
45 gepub_archive_open (GepubArchive *archive)
46 {
47     int r;
48 
49     archive->archive = archive_read_new ();
50     archive_read_support_format_zip (archive->archive);
51 
52     r = archive_read_open_filename (archive->archive, archive->path, 10240);
53 
54     if (r != ARCHIVE_OK) {
55         return FALSE;
56     }
57 
58     return TRUE;
59 }
60 
61 static void
gepub_archive_close(GepubArchive * archive)62 gepub_archive_close (GepubArchive *archive)
63 {
64     if (!archive->archive)
65         return;
66 
67     archive_read_free (archive->archive);
68     archive->archive = NULL;
69 }
70 
71 static void
gepub_archive_finalize(GObject * object)72 gepub_archive_finalize (GObject *object)
73 {
74     GepubArchive *archive = GEPUB_ARCHIVE (object);
75 
76     g_clear_pointer (&archive->path, g_free);
77 
78     gepub_archive_close (archive);
79 
80     G_OBJECT_CLASS (gepub_archive_parent_class)->finalize (object);
81 }
82 
83 static void
gepub_archive_init(GepubArchive * archive)84 gepub_archive_init (GepubArchive *archive)
85 {
86 }
87 
88 static void
gepub_archive_class_init(GepubArchiveClass * klass)89 gepub_archive_class_init (GepubArchiveClass *klass)
90 {
91     GObjectClass *object_class = G_OBJECT_CLASS (klass);
92 
93     object_class->finalize = gepub_archive_finalize;
94 }
95 
96 GepubArchive *
gepub_archive_new(const gchar * path)97 gepub_archive_new (const gchar *path)
98 {
99     GepubArchive *archive;
100 
101     archive = GEPUB_ARCHIVE (g_object_new (GEPUB_TYPE_ARCHIVE, NULL));
102     archive->path = g_strdup (path);
103     archive->archive = NULL;
104 
105     return archive;
106 }
107 
108 /**
109  * gepub_archive_list_files:
110  * @archive: a #GepubArchive
111  *
112  * Returns: (element-type utf8) (transfer full): list of files in the archive
113  */
114 GList *
gepub_archive_list_files(GepubArchive * archive)115 gepub_archive_list_files (GepubArchive *archive)
116 {
117     struct archive_entry *entry;
118     GList *file_list = NULL;
119 
120     if (!gepub_archive_open (archive))
121         return NULL;
122     while (archive_read_next_header (archive->archive, &entry) == ARCHIVE_OK) {
123         file_list = g_list_prepend (file_list, g_strdup (archive_entry_pathname (entry)));
124         archive_read_data_skip (archive->archive);
125     }
126     gepub_archive_close (archive);
127 
128     return file_list;
129 }
130 
131 GBytes *
gepub_archive_read_entry(GepubArchive * archive,const gchar * path)132 gepub_archive_read_entry (GepubArchive *archive,
133                           const gchar *path)
134 {
135     struct archive_entry *entry;
136     guchar *buffer;
137     gint size;
138 
139     if (!gepub_archive_open (archive))
140         return NULL;
141 
142     while (archive_read_next_header (archive->archive, &entry) == ARCHIVE_OK) {
143         if (g_ascii_strcasecmp (path, archive_entry_pathname (entry)) == 0)
144             break;
145         archive_read_data_skip (archive->archive);
146     }
147 
148     size = archive_entry_size (entry);
149     buffer = g_malloc0 (size);
150     archive_read_data (archive->archive, buffer, size);
151 
152     gepub_archive_close (archive);
153     return g_bytes_new_take (buffer, size);
154 }
155 
156 gchar *
gepub_archive_get_root_file(GepubArchive * archive)157 gepub_archive_get_root_file (GepubArchive *archive)
158 {
159     xmlDoc *doc = NULL;
160     xmlNode *root_element = NULL;
161     xmlNode *root_node = NULL;
162     GBytes *bytes;
163     const gchar *buffer;
164     gsize bufsize;
165     gchar *root_file = NULL;
166 
167     // root file is in META-INF/container.xml
168     bytes = gepub_archive_read_entry (archive, "META-INF/container.xml");
169     if (!bytes)
170         return NULL;
171 
172     buffer = g_bytes_get_data (bytes, &bufsize);
173     doc = xmlRecoverMemory (buffer, bufsize);
174     root_element = xmlDocGetRootElement (doc);
175     root_node = gepub_utils_get_element_by_tag (root_element, "rootfile");
176     root_file = gepub_utils_get_prop (root_node, "full-path");
177 
178     xmlFreeDoc (doc);
179     g_bytes_unref (bytes);
180 
181     return root_file;
182 }
183