2 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "pbd/gstdio_compat.h"
27 #include <archive_entry.h>
28 #include <curl/curl.h>
30 #include "pbd/failed_constructor.h"
31 #include "pbd/file_archive.h"
36 write_callback (void* buffer, size_t size, size_t nmemb, void* d)
38 FileArchive::MemPipe* p = (FileArchive::MemPipe*)d;
39 size_t realsize = size * nmemb;
42 p->data = (uint8_t*) realloc ((void*) p->data, p->size + realsize);
43 memcpy (&p->data[p->size], buffer, realsize);
53 FileArchive::Request* r = (FileArchive::Request*) arg;
56 curl = curl_easy_init ();
57 curl_easy_setopt (curl, CURLOPT_URL, r->url);
58 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
62 curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
63 curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
64 curl_easy_perform (curl);
65 curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &r->mp.length);
68 curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
69 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_callback);
70 curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void*) &r->mp);
71 curl_easy_perform (curl);
72 curl_easy_cleanup (curl);
83 ar_read (struct archive* a, void* d, const void** buff)
85 FileArchive::MemPipe* p = (FileArchive::MemPipe*)d;
89 while (p->size == 0) {
97 rv = p->size > 8192 ? 8192 : p->size;
98 memcpy (p->buf, p->data, rv);
100 memmove (p->data, &p->data[rv], p->size - rv);
106 p->progress->progress (p->processed, p->length);
113 ar_copy_data (struct archive *ar, struct archive *aw)
120 r = archive_read_data_block (ar, &buff, &size, &offset);
121 if (r == ARCHIVE_EOF) {
124 if (r != ARCHIVE_OK) {
127 r = archive_write_data_block (aw, buff, size, offset);
128 if (r != ARCHIVE_OK) {
129 fprintf (stderr, "Extract/Write Archive: %s", archive_error_string(aw));
135 static struct archive*
139 a = archive_read_new ();
140 archive_read_support_filter_all (a);
141 archive_read_support_format_all (a);
145 FileArchive::FileArchive (const std::string& url)
149 fprintf (stderr, "Invalid Archive URL/filename\n");
150 throw failed_constructor ();
153 if (_req.is_remote ()) {
154 _req.mp.progress = this;
156 _req.mp.progress = 0;
161 FileArchive::inflate (const std::string& destdir)
164 std::string pwd (Glib::get_current_dir ());
166 if (g_chdir (destdir.c_str ())) {
167 fprintf (stderr, "Archive: cannot chdir to '%s'\n", destdir.c_str ());
171 if (_req.is_remote ()) {
174 rv = extract_file ();
177 g_chdir (pwd.c_str());
181 std::vector<std::string>
182 FileArchive::contents ()
184 if (_req.is_remote ()) {
185 return contents_url ();
187 return contents_file ();
191 std::vector<std::string>
192 FileArchive::contents_file ()
194 struct archive* a = setup_archive ();
196 if (!g_stat (_req.url, &statbuf)) {
197 _req.mp.length = statbuf.st_size;
201 if (ARCHIVE_OK != archive_read_open_filename (a, _req.url, 8192)) {
202 fprintf (stderr, "Error opening archive: %s\n", archive_error_string(a));
203 return std::vector<std::string> ();
205 return get_contents (a);
208 std::vector<std::string>
209 FileArchive::contents_url ()
212 pthread_create (&_tid, NULL, get_url, (void*)&_req);
214 struct archive* a = setup_archive ();
215 archive_read_open (a, (void*)&_req.mp, NULL, ar_read, NULL);
216 std::vector<std::string> rv (get_contents (a));
218 pthread_join (_tid, NULL);
223 FileArchive::extract_file ()
225 struct archive* a = setup_archive ();
227 if (!g_stat (_req.url, &statbuf)) {
228 _req.mp.length = statbuf.st_size;
232 if (ARCHIVE_OK != archive_read_open_filename (a, _req.url, 8192)) {
233 fprintf (stderr, "Error opening archive: %s\n", archive_error_string(a));
236 return do_extract (a);
240 FileArchive::extract_url ()
243 pthread_create (&_tid, NULL, get_url, (void*)&_req);
245 struct archive* a = setup_archive ();
246 archive_read_open (a, (void*)&_req.mp, NULL, ar_read, NULL);
247 int rv = do_extract (a);
249 pthread_join (_tid, NULL);
253 std::vector<std::string>
254 FileArchive::get_contents (struct archive* a)
256 std::vector<std::string> rv;
257 struct archive_entry* entry;
259 int r = archive_read_next_header (a, &entry);
260 if (!_req.mp.progress) {
261 // file i/o -- not URL
262 const uint64_t read = archive_filter_bytes (a, -1);
263 progress (read, _req.mp.length);
265 if (r == ARCHIVE_EOF) {
268 if (r != ARCHIVE_OK) {
269 fprintf (stderr, "Error reading archive: %s\n", archive_error_string(a));
272 rv.push_back (archive_entry_pathname (entry));
275 archive_read_close (a);
276 archive_read_free (a);
281 FileArchive::do_extract (struct archive* a)
283 int flags = ARCHIVE_EXTRACT_TIME;
286 struct archive_entry* entry;
289 ext = archive_write_disk_new();
290 archive_write_disk_set_options(ext, flags);
293 int r = archive_read_next_header (a, &entry);
294 if (!_req.mp.progress) {
295 // file i/o -- not URL
296 const uint64_t read = archive_filter_bytes (a, -1);
297 progress (read, _req.mp.length);
300 if (r == ARCHIVE_EOF) {
303 if (r != ARCHIVE_OK) {
304 fprintf (stderr, "Error reading archive: %s\n", archive_error_string(a));
308 #if 0 // hacky alternative to chdir
309 const std::string full_path = Glib::build_filename (destdir, archive_entry_pathname (entry));
310 archive_entry_set_pathname (entry, full_path.c_str());
313 r = archive_write_header(ext, entry);
314 if (r != ARCHIVE_OK) {
315 fprintf (stderr, "Extracting archive: %s\n", archive_error_string(ext));
317 ar_copy_data (a, ext);
318 r = archive_write_finish_entry (ext);
319 if (r != ARCHIVE_OK) {
320 fprintf (stderr, "Extracting archive: %s\n", archive_error_string(ext));
327 archive_read_close (a);
328 archive_read_free (a);
329 archive_write_close(ext);
330 archive_write_free(ext);