/** * Intuit the media type they are searching based on the first XML tag * we find in the meta data string, using simplistic lexical parsing which * will encompass 99% of the cases. */ static uint32 g2_node_intuit_media_type(const char *md) { const char *p = md; const char *start; int c; uint32 flags; while ('<' != (c = *p++) && c != 0) /* empty */; if (0 == c) return 0; /* Did not find any tag opening */ start = p = skip_ascii_spaces(p); while (0 != (c = *p)) { if (is_ascii_space(c) || '/' == c || '>' == c) { char *name; /* Found end of word, we got the tag name */ name = h_strndup(start, p - start); flags = TOKENIZE(name, g2_q2_md); if (0 == flags) { g_warning("%s(): unknown tag \"%s\", XML string was \"%s\"", G_STRFUNC, name, md); } hfree(name); return flags; } p++; } return 0; }
/** * Try to connect to the list of nodes given by in following form: * * list = <node> | <node>, 1*<node> * port = 1..65535 * hostname = 1*[a-zA-Z0-9.-] * node = hostname [":" <port>] * | <IPv4 address>[":" <port>] * | <IPv6 address> * | "[" <IPv6 address> "]:" <port> * peer = ["tls:"]["g2:"]<node> * * If the port is omitted, the default port (GTA_PORT: 6346) is used. * The case-insensitive prefix "tls:" requests a TLS (encrypted) connection. */ void nodes_gui_common_connect_by_name(const gchar *line) { const gchar *q; g_assert(line); q = line; while ('\0' != *q) { const gchar *endptr, *hostname; size_t hostname_len; host_addr_t addr; guint32 flags; guint16 port; bool g2; q = skip_ascii_spaces(q); if (',' == *q) { q++; continue; } addr = zero_host_addr; port = GTA_PORT; flags = SOCK_F_FORCE; endptr = NULL; hostname = NULL; hostname_len = 0; endptr = is_strcaseprefix(q, "tls:"); if (endptr) { flags |= SOCK_F_TLS; q = endptr; } endptr = is_strcaseprefix(q, "g2:"); if (endptr) { g2 = TRUE; q = endptr; } else { g2 = FALSE; } if (!string_to_host_or_addr(q, &endptr, &addr)) { g_message("expected hostname or IP address"); break; } if (!is_host_addr(addr)) { hostname = q; hostname_len = endptr - q; } q = endptr; if (':' == *q) { gint error; port = parse_uint16(&q[1], &endptr, 10, &error); if (error || 0 == port) { g_message("cannot parse port"); break; } q = skip_ascii_spaces(endptr); } else { q = skip_ascii_spaces(endptr); if ('\0' != *q && ',' != *q) { g_message("expected \",\" or \":\""); break; } } if (!hostname) { if (g2) { guc_node_g2_add(addr, port, flags); } else { guc_node_add(addr, port, flags); } } else { struct add_node_context *ctx; gchar *p; if ('\0' == hostname[hostname_len]) { p = NULL; } else { size_t n = 1 + hostname_len; g_assert(n > hostname_len); p = halloc(n); g_strlcpy(p, hostname, n); hostname = p; } WALLOC(ctx); ctx->port = port; ctx->flags = flags; ctx->g2 = g2; guc_adns_resolve(hostname, add_node_helper, ctx); HFREE_NULL(p); } } }
/** * Append a new line of text at the end of the header. * A private copy of the text is made. * * @return an error code, or HEAD_OK if appending was successful. */ int header_append(header_t *o, const char *text, int len) { char buf[MAX_LINE_SIZE]; const char *p = text; uchar c; header_field_t *hf; header_check(o); g_assert(len >= 0); if (o->flags & HEAD_F_EOH) return HEAD_EOH_REACHED; /* * If empty line, we reached EOH. */ if (len == 0) { o->flags |= HEAD_F_EOH; /* Mark we reached EOH */ return HEAD_EOH; } /* * Sanity checks. */ if (o->size >= HEAD_MAX_SIZE) return HEAD_TOO_LARGE; if (++(o->num_lines) >= HEAD_MAX_LINES) return HEAD_MANY_LINES; /* * Detect whether line is a new header or a continuation. */ c = *p; if (is_ascii_space(c)) { /* * It's a continuation. * * Make sure we already have recorded something, or we have * an unexpected continuation line. */ if (NULL == o->fields) return HEAD_CONTINUATION; /* Unexpected continuation */ /* * When a previous header line was malformed, we cannot accept * further continuation lines. */ if (o->flags & HEAD_F_SKIP) return HEAD_SKIPPED; /* * We strip leading spaces of all continuations before storing * them. If we have to dump the header, we will have to put * some spaces, but we don't guarantee we'll put the same amount. */ p++; /* First char is known space */ while ((c = *p)) { if (!is_ascii_space(c)) break; p++; } /* * If we've reached the end of the line, then the continuation * line was made of spaces only. Weird, but we can ignore it. * Note that it's not an EOH mark. */ if (*p == '\0') return HEAD_OK; /* * Save the continuation line by appending into the last header * field we handled. */ hf = slist_tail(o->fields); hfield_append(hf, p); add_continuation(o, hf->name, p); o->size += len - (p - text); /* Count only effective text */ /* * Also append the data in the hash table. */ } else { char *b; bool seen_space = FALSE; /* * It's a new header line. */ o->flags &= ~HEAD_F_SKIP; /* Assume this line will be OK */ /* * Parse header field. Must be composed of ascii chars only. * (no control characters, no space, no ISO Latin or other extension). * The field name ends with ':', after possible white spaces. */ for (b = buf, c = *p; c; c = *(++p)) { if (c == ':') { *b++ = '\0'; /* Reached end of field */ break; /* Done, buf[] holds field name */ } if (is_ascii_space(c)) { seen_space = TRUE; /* Only trailing spaces allowed */ continue; } if ( seen_space || (c != '-' && c != '.' && (!isascii(c) || is_ascii_cntrl(c) || is_ascii_punct(c))) ) { o->flags |= HEAD_F_SKIP; return HEAD_BAD_CHARS; } *b++ = c; } /* * If buf[] does not end with a NUL, we did not fully recognize * the header: we reached the end of the line without encountering * the ':' marker. * * If the buffer starts with a NUL char, it's also clearly malformed. */ g_assert(b > buf || (b == buf && *text == '\0')); if (b == buf || *(b-1) != '\0') { o->flags |= HEAD_F_SKIP; return HEAD_MALFORMED; } /* * We have a valid header field in buf[]. */ hf = hfield_make(buf); /* * Strip leading spaces in the value. */ g_assert(*p == ':'); p++; /* First char is field separator */ p = skip_ascii_spaces(p); /* * Record field value. */ hfield_append(hf, p); add_header(o, buf, p); if (!o->fields) { o->fields = slist_new(); } slist_append(o->fields, hf); o->size += len - (p - text); /* Count only effective text */ } return HEAD_OK; }
/** * Creates a valid and sanitized filename from the supplied string. For most * Unix-like platforms anything goes but for security reasons, shell meta * characters are replaced by harmless characters. * * @param filename the suggested filename. * @param no_spaces if TRUE, spaces are replaced with underscores. * @param no_evil if TRUE, "evil" characters are replaced with underscores. * * @returns a newly allocated string using halloc() or ``filename'' * if it was a valid filename already. */ char * filename_sanitize(const char *filename, bool no_spaces, bool no_evil) { const char *p; const char *s; char *q; g_assert(filename); /* Almost all evil characters are forbidden on Windows, anyway */ no_evil |= is_running_on_mingw(); /* Leading spaces are just confusing */ p = skip_ascii_spaces(filename); /* Make sure the filename isn't too long */ if (strlen(p) >= FILENAME_MAXBYTES) { q = halloc(FILENAME_MAXBYTES); filename_shrink(p, q, FILENAME_MAXBYTES); s = q; } else { s = p; q = NULL; } /* * Replace shell meta characters and likely problematic characters. * * Although parentheses are not evil per se, they make it a pain to * copy-n-paste filenames without going through the shell's auto- * completion (which normally does the necessary escaping). * * To keep things "readable", we replace parentheses with brackets. * Although brackets are meaningful for the shells, they are only * interpreted in the presence of "*" or "?", two characters that we * strip already. * --RAM, 2013-11-03 */ { size_t i; uchar c; for (i = 0; '\0' != (c = s[i]); i++) { if ( c < 32 || is_ascii_cntrl(c) || G_DIR_SEPARATOR == c || '/' == c || (0 == i && ('.' == c || '-' == c)) || (no_spaces && is_ascii_space(c)) || (no_evil && filename_is_evil_char(c)) ) { if (!q) q = h_strdup(s); q[i] = '_'; /* replace undesired char with underscore */ } else if ('(' == c) { if (!q) q = h_strdup(s); q[i] = '['; } else if (')' == c) { if (!q) q = h_strdup(s); q[i] = ']'; } } /** * Windows does not like filenames ending with a space or period. */ while (i-- > 0 && (is_ascii_space(s[i]) || '.' == s[i])) { if (!q) q = h_strdup(s); q[i] = '\0'; /* truncate string */ } } if (filename_is_reserved(q ? q : s)) { HFREE_NULL(q); q = h_strdup("noname"); } if (NULL == q && s != filename) q = h_strdup(s); /* Trimmed leading white space, must copy */ return q ? q : deconstify_gchar(s); }
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; }