/** * Read the whole persistent cache into memory. */ static G_GNUC_COLD void sha1_read_cache(void) { FILE *f; file_path_t fp[1]; bool truncated = FALSE; g_return_if_fail(settings_config_dir()); file_path_set(fp, settings_config_dir(), "sha1_cache"); f = file_config_open_read("SHA-1 cache", fp, G_N_ELEMENTS(fp)); if (f) { for (;;) { char buffer[4096]; if (NULL == fgets(buffer, sizeof buffer, f)) break; if (!file_line_chomp_tail(buffer, sizeof buffer, NULL)) { truncated = TRUE; } else if (truncated) { truncated = FALSE; } else { parse_and_append_cache_entry(buffer); } } fclose(f); dump_cache(TRUE); } }
G_GNUC_COLD void tls_global_init(void) { static const struct { const char * const name; const int major; const int minor; } f = { "tls", 1, 0 }; char *cert_file, *key_file; #if !defined(REMAP_ZALLOC) && !defined(TRACK_MALLOC) && !defined(TRACK_ZALLOC) gnutls_global_set_mem_functions(halloc, halloc, NULL, hrealloc, hfree); #endif if (gnutls_global_init()) { g_error("gnutls_global_init() failed"); } #ifdef USE_TLS_CUSTOM_IO gnutls_global_set_log_level(9); gnutls_global_set_log_function(tls_log_function); #endif /* USE_TLS_CUSTOM_IO */ get_dh_params(); gnutls_certificate_allocate_credentials(&cert_cred); key_file = make_pathname(settings_config_dir(), "key.pem"); cert_file = make_pathname(settings_config_dir(), "cert.pem"); if (file_exists(key_file) && file_exists(cert_file)) { int ret; ret = gnutls_certificate_set_x509_key_file(cert_cred, cert_file, key_file, GNUTLS_X509_FMT_PEM); if (ret < 0) { g_warning("gnutls_certificate_set_x509_key_file() failed: %s", gnutls_strerror(ret)); } else { gnutls_certificate_set_dh_params(cert_cred, get_dh_params()); } } HFREE_NULL(key_file); HFREE_NULL(cert_file); header_features_add(FEATURES_CONNECTIONS, f.name, f.major, f.minor); header_features_add(FEATURES_G2_CONNECTIONS, f.name, f.major, f.minor); header_features_add(FEATURES_DOWNLOADS, f.name, f.major, f.minor); header_features_add(FEATURES_UPLOADS, f.name, f.major, f.minor); }
/** * Loads the spam.txt into memory. * * The selected file will then be monitored and a reloading will occur * shortly after a modification. */ static void spam_retrieve(void) { file_path_t fp[4]; FILE *f; int idx; char *tmp; unsigned length = 0; file_path_set(&fp[length++], settings_config_dir(), spam_text_file); file_path_set(&fp[length++], PRIVLIB_EXP, spam_text_file); #ifndef OFFICIAL_BUILD file_path_set(&fp[length++], PACKAGE_EXTRA_SOURCE_DIR, spam_text_file); #endif /* !OFFICIAL_BUILD */ tmp = get_folder_path(PRIVLIB_PATH, NULL); if (tmp != NULL) file_path_set(&fp[length++], tmp, spam_text_file); g_assert(length <= G_N_ELEMENTS(fp)); f = file_config_open_read_norename_chosen(spam_what, fp, length, &idx); if (f != NULL) { spam_retrieve_from_file(f, fp[idx].dir, fp[idx].name); fclose(f); } HFREE_NULL(tmp); }
/** * Initialize node stability caching. */ G_GNUC_COLD void stable_init(void) { dbstore_kv_t kv = { KUID_RAW_SIZE, NULL, sizeof(struct lifedata), 0 }; dbstore_packing_t packing = { serialize_lifedata, deserialize_lifedata, NULL }; g_assert(NULL == db_lifedata); g_assert(NULL == stable_sync_ev); g_assert(NULL == stable_prune_ev); /* Legacy: remove after 0.97 -- RAM, 2011-05-03 */ dbstore_move(settings_config_dir(), settings_dht_db_dir(), db_stable_base); db_lifedata = dbstore_open(db_stable_what, settings_dht_db_dir(), db_stable_base, kv, packing, STABLE_DB_CACHE_SIZE, kuid_hash, kuid_eq, GNET_PROPERTY(dht_storage_in_memory)); dbmw_set_map_cache(db_lifedata, STABLE_MAP_CACHE_SIZE); stable_prune_old(); stable_sync_ev = cq_periodic_main_add(STABLE_SYNC_PERIOD, stable_sync, NULL); stable_prune_ev = cq_periodic_main_add(STABLE_PRUNE_PERIOD, stable_periodic_prune, NULL); }
/** * Initialize RX dumping. * * @return TRUE if initialized. */ static gboolean dump_initialize(struct dump *dump) { char *pathname; if (dump->initialized) return TRUE; pathname = make_pathname(settings_config_dir(), dump->filename); dump->fd = file_open_missing(pathname, O_WRONLY | O_APPEND | O_NONBLOCK); HFREE_NULL(pathname); /* * If the dump "file" is actually a named pipe, we'd block quickly * if there was no reader. So set the file as non-blocking and * we'll disable dumping as soon as we can't write all the data * we want. */ if (dump->fd < 0) { g_warning("can't open %s -- disabling dumping", dump->filename); dump_disable(dump); return FALSE; } fd_set_nonblocking(dump->fd); dump->slist = slist_new(); dump->fill = 0; dump->initialized = TRUE; return TRUE; }
/** * Initialize security token caching. */ G_GNUC_COLD void tcache_init(void) { dbstore_kv_t kv = { KUID_RAW_SIZE, NULL, sizeof(struct tokdata), sizeof(struct tokdata) + MAX_INT_VAL(uint8) }; dbstore_packing_t packing = { serialize_tokdata, deserialize_tokdata, free_tokdata }; g_assert(NULL == db_tokdata); g_assert(NULL == tcache_prune_ev); /* Legacy: remove after 0.97 -- RAM, 2011-05-03 */ dbstore_move(settings_config_dir(), settings_dht_db_dir(), db_tcache_base); db_tokdata = dbstore_create(db_tcache_what, settings_dht_db_dir(), db_tcache_base, kv, packing, TOK_DB_CACHE_SIZE, kuid_hash, kuid_eq, GNET_PROPERTY(dht_storage_in_memory)); dbmw_set_map_cache(db_tokdata, TOK_MAP_CACHE_SIZE); dbmw_set_debugging(db_tokdata, &tcache_dbmw_dbg); token_life = MIN(TOK_LIFE, token_lifetime()); if (GNET_PROPERTY(dht_tcache_debug)) g_debug("DHT cached token lifetime set to %u secs", (unsigned) token_life); tcache_prune_ev = cq_periodic_main_add(TCACHE_PRUNE_PERIOD, tcache_periodic_prune, NULL); }
/** * 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(); }
/** * Add an entry to the persistent cache. */ static void add_persistent_cache_entry(const char *filename, filesize_t size, time_t mtime, const struct sha1 *sha1, const struct tth *tth) { char *pathname; FILE *f; pathname = make_pathname(settings_config_dir(), "sha1_cache"); f = file_fopen(pathname, "a"); if (f) { filestat_t sb; /* * If we're adding the very first entry (file empty), then emit header. */ if (fstat(fileno(f), &sb)) { g_warning("%s(): could not stat \"%s\": %m", G_STRFUNC, pathname); } else { if (0 == sb.st_size) { fputs(sha1_persistent_cache_file_header, f); } cache_entry_print(f, filename, sha1, tth, size, mtime); } fclose(f); } else { g_warning("%s(): could not open \"%s\": %m", G_STRFUNC, pathname); } HFREE_NULL(pathname); }
static const char * tth_cache_directory(void) { static char *directory; if (!directory) { directory = make_pathname(settings_config_dir(), "tth_cache"); } return NOT_LEAKING(directory); }
/** * 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); }
/** * 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; }
/** * 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); }
/** * 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; }
/** * 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 {
const char * guc_settings_config_dir(void) { return settings_config_dir(); }
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; }