1 /* librepo - A library providing (libcURL like) API to downloading repository
2 * Copyright (C) 2012 Tomas Mlcoch
3 *
4 * Licensed under the GNU Lesser General Public License Version 2.1
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #define _POSIX_C_SOURCE 200809L
22 #define _XOPEN_SOURCE 500
23 #include <glib.h>
24 #include <glib/gprintf.h>
25 #include <curl/curl.h>
26 #include <assert.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <stdarg.h>
34 #include <ftw.h>
35
36 #include "util.h"
37 #include "version.h"
38 #include "metalink.h"
39 #include "cleanup.h"
40 #include "yum.h"
41
42 #define DIR_SEPARATOR "/"
43 #define ENV_DEBUG "LIBREPO_DEBUG"
44
45 #ifdef CURL_GLOBAL_ACK_EINTR
46 #define EINTR_SUPPORT " with CURL_GLOBAL_ACK_EINTR support"
47 #define CURL_GLOBAL_INIT_FLAGS CURL_GLOBAL_ALL|CURL_GLOBAL_ACK_EINTR
48 #else
49 #define EINTR_SUPPORT ""
50 #define CURL_GLOBAL_INIT_FLAGS CURL_GLOBAL_ALL
51 #endif
52
53 static void
lr_log_handler(G_GNUC_UNUSED const gchar * log_domain,G_GNUC_UNUSED GLogLevelFlags log_level,const gchar * message,G_GNUC_UNUSED gpointer user_data)54 lr_log_handler(G_GNUC_UNUSED const gchar *log_domain,
55 G_GNUC_UNUSED GLogLevelFlags log_level,
56 const gchar *message,
57 G_GNUC_UNUSED gpointer user_data)
58 {
59 g_fprintf(stderr, "%s\n", message);
60 }
61
62 static void
lr_init_debugging(void)63 lr_init_debugging(void)
64 {
65 if (!g_getenv(ENV_DEBUG))
66 return;
67
68 g_log_set_handler("librepo", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
69 | G_LOG_FLAG_RECURSION, lr_log_handler, NULL);
70 }
71
72 void
lr_log_librepo_summary(void)73 lr_log_librepo_summary(void)
74 {
75 _cleanup_free_ gchar *time = NULL;
76 _cleanup_date_time_unref_ GDateTime *datetime = NULL;
77
78 g_info("Librepo version: %d.%d.%d%s (%s)", LR_VERSION_MAJOR,
79 LR_VERSION_MINOR,
80 LR_VERSION_PATCH,
81 EINTR_SUPPORT,
82 curl_version());
83
84 datetime = g_date_time_new_now_local();
85 // Date+Time in ISO 8601 format
86 time = g_date_time_format(datetime, "%Y-%m-%dT%H:%M:%S%z");
87 g_debug("Current date: %s", time);
88 }
89
90 static gpointer
lr_init_once_cb(gpointer user_data G_GNUC_UNUSED)91 lr_init_once_cb(gpointer user_data G_GNUC_UNUSED)
92 {
93 curl_global_init((long) CURL_GLOBAL_INIT_FLAGS);
94 lr_init_debugging();
95 lr_log_librepo_summary();
96 return GINT_TO_POINTER(1);
97 }
98
99 void
lr_global_init(void)100 lr_global_init(void)
101 {
102 static GOnce init_once = G_ONCE_INIT;
103 g_once(&init_once, lr_init_once_cb, NULL);
104 }
105
106
107 /*
108 void
109 lr_global_cleanup()
110 {
111 curl_global_cleanup();
112 }
113 */
114
115 void
lr_out_of_memory(void)116 lr_out_of_memory(void)
117 {
118 fprintf(stderr, "Out of memory\n");
119 abort();
120 exit(1);
121 }
122
123 void *
lr_malloc(size_t len)124 lr_malloc(size_t len)
125 {
126 void *m = malloc(len);
127 if (!m) lr_out_of_memory();
128 return m;
129 }
130
131 void *
lr_malloc0(size_t len)132 lr_malloc0(size_t len)
133 {
134 void *m = calloc(1, len);
135 if (!m) lr_out_of_memory();
136 return m;
137 }
138
139 void *
lr_realloc(void * ptr,size_t len)140 lr_realloc(void *ptr, size_t len)
141 {
142 void *m = realloc(ptr, len);
143 if (!m && len) lr_out_of_memory();
144 return m;
145 }
146
147 void
lr_free(void * m)148 lr_free(void *m)
149 {
150 if (m) free(m);
151 }
152
153 int
lr_gettmpfile(void)154 lr_gettmpfile(void)
155 {
156 int fd;
157 _cleanup_free_ char *template = NULL;
158 template = g_build_filename(g_get_tmp_dir(), "librepo-tmp-XXXXXX", NULL);
159 fd = mkstemp(template);
160 if (fd < 0) {
161 perror("Cannot create temporary file - mkstemp");
162 exit(1);
163 }
164 unlink(template);
165 return fd;
166 }
167
168 char *
lr_gettmpdir(void)169 lr_gettmpdir(void)
170 {
171 char *template = g_build_filename(g_get_tmp_dir(), "librepo-tmpdir-XXXXXX", NULL);
172 if (!mkdtemp(template)) {
173 lr_free(template);
174 return NULL;
175 }
176 return template;
177 }
178
179 char *
lr_pathconcat(const char * first,...)180 lr_pathconcat(const char *first, ...)
181 {
182 va_list args;
183 const char *next;
184 char *separator = DIR_SEPARATOR;
185 char *chunk, *res = NULL;
186 size_t separator_len = strlen(DIR_SEPARATOR);
187 size_t total_len; // Maximal len of result
188 size_t offset = 0;
189 int is_first = 1;
190 char *qmark_section;
191 int previous_was_empty = 0; // If last chunk was "" then separator will be
192 // appended to the result
193
194 if (!first)
195 return NULL;
196
197 total_len = strlen(first);
198
199 va_start(args, first);
200 while ((chunk = va_arg(args, char *)))
201 total_len += (strlen(chunk) + separator_len);
202 va_end(args);
203
204 if (total_len == 0)
205 return g_strdup("");
206
207 qmark_section = strchr(first, '?');
208
209 res = lr_malloc(total_len + separator_len + 1);
210
211 next = first;
212 va_start(args, first);
213 while (1) {
214 const char *current, *start, *end;
215 size_t current_len;
216
217 if (next) {
218 current = next;
219 next = va_arg(args, char *);
220 } else
221 break;
222
223 current_len = strlen(current);
224
225 if (!current_len) {
226 previous_was_empty = 1;
227 continue; /* Skip empty element */
228 } else
229 previous_was_empty = 0;
230
231 start = current;
232 end = start + current_len;
233 if (is_first && qmark_section)
234 end -= strlen(qmark_section);
235
236 /* Skip leading separators - except first element */
237 if (separator_len && is_first == 0) {
238 while (!strncmp(start, separator, separator_len))
239 start += separator_len;
240 }
241
242 /* Skip trailing separators */
243 if (separator_len) {
244 while (start + separator_len <= end &&
245 !strncmp(end-separator_len, separator, separator_len))
246 end -= separator_len;
247 }
248
249 if (start >= end) {
250 /* Element is filled only by separators */
251 if (is_first)
252 is_first = 0;
253 continue;
254 }
255
256 /* Prepend separator - except first element */
257 if (is_first == 0) {
258 memcpy(res + offset, separator, separator_len);
259 offset += separator_len;
260 } else
261 is_first = 0;
262
263 memcpy(res + offset, start, end - start);
264 offset += end - start;
265 }
266 va_end(args);
267
268 if (qmark_section) {
269 strcpy(res + offset, qmark_section);
270 offset += strlen(qmark_section);
271 }
272
273 assert(offset <= total_len);
274
275 if (offset == 0) {
276 lr_free(res);
277 return g_strdup(first);
278 }
279
280 /* If last element was emtpy string, append separator to the end */
281 if (previous_was_empty && is_first == 0) {
282 memcpy(res + offset, separator, separator_len);
283 offset += separator_len;
284 }
285
286 assert(offset <= total_len);
287
288 res[offset] = '\0';
289
290 return res;
291 }
292
293 int
lr_remove_dir_cb(const char * fpath,G_GNUC_UNUSED const struct stat * sb,G_GNUC_UNUSED int typeflag,G_GNUC_UNUSED struct FTW * ftwbuf)294 lr_remove_dir_cb(const char *fpath,
295 G_GNUC_UNUSED const struct stat *sb,
296 G_GNUC_UNUSED int typeflag,
297 G_GNUC_UNUSED struct FTW *ftwbuf)
298 {
299 int rv = remove(fpath);
300 if (rv)
301 g_warning("Cannot remove: %s: %s", fpath, g_strerror(errno));
302 return rv;
303 }
304
305 int
lr_remove_dir(const char * path)306 lr_remove_dir(const char *path)
307 {
308 return nftw(path, lr_remove_dir_cb, 64, FTW_DEPTH | FTW_PHYS);
309 }
310
311 int
lr_copy_content(int source,int dest)312 lr_copy_content(int source, int dest)
313 {
314 const int bufsize = 2048;
315 char buf[bufsize];
316 ssize_t size;
317
318 lseek(source, 0, SEEK_SET);
319 lseek(dest, 0, SEEK_SET);
320
321 while ((size = read(source, buf, bufsize)) > 0)
322 if (write(dest, buf, size) == -1)
323 return -1;
324
325 return (size < 0) ? -1 : 0;
326 }
327
328 char *
lr_prepend_url_protocol(const char * path)329 lr_prepend_url_protocol(const char *path)
330 {
331 if (!path)
332 return NULL;
333
334 if (strstr(path, "://")) // Protocol was specified
335 return g_strdup(path);
336
337 if (g_str_has_prefix(path, "file:/"))
338 return g_strdup(path);
339
340 if (path[0] == '/') // Path is absolute path
341 return g_strconcat("file://", path, NULL);
342
343 char *path_with_protocol, *resolved_path = realpath(path, NULL);
344 if (!resolved_path) {
345 g_warning("Error resolving real path of %s: %s", path, g_strerror(errno));
346 return NULL;
347 }
348 path_with_protocol = g_strconcat("file://", resolved_path, NULL);
349 free(resolved_path);
350 return path_with_protocol;
351 }
352
353 gchar *
lr_string_chunk_insert(GStringChunk * chunk,const gchar * string)354 lr_string_chunk_insert(GStringChunk *chunk, const gchar *string)
355 {
356 assert(chunk);
357
358 if (!string)
359 return NULL;
360
361 return g_string_chunk_insert(chunk, string);
362 }
363
364 int
lr_xml_parser_warning_logger(LrXmlParserWarningType type G_GNUC_UNUSED,char * msg,void * cbdata,GError ** err G_GNUC_UNUSED)365 lr_xml_parser_warning_logger(LrXmlParserWarningType type G_GNUC_UNUSED,
366 char *msg,
367 void *cbdata,
368 GError **err G_GNUC_UNUSED)
369 {
370 g_warning("WARNING: %s: %s", (char *) cbdata, msg);
371 return LR_CB_RET_OK;
372 }
373
374 gboolean
lr_best_checksum(GSList * list,LrChecksumType * type,gchar ** value)375 lr_best_checksum(GSList *list, LrChecksumType *type, gchar **value)
376 {
377 if (!list)
378 return FALSE;
379
380 assert(type);
381 assert(value);
382
383 LrChecksumType tmp_type = LR_CHECKSUM_UNKNOWN;
384 gchar *tmp_value = NULL;
385
386 for (GSList *elem = list; elem; elem = g_slist_next(elem)) {
387 LrMetalinkHash *hash = elem->data;
388
389 if (!hash->type || !hash->value)
390 continue;
391
392 LrChecksumType ltype = lr_checksum_type(hash->type);
393 if (ltype != LR_CHECKSUM_UNKNOWN && ltype > tmp_type) {
394 tmp_type = ltype;
395 tmp_value = hash->value;
396 }
397 }
398
399 if (tmp_type != LR_CHECKSUM_UNKNOWN) {
400 *type = tmp_type;
401 *value = tmp_value;
402 return TRUE;
403 }
404
405 return FALSE;
406 }
407
408 gchar *
lr_url_without_path(const char * url)409 lr_url_without_path(const char *url)
410 {
411 if (!url) return NULL;
412
413 // Filesystem
414 if (g_str_has_prefix(url, "file:///"))
415 return g_strdup("file://");
416 if (g_str_has_prefix(url, "file:/"))
417 return g_strdup("file://");
418
419 // Skip protocol prefix (ftp://, http://, file://, etc.)
420 gchar *ptr = strstr(url, "://");
421 if (ptr)
422 ptr += 3;
423 else
424 ptr = (gchar *) url;
425
426 // Find end of the host name
427 while (*ptr != '\0' && *ptr != '/')
428 ptr++;
429
430 // Calculate length of hostname
431 size_t len = ptr - url;
432
433 gchar *host = g_strndup(url, len);
434 //g_debug("%s: %s -> %s", __func__, url, host);
435
436 return host;
437 }
438
439 gchar **
lr_strv_dup(gchar ** array)440 lr_strv_dup(gchar **array)
441 {
442 guint length;
443 gchar **copy = NULL;
444 GPtrArray *ptrarray = NULL;
445
446 if (!array)
447 return array;
448
449 length = g_strv_length(array);
450 ptrarray = g_ptr_array_sized_new(length + 1);
451 for (guint x=0; x < length; x++)
452 g_ptr_array_add(ptrarray, g_strdup(array[x]));
453 g_ptr_array_add(ptrarray, NULL);
454 copy = (gchar **) ptrarray->pdata;
455 g_ptr_array_free(ptrarray, FALSE);
456 return copy;
457 }
458
459 gboolean
lr_is_local_path(const gchar * path)460 lr_is_local_path(const gchar *path)
461 {
462 if (!path || !*path)
463 return FALSE;
464
465 if (strstr(path, "://") && !g_str_has_prefix(path, "file://"))
466 return FALSE;
467
468 return TRUE;
469 }
470
471 gboolean
lr_key_file_save_to_file(GKeyFile * keyfile,const gchar * filename,GError ** err)472 lr_key_file_save_to_file(GKeyFile *keyfile,
473 const gchar *filename,
474 GError **err)
475 {
476 _cleanup_free_ gchar *content = NULL;
477 gsize length;
478
479 content = g_key_file_to_data(keyfile, &length, err);
480 if (!content)
481 return FALSE;
482
483 return g_file_set_contents(filename, content, length, err);
484 }
485
486 #ifdef WITH_ZCHUNK
487 LrChecksumType
lr_checksum_from_zck_hash(zck_hash zck_checksum_type)488 lr_checksum_from_zck_hash(zck_hash zck_checksum_type)
489 {
490 switch (zck_checksum_type) {
491 case ZCK_HASH_SHA1:
492 return LR_CHECKSUM_SHA1;
493 case ZCK_HASH_SHA256:
494 return LR_CHECKSUM_SHA256;
495 default:
496 return LR_CHECKSUM_UNKNOWN;
497 }
498 }
499
500 zck_hash
lr_zck_hash_from_lr_checksum(LrChecksumType checksum_type)501 lr_zck_hash_from_lr_checksum(LrChecksumType checksum_type)
502 {
503 switch (checksum_type) {
504 case LR_CHECKSUM_SHA1:
505 return ZCK_HASH_SHA1;
506 case LR_CHECKSUM_SHA256:
507 return ZCK_HASH_SHA256;
508 default:
509 return ZCK_HASH_UNKNOWN;
510 }
511 }
512
513 static zckCtx *
init_zck_read(const char * checksum,LrChecksumType checksum_type,gint64 zck_header_size,int fd,GError ** err)514 init_zck_read(const char *checksum, LrChecksumType checksum_type,
515 gint64 zck_header_size, int fd, GError **err)
516 {
517 assert(!err || *err == NULL);
518
519 zckCtx *zck = zck_create();
520 if(!zck_init_adv_read(zck, fd)) {
521 g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK,
522 "Unable to initialize zchunk file for reading");
523 return FALSE;
524 }
525
526 zck_hash ct = lr_zck_hash_from_lr_checksum(checksum_type);
527 if(ct == ZCK_HASH_UNKNOWN) {
528 g_set_error(err, LR_YUM_ERROR, LRE_ZCK,
529 "Zchunk doesn't support checksum type %i",
530 checksum_type);
531 free(zck);
532 return NULL;
533 }
534 if(!zck_set_ioption(zck, ZCK_VAL_HEADER_HASH_TYPE, ct)) {
535 g_set_error(err, LR_YUM_ERROR, LRE_ZCK,
536 "Error setting validation checksum type");
537 free(zck);
538 return NULL;
539 }
540 if(!zck_set_ioption(zck, ZCK_VAL_HEADER_LENGTH, zck_header_size)) {
541 g_set_error(err, LR_YUM_ERROR, LRE_ZCK,
542 "Error setting header size");
543 free(zck);
544 return NULL;
545 }
546 if(!zck_set_soption(zck, ZCK_VAL_HEADER_DIGEST, checksum,
547 strlen(checksum))) {
548 g_set_error(err, LR_YUM_ERROR, LRE_ZCK,
549 "Unable to set validation checksum: %s",
550 checksum);
551 free(zck);
552 return NULL;
553 }
554 return zck;
555 }
556
557 zckCtx *
lr_zck_init_read_base(const char * checksum,LrChecksumType checksum_type,gint64 zck_header_size,int fd,GError ** err)558 lr_zck_init_read_base(const char *checksum, LrChecksumType checksum_type,
559 gint64 zck_header_size, int fd, GError **err)
560 {
561 assert(!err || *err == NULL);
562
563 lseek(fd, 0, SEEK_SET);
564 zckCtx *zck = init_zck_read(checksum, checksum_type, zck_header_size, fd, err);
565 if(zck == NULL)
566 return NULL;
567
568 if(!zck_read_lead(zck)) {
569 g_set_error(err, LR_YUM_ERROR, LRE_ZCK,
570 "Unable to read zchunk lead");
571 zck_free(&zck);
572 return NULL;
573 }
574 if(!zck_read_header(zck)) {
575 g_set_error(err, LR_YUM_ERROR, LRE_ZCK,
576 "Unable to read zchunk header");
577 zck_free(&zck);
578 return NULL;
579 }
580 return zck;
581 }
582
583 gboolean
lr_zck_valid_header_base(const char * checksum,LrChecksumType checksum_type,gint64 zck_header_size,int fd,GError ** err)584 lr_zck_valid_header_base(const char *checksum, LrChecksumType checksum_type,
585 gint64 zck_header_size, int fd, GError **err)
586 {
587 assert(!err || *err == NULL);
588
589 lseek(fd, 0, SEEK_SET);
590 zckCtx *zck = init_zck_read(checksum, checksum_type, zck_header_size, fd, err);
591 if(zck == NULL)
592 return FALSE;
593
594 if(!zck_validate_lead(zck)) {
595 g_set_error(err, LR_YUM_ERROR, LRE_ZCK,
596 "Unable to read zchunk lead");
597 zck_free(&zck);
598 return FALSE;
599 }
600 zck_free(&zck);
601 return TRUE;
602 }
603
604 zckCtx *
lr_zck_init_read(LrDownloadTarget * target,char * filename,int fd,GError ** err)605 lr_zck_init_read(LrDownloadTarget *target, char *filename, int fd, GError **err)
606 {
607 zckCtx *zck = NULL;
608 gboolean found = FALSE;
609 for(GSList *cksum = target->checksums; cksum; cksum = g_slist_next(cksum)) {
610 GError *tmp_err = NULL;
611 LrDownloadTargetChecksum *ck =
612 (LrDownloadTargetChecksum*)(cksum->data);
613 g_debug("Checking checksum: %i: %s", ck->type, ck->value);
614 zck = lr_zck_init_read_base(ck->value, ck->type, target->zck_header_size,
615 fd, &tmp_err);
616 if(zck == NULL) {
617 g_debug("%s: Didn't find matching header in %s: %s", __func__,
618 filename, tmp_err->message);
619 g_clear_error(&tmp_err);
620 continue;
621 }
622 g_debug("%s: Found matching header in %s", __func__, filename);
623 found = TRUE;
624 break;
625 }
626 if(!found)
627 g_set_error(err, LR_YUM_ERROR, LRE_ZCK,
628 "Zchunk header checksum didn't match expected checksum");
629 return zck;
630 }
631
632 gboolean
lr_zck_valid_header(LrDownloadTarget * target,char * filename,int fd,GError ** err)633 lr_zck_valid_header(LrDownloadTarget *target, char *filename, int fd, GError **err)
634 {
635 assert(!err || *err == NULL);
636
637 for(GSList *cksum = target->checksums; cksum; cksum = g_slist_next(cksum)) {
638 GError *tmp_err = NULL;
639 LrDownloadTargetChecksum *ck =
640 (LrDownloadTargetChecksum*)(cksum->data);
641 if(lr_zck_valid_header_base(ck->value, ck->type, target->zck_header_size,
642 fd, &tmp_err)) {
643 return TRUE;
644 }
645 g_clear_error(&tmp_err);
646 }
647 g_set_error(err, LR_DOWNLOADER_ERROR, LRE_ZCK,
648 "%s's zchunk header doesn't match", filename);
649 return FALSE;
650 }
651 #endif /* WITH_ZCHUNK */
652
653 gboolean
lr_get_recursive_files_rec(char * path,char * extension,GSList ** filelist,GError ** err)654 lr_get_recursive_files_rec(char *path, char *extension, GSList **filelist,
655 GError **err)
656 {
657 assert(!err || *err == NULL);
658 assert(filelist);
659
660 GDir *d = g_dir_open(path, 0, err);
661 if(d == NULL)
662 return FALSE;
663
664 const char *file = NULL;
665 while((file = g_dir_read_name(d))) {
666 GError *tmp_err = NULL;
667
668 char *fullpath = g_build_path("/", path, file, NULL);
669 if(g_file_test(fullpath, G_FILE_TEST_IS_DIR)) {
670 lr_get_recursive_files_rec(fullpath, extension, filelist, &tmp_err);
671 if(tmp_err) {
672 g_warning("Unable to read directory %s: %s", fullpath, tmp_err->message);
673 g_clear_error(&tmp_err);
674 }
675 g_free(fullpath);
676 } else if(g_file_test(fullpath, G_FILE_TEST_IS_REGULAR) &&
677 g_str_has_suffix(fullpath, extension)) {
678 *filelist = g_slist_prepend(*filelist, fullpath);
679 } else {
680 g_free(fullpath);
681 }
682 }
683 g_dir_close(d);
684 return TRUE;
685 }
686
687 GSList *
lr_get_recursive_files(char * path,char * extension,GError ** err)688 lr_get_recursive_files(char *path, char *extension, GError **err)
689 {
690 GSList *filelist = NULL;
691 assert(!err || *err == NULL);
692
693 if(!lr_get_recursive_files_rec(path, extension, &filelist, err)) {
694 g_slist_free_full(filelist, free);
695 return NULL;
696 }
697 return filelist;
698 }
699