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.
26 #include "pbd/gstdio_compat.h"
30 #include <archive_entry.h>
31 #include <curl/curl.h>
33 #include "pbd/failed_constructor.h"
34 #include "pbd/file_archive.h"
35 #include "pbd/file_utils.h"
40 write_callback (void* buffer, size_t size, size_t nmemb, void* d)
42 FileArchive::MemPipe* p = (FileArchive::MemPipe*)d;
43 size_t realsize = size * nmemb;
46 p->data = (uint8_t*) realloc ((void*) p->data, p->size + realsize);
47 memcpy (&p->data[p->size], buffer, realsize);
57 FileArchive::Request* r = (FileArchive::Request*) arg;
60 curl = curl_easy_init ();
61 curl_easy_setopt (curl, CURLOPT_URL, r->url);
62 curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L);
66 curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
67 curl_easy_setopt(curl, CURLOPT_HEADER, 0L);
68 curl_easy_perform (curl);
69 curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &r->mp.length);
72 curl_easy_setopt(curl, CURLOPT_NOBODY, 0L);
73 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_callback);
74 curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void*) &r->mp);
75 curl_easy_perform (curl);
76 curl_easy_cleanup (curl);
87 ar_read (struct archive* a, void* d, const void** buff)
89 FileArchive::MemPipe* p = (FileArchive::MemPipe*)d;
93 while (p->size == 0) {
101 rv = p->size > 8192 ? 8192 : p->size;
102 memcpy (p->buf, p->data, rv);
104 memmove (p->data, &p->data[rv], p->size - rv);
110 p->progress->progress (p->processed, p->length);
117 ar_copy_data (struct archive *ar, struct archive *aw)
124 r = archive_read_data_block (ar, &buff, &size, &offset);
125 if (r == ARCHIVE_EOF) {
128 if (r != ARCHIVE_OK) {
131 r = archive_write_data_block (aw, buff, size, offset);
132 if (r != ARCHIVE_OK) {
133 fprintf (stderr, "Extract/Write Archive: %s", archive_error_string(aw));
139 static struct archive*
143 a = archive_read_new ();
144 archive_read_support_filter_all (a);
145 archive_read_support_format_all (a);
149 FileArchive::FileArchive (const std::string& url)
153 fprintf (stderr, "Invalid Archive URL/filename\n");
154 throw failed_constructor ();
157 if (_req.is_remote ()) {
158 _req.mp.progress = this;
160 _req.mp.progress = 0;
165 FileArchive::inflate (const std::string& destdir)
168 std::string pwd (Glib::get_current_dir ());
170 if (g_chdir (destdir.c_str ())) {
171 fprintf (stderr, "Archive: cannot chdir to '%s'\n", destdir.c_str ());
175 if (_req.is_remote ()) {
178 rv = extract_file ();
181 g_chdir (pwd.c_str());
185 std::vector<std::string>
186 FileArchive::contents ()
188 if (_req.is_remote ()) {
189 return contents_url ();
191 return contents_file ();
195 std::vector<std::string>
196 FileArchive::contents_file ()
198 struct archive* a = setup_archive ();
200 if (!g_stat (_req.url, &statbuf)) {
201 _req.mp.length = statbuf.st_size;
205 if (ARCHIVE_OK != archive_read_open_filename (a, _req.url, 8192)) {
206 fprintf (stderr, "Error opening archive: %s\n", archive_error_string(a));
207 return std::vector<std::string> ();
209 return get_contents (a);
212 std::vector<std::string>
213 FileArchive::contents_url ()
216 pthread_create (&_tid, NULL, get_url, (void*)&_req);
218 struct archive* a = setup_archive ();
219 archive_read_open (a, (void*)&_req.mp, NULL, ar_read, NULL);
220 std::vector<std::string> rv (get_contents (a));
222 pthread_join (_tid, NULL);
227 FileArchive::extract_file ()
229 struct archive* a = setup_archive ();
231 if (!g_stat (_req.url, &statbuf)) {
232 _req.mp.length = statbuf.st_size;
236 if (ARCHIVE_OK != archive_read_open_filename (a, _req.url, 8192)) {
237 fprintf (stderr, "Error opening archive: %s\n", archive_error_string(a));
240 return do_extract (a);
244 FileArchive::extract_url ()
247 pthread_create (&_tid, NULL, get_url, (void*)&_req);
249 struct archive* a = setup_archive ();
250 archive_read_open (a, (void*)&_req.mp, NULL, ar_read, NULL);
251 int rv = do_extract (a);
253 pthread_join (_tid, NULL);
257 std::vector<std::string>
258 FileArchive::get_contents (struct archive* a)
260 std::vector<std::string> rv;
261 struct archive_entry* entry;
263 int r = archive_read_next_header (a, &entry);
264 if (!_req.mp.progress) {
265 // file i/o -- not URL
266 const uint64_t read = archive_filter_bytes (a, -1);
267 progress (read, _req.mp.length);
269 if (r == ARCHIVE_EOF) {
272 if (r != ARCHIVE_OK) {
273 fprintf (stderr, "Error reading archive: %s\n", archive_error_string(a));
276 rv.push_back (archive_entry_pathname (entry));
279 archive_read_close (a);
280 archive_read_free (a);
285 FileArchive::do_extract (struct archive* a)
287 int flags = ARCHIVE_EXTRACT_TIME;
290 struct archive_entry* entry;
293 ext = archive_write_disk_new();
294 archive_write_disk_set_options(ext, flags);
297 int r = archive_read_next_header (a, &entry);
298 if (!_req.mp.progress) {
299 // file i/o -- not URL
300 const uint64_t read = archive_filter_bytes (a, -1);
301 progress (read, _req.mp.length);
304 if (r == ARCHIVE_EOF) {
307 if (r != ARCHIVE_OK) {
308 fprintf (stderr, "Error reading archive: %s\n", archive_error_string(a));
312 #if 0 // hacky alternative to chdir
313 const std::string full_path = Glib::build_filename (destdir, archive_entry_pathname (entry));
314 archive_entry_set_pathname (entry, full_path.c_str());
317 r = archive_write_header(ext, entry);
318 if (r != ARCHIVE_OK) {
319 fprintf (stderr, "Extracting archive: %s\n", archive_error_string(ext));
321 ar_copy_data (a, ext);
322 r = archive_write_finish_entry (ext);
323 if (r != ARCHIVE_OK) {
324 fprintf (stderr, "Extracting archive: %s\n", archive_error_string(ext));
331 archive_read_close (a);
332 archive_read_free (a);
333 archive_write_close(ext);
334 archive_write_free(ext);
340 FileArchive::create (const std::string& srcdir)
342 if (_req.is_remote ()) {
346 std::string parent = Glib::path_get_dirname (srcdir);
347 size_t p_len = parent.size () + 1;
349 Searchpath sp (srcdir);
350 std::vector<std::string> files;
351 find_files_matching_pattern (files, sp, "*");
353 std::map<std::string, std::string> filemap;
355 for (std::vector<std::string>::const_iterator f = files.begin (); f != files.end (); ++f) {
356 assert (f->size () > p_len);
357 filemap[*f] = f->substr (p_len);
360 return create (filemap);
364 FileArchive::create (const std::map<std::string, std::string>& filemap)
367 struct archive_entry *entry;
369 size_t read_bytes = 0;
370 size_t total_bytes = 0;
372 for (std::map<std::string, std::string>::const_iterator f = filemap.begin (); f != filemap.end (); ++f) {
374 if (g_stat (f->first.c_str(), &statbuf)) {
377 total_bytes += statbuf.st_size;
380 if (total_bytes == 0) {
384 progress (0, total_bytes);
386 a = archive_write_new ();
387 archive_write_set_format_pax_restricted (a);
388 archive_write_add_filter_lzma (a);
389 archive_write_open_filename (a, _req.url);
390 entry = archive_entry_new ();
392 for (std::map<std::string, std::string>::const_iterator f = filemap.begin (); f != filemap.end (); ++f) {
394 const char* filepath = f->first.c_str ();
395 const char* filename = f->second.c_str ();
398 if (g_stat (filepath, &statbuf)) {
402 archive_entry_clear (entry);
404 #ifdef PLATFORM_WINDOWS
405 archive_entry_set_size (entry, statbuf.st_size);
406 archive_entry_set_atime (entry, statbuf.st_atime, 0);
407 archive_entry_set_ctime (entry, statbuf.st_ctime, 0);
408 archive_entry_set_mtime (entry, statbuf.st_mtime, 0);
410 archive_entry_copy_stat (entry, &statbuf);
413 archive_entry_set_pathname (entry, filename);
414 archive_entry_set_filetype (entry, AE_IFREG);
415 archive_entry_set_perm (entry, 0644);
417 archive_write_header (a, entry);
419 int fd = g_open (filepath, O_RDONLY, 0444);
422 ssize_t len = read (fd, buf, sizeof (buf));
425 archive_write_data (a, buf, len);
426 progress (read_bytes, total_bytes);
427 len = read (fd, buf, sizeof (buf));
432 archive_entry_free (entry);
433 archive_write_close (a);
434 archive_write_free (a);