static void setup_map(void) { static gboolean done; guint i; if (done) return; for (i = 0; i < G_N_ELEMENTS(map); i++) { guchar c; if (i > 0 && utf8_byte_is_allowed(i)) { if (is_ascii_upper(i)) { c = ascii_tolower(i); } else if ( is_ascii_punct(i) || is_ascii_cntrl(i) || is_ascii_space(i) ) { c = ' '; } else { c = i; } } else { c = 0; } map[i] = c; } done = TRUE; }
/** * 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); }
/** * 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; }