/** * Dump the whole in-memory cache onto disk. */ static void dump_cache(bool force) { FILE *f; file_path_t fp; if (!force && !cache_dirty) return; file_path_set(&fp, settings_config_dir(), "sha1_cache"); f = file_config_open_write("SHA-1 cache", &fp); if (f) { struct dump_cache_context ctx; fputs(sha1_persistent_cache_file_header, f); ctx.f = f; ctx.forced = force; hikset_foreach(sha1_cache, dump_cache_one_entry, &ctx); if (file_config_close(f, &fp)) { cache_dirty = FALSE; } } /* * Update the timestamp even on failure to avoid that we retry this * too frequently. */ cache_dumped = tm_time(); }
/** * Loads the geo-ip.txt into memory. * * Choosing the first file we find among the several places we look at, * typically: * * -# ~/.gtk-gnutella/geo-ip.txt * -# /usr/share/gtk-gnutella/geo-ip.txt * -# /home/src/gtk-gnutella/geo-ip.txt * * The selected file will then be monitored and a reloading will occur * shortly after a modification. */ static void gip_retrieve(unsigned n) { FILE *f; int idx; char *filename; file_path_t fp[4]; unsigned length = 0; char *tmp; file_path_set(&fp[length++], settings_config_dir(), gip_source[n].file); tmp = get_folder_path(PRIVLIB_PATH, NULL); if (tmp != NULL) file_path_set(&fp[length++], tmp, gip_source[n].file); file_path_set(&fp[length++], PRIVLIB_EXP, gip_source[n].file); #ifndef OFFICIAL_BUILD file_path_set(&fp[length++], PACKAGE_EXTRA_SOURCE_DIR, gip_source[n].file); #endif g_assert(length <= G_N_ELEMENTS(fp)); f = file_config_open_read_norename_chosen(gip_source[n].what, fp, length, &idx); if (NULL == f) goto done; filename = make_pathname(fp[idx].dir, fp[idx].name); watcher_register(filename, gip_changed, uint_to_pointer(n)); HFREE_NULL(filename); gip_load(f, n); fclose(f); done: HFREE_NULL(tmp); }
/** * Loads the bogons.txt into memory. * * Choosing the first file we find among the several places we look at, * typically: * * -# ~/.gtk-gnutella/bogons.txt * -# /usr/share/gtk-gnutella/bogons.txt * -# PACKAGE_EXTRA_SOURCE_DIR/bogons.txt * * The selected file will then be monitored and a reloading will occur * shortly after a modification. */ static G_GNUC_COLD void bogons_retrieve(void) { FILE *f; int idx; char *filename; file_path_t fp[4]; unsigned length = 0; char *tmp; file_path_set(&fp[length++], settings_config_dir(), bogons_file); tmp = get_folder_path(PRIVLIB_PATH, NULL); if (tmp != NULL) file_path_set(&fp[length++], tmp, bogons_file); file_path_set(&fp[length++], PRIVLIB_EXP, bogons_file); #ifndef OFFICIAL_BUILD file_path_set(&fp[length++], PACKAGE_EXTRA_SOURCE_DIR, bogons_file); #endif g_assert(length <= G_N_ELEMENTS(fp)); f = file_config_open_read_norename_chosen(bogons_what, fp, length, &idx); if (NULL == f) goto done; filename = make_pathname(fp[idx].dir, fp[idx].name); watcher_register(filename, bogons_changed, NULL); HFREE_NULL(filename); bogons_load(f); fclose(f); done: HFREE_NULL(tmp); }
/** * Save upload statistics to file. */ static void upload_stats_dump_history(void) { FILE *out; file_path_t fp; /* open file for writing */ file_path_set(&fp, settings_config_dir(), ul_stats_file); out = file_config_open_write(ul_stats_what, &fp); if (NULL == out) return; file_config_preamble(out, "Upload statistics"); fputs( "#\n" "# Format is:\n" "# File basename <TAB> size <TAB> attempts <TAB> completed\n" "# <TAB> bytes_sent-high <TAB> bytes_sent-low\n" "# <TAB> time of last request <TAB> time of last served chunk\n" "# <TAB> SHA1 (\"*\" if unknown)\n" "#\n" "\n", out ); /* * Don't check this sooner so that the file is cleared, if the user * cleared the history. */ if (upload_stats_list) { /* for each element in uploads_stats_list, write out to hist file */ hash_list_foreach(upload_stats_list, upload_stats_dump_item, out); } file_config_close(out, &fp); dirty = FALSE; }
/** * Store known GWC URLs. * They are normally saved in ~/.gtk-gnutella/gwcache. */ static void gwc_store(void) { FILE *out; int i; int j; file_path_t fp; file_path_set(&fp, settings_config_dir(), gwc_file); out = file_config_open_write(gwc_what, &fp); if (!out) return; file_config_preamble(out, "Gnutella web cache URLs"); /* * Start dumping with the next slot we'll supersede, so that the oldest * entries are at the top: when the cache is full, we'll loop over at * retrieve time and will start superseding the oldest entries. */ i = gwc_url_slot + 1; if (i >= MAX_GWC_URLS) i = 0; for (j = 0; j < MAX_GWC_URLS; j++) { const char *url = gwc_url[i].url; i = (i + 1) % MAX_GWC_URLS; if (url == NULL) continue; fprintf(out, "%s\n", url); } if (file_config_close(out, &fp)) gwc_file_dirty = FALSE; }
static G_GNUC_COLD void load_faq(void) { static const gchar faq_file[] = "FAQ"; static file_path_t fp[6]; static int initialized; GtkWidget *textview; const gchar *lang; guint i = 0; FILE *f; html_view_free(&faq_html_view); textview = gui_dlg_faq_lookup("textview_faq"); lang = locale_get_language(); if (initialized != 0) { i = initialized; } else { char *tmp; char *path; tmp = get_folder_path(PRIVLIB_PATH, NULL); if (tmp != NULL) { path = make_pathname(tmp, lang); file_path_set(&fp[i++], ostrdup(path), faq_file); HFREE_NULL(path); path = make_pathname(tmp, "en"); file_path_set(&fp[i++], ostrdup(path), faq_file); HFREE_NULL(path); } HFREE_NULL(tmp); path = make_pathname(PRIVLIB_EXP, lang); file_path_set(&fp[i++], ostrdup(path), faq_file); HFREE_NULL(path); file_path_set(&fp[i++], PRIVLIB_EXP G_DIR_SEPARATOR_S "en", faq_file); #ifndef OFFICIAL_BUILD path = make_pathname(PACKAGE_EXTRA_SOURCE_DIR, lang); file_path_set(&fp[i++], ostrdup(path), faq_file); HFREE_NULL(path); file_path_set(&fp[i++], PACKAGE_EXTRA_SOURCE_DIR G_DIR_SEPARATOR_S "en", faq_file); #endif /* !OFFICIAL_BUILD */ initialized = i; } g_assert(i <= G_N_ELEMENTS(fp)); f = file_config_open_read_norename("FAQ", fp, i); if (f) { faq_html_view = html_view_load_file(textview, fileno(f)); fclose(f); } else { static const gchar msg[] = N_( "<html>" "<head>" "<title>Frequently Asked Questions</title>" "</head>" "<body>" "<p>" "The FAQ document could not be loaded. Please read the " "<a href=\"http://gtk-gnutella.sourceforge.net/?page=faq\">" "FAQ online</a> instead." "</p>" "</body>" "</html>" ); faq_html_view = html_view_load_memory(textview, array_from_string(msg)); } }
/** * Loads the whitelist into memory. */ static void G_COLD whitelist_retrieve(void) { char line[1024]; FILE *f; filestat_t st; unsigned linenum = 0; file_path_t fp[1]; whitelist_generation++; file_path_set(fp, settings_config_dir(), whitelist_file); f = file_config_open_read_norename("Host Whitelist", fp, N_ITEMS(fp)); if (!f) return; if (fstat(fileno(f), &st)) { g_warning("%s(): fstat() failed: %m", G_STRFUNC); fclose(f); return; } while (fgets(line, sizeof line, f)) { pslist_t *sl_addr, *sl; const char *endptr, *start; host_addr_t addr; uint16 port; uint8 bits; bool item_ok; bool use_tls; char *hname; linenum++; if (!file_line_chomp_tail(line, sizeof line, NULL)) { g_warning("%s(): line %u too long, aborting", G_STRFUNC, linenum); break; } if (file_line_is_skipable(line)) continue; sl_addr = NULL; addr = zero_host_addr; endptr = NULL; hname = NULL; endptr = is_strprefix(line, "tls:"); if (endptr) { use_tls = TRUE; start = endptr; } else { use_tls = FALSE; start = line; } port = 0; if (string_to_host_addr_port(start, &endptr, &addr, &port)) { sl_addr = name_to_host_addr(host_addr_to_string(addr), settings_dns_net()); } else if (string_to_host_or_addr(start, &endptr, &addr)) { uchar c = *endptr; switch (c) { case '\0': case ':': case '/': break; default: if (!is_ascii_space(c)) endptr = NULL; } if (!endptr) { g_warning("%s(): line %d: " "expected a hostname or IP address \"%s\"", G_STRFUNC, linenum, line); continue; } /* Terminate the string for name_to_host_addr() */ hname = h_strndup(start, endptr - start); } else { g_warning("%s(): line %d: expected hostname or IP address \"%s\"", G_STRFUNC, linenum, line); continue; } g_assert(sl_addr != NULL || hname != NULL); g_assert(NULL != endptr); bits = 0; item_ok = TRUE; /* * When an explicit address is given (no hostname) and with no * port, one can suffix the address with bits to indicate a CIDR * range of whitelisted addresses. */ if (0 == port) { /* Ignore trailing items separated by a space */ while ('\0' != *endptr && !is_ascii_space(*endptr)) { uchar c = *endptr++; if (':' == c) { int error; uint32 v; if (0 != port) { g_warning("%s(): line %d: multiple colons after host", G_STRFUNC, linenum); item_ok = FALSE; break; } v = parse_uint32(endptr, &endptr, 10, &error); port = (error || v > 0xffff) ? 0 : v; if (0 == port) { g_warning("%s(): line %d: " "invalid port value after host", G_STRFUNC, linenum); item_ok = FALSE; break; } } else if ('/' == c) { const char *ep; uint32 mask; if (0 != bits) { g_warning("%s(): line %d: " "multiple slashes after host", G_STRFUNC, linenum); item_ok = FALSE; break; } if (string_to_ip_strict(endptr, &mask, &ep)) { if (!host_addr_is_ipv4(addr)) { g_warning("%s(): line %d: " "IPv4 netmask after non-IPv4 address", G_STRFUNC, linenum); item_ok = FALSE; break; } endptr = ep; if (0 == (bits = netmask_to_cidr(mask))) { g_warning("%s(): line %d: " "IPv4 netmask after non-IPv4 address", G_STRFUNC, linenum); item_ok = FALSE; break; } } else { int error; uint32 v; v = parse_uint32(endptr, &endptr, 10, &error); if ( error || 0 == v || (v > 32 && host_addr_is_ipv4(addr)) || (v > 128 && host_addr_is_ipv6(addr)) ) { g_warning("%s(): line %d: " "invalid numeric netmask after host", G_STRFUNC, linenum); item_ok = FALSE; break; } bits = v; } } else { g_warning("%s(): line %d: " "unexpected character after host", G_STRFUNC, linenum); item_ok = FALSE; break; } } } if (item_ok) { struct whitelist *item; if (hname) { item = whitelist_hostname_create(use_tls, hname, port); whitelist_dns_resolve(item, FALSE); } else { PSLIST_FOREACH(sl_addr, sl) { host_addr_t *aptr = sl->data; g_assert(aptr != NULL); item = whitelist_addr_create(use_tls, *aptr, port, bits); whitelist_add(item); } } } else {
G_GNUC_COLD void upload_stats_load_history(void) { FILE *upload_stats_file; file_path_t fp; char line[FILENAME_MAX + 64]; guint lineno = 0; gcu_upload_stats_gui_freeze(); file_path_set(&fp, settings_config_dir(), ul_stats_file); /* open file for reading */ upload_stats_file = file_config_open_read(ul_stats_what, &fp, 1); if (upload_stats_file == NULL) goto done; /* parse, insert names into ul_stats_clist */ while (fgets(line, sizeof(line), upload_stats_file)) { static const struct ul_stats zero_item; struct ul_stats item; struct sha1 sha1_buf; const char *p; size_t i; lineno++; if (line[0] == '#' || line[0] == '\n') continue; p = strchr(line, '\t'); if (NULL == p) goto corrupted; line[p - line] = '\0'; /* line is now the URL-escaped file name */ p++; /* URL-unescape in-place */ if (!url_unescape(line, TRUE)) goto corrupted; item = zero_item; item.pathname = line; for (i = 0; i < 8; i++) { guint64 v; int error; const char *endptr; p = skip_ascii_spaces(p); /* SVN versions up to 15322 had only 6 fields in the history */ if (5 == i && '\0' == *p) break; switch (i) { case 7: /* We have a SHA1 or '*' if none known */ if ('*' != *p) { size_t len = clamp_strlen(p, SHA1_BASE32_SIZE); error = !parse_base32_sha1(p, len, &sha1_buf); item.sha1 = error ? NULL : &sha1_buf; } else { error = FALSE; } p = skip_ascii_non_spaces(p); v = 0; break; default: v = parse_uint64(p, &endptr, 10, &error); p = deconstify_gchar(endptr); } if (error || !is_ascii_space(*endptr)) goto corrupted; switch (i) { case 0: item.size = v; break; case 1: item.attempts = v; break; case 2: item.complete = v; break; case 3: item.bytes_sent |= ((guint64) (guint32) v) << 32; break; case 4: item.bytes_sent |= (guint32) v; break; case 5: item.rtime = MIN(v + (time_t) 0, TIME_T_MAX + (guint64) 0); case 6: item.dtime = MIN(v + (time_t) 0, TIME_T_MAX + (guint64) 0); case 7: break; /* Already stored above */ default: g_assert_not_reached(); goto corrupted; } } /* * We store the filenames UTF-8 encoded but the file might have been * edited or corrupted. */ if (is_absolute_path(item.pathname)) { item.filename = lazy_filename_to_utf8_normalized( filepath_basename(item.pathname), UNI_NORM_NFC); } else { item.filename = lazy_unknown_to_utf8_normalized( filepath_basename(item.pathname), UNI_NORM_NFC, NULL); } if (upload_stats_find(NULL, item.pathname, item.size)) { g_warning("upload_stats_load_history():" " Ignoring line %u due to duplicate file.", lineno); } else if (upload_stats_find(item.sha1, item.pathname, item.size)) { g_warning("upload_stats_load_history():" " Ignoring line %u due to duplicate file.", lineno); } else { upload_stats_add(item.pathname, item.size, item.filename, item.attempts, item.complete, item.bytes_sent, item.rtime, item.dtime, item.sha1); } continue; corrupted: g_warning("upload statistics file corrupted at line %u.", lineno); } /* close file */ fclose(upload_stats_file); done: gcu_upload_stats_gui_thaw(); return; }