/*
* Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2003 Hiroyuki Yamamoto
+ * Copyright (C) 1999-2004 Hiroyuki Yamamoto & The Sylpheed-Claws Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <sys/wait.h>
#include <dirent.h>
#include <time.h>
+#include <regex.h>
+#include <sys/utsname.h>
#include "intl.h"
#include "utils.h"
g_hash_table_foreach(table, hash_free_value_mem_func, NULL);
}
+gint str_case_equal(gconstpointer v, gconstpointer v2)
+{
+ return g_strcasecmp((const gchar *)v, (const gchar *)v2) == 0;
+}
+
+guint str_case_hash(gconstpointer key)
+{
+ const gchar *p = key;
+ guint h = *p;
+
+ if (h) {
+ h = tolower(h);
+ for (p += 1; *p != '\0'; p++)
+ h = (h << 5) - h + tolower(*p);
+ }
+
+ return h;
+}
+
void ptr_array_free_strings(GPtrArray *array)
{
gint i;
}
}
+gboolean str_find(const gchar *haystack, const gchar *needle)
+{
+ return strstr(haystack, needle) != NULL ? TRUE : FALSE;
+}
+
+gboolean str_case_find(const gchar *haystack, const gchar *needle)
+{
+ return strcasestr(haystack, needle) != NULL ? TRUE : FALSE;
+}
+
+gboolean str_find_equal(const gchar *haystack, const gchar *needle)
+{
+ return strcmp(haystack, needle) == 0;
+}
+
+gboolean str_case_find_equal(const gchar *haystack, const gchar *needle)
+{
+ return strcasecmp(haystack, needle) == 0;
+}
+
gint to_number(const gchar *nstr)
{
- register const gchar *p;
+ register const guchar *p;
if (*nstr == '\0') return -1;
return NULL;
while (haystack_len >= needle_len) {
- if (!strncasecmp(haystack, needle, needle_len))
+ if (!g_strncasecmp(haystack, needle, needle_len))
return (gchar *)haystack;
else {
haystack++;
return NULL;
}
+gpointer my_memmem(gconstpointer haystack, size_t haystacklen,
+ gconstpointer needle, size_t needlelen)
+{
+ const gchar *haystack_ = (const gchar *)haystack;
+ const gchar *needle_ = (const gchar *)needle;
+ const gchar *haystack_cur = (const gchar *)haystack;
+
+ if (needlelen == 1)
+ return memchr(haystack_, *needle_, haystacklen);
+
+ while ((haystack_cur = memchr(haystack_cur, *needle_, haystacklen))
+ != NULL) {
+ if (haystacklen - (haystack_cur - haystack_) < needlelen)
+ break;
+ if (memcmp(haystack_cur + 1, needle_ + 1, needlelen - 1) == 0)
+ return (gpointer)haystack_cur;
+ else
+ haystack_cur++;
+ }
+
+ return NULL;
+}
+
/* Copy no more than N characters of SRC to DEST, with NULL terminating. */
gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
{
return NULL;
}
-gint get_wcs_len(const gchar *s)
+gint get_mbs_len(const gchar *s)
{
const gchar *p = s;
gint mb_len;
return FALSE;
}
-gint get_next_word_len(const gchar *s)
+gint get_next_word_len(const guchar *s)
{
gint len = 0;
trim_subject_for_sort(str1);
trim_subject_for_sort(str2);
- return strcasecmp(str1, str2);
+ return g_strcasecmp(str1, str2);
}
void trim_subject_for_compare(gchar *str)
eliminate_parenthesis(str, '(', ')');
g_strstrip(str);
- while (!strncasecmp(str, "Re:", 3)) {
- srcp = str + 3;
- while (isspace(*srcp)) srcp++;
+ srcp = str + subject_get_prefix_length(str);
+ if (srcp != str)
memmove(str, srcp, strlen(srcp) + 1);
- }
}
void trim_subject_for_sort(gchar *str)
g_strstrip(str);
- while (!strncasecmp(str, "Re:", 3)) {
- srcp = str + 3;
- while (isspace(*srcp)) srcp++;
+ srcp = str + subject_get_prefix_length(str);
+ if (srcp != str)
memmove(str, srcp, strlen(srcp) + 1);
- }
}
void trim_subject(gchar *str)
{
- register gchar *srcp, *destp;
+ register guchar *srcp, *destp;
gchar op, cl;
gint in_brace;
- destp = str;
- while (!strncasecmp(destp, "Re:", 3)) {
- destp += 3;
- while (isspace(*destp)) destp++;
- }
+ destp = str + subject_get_prefix_length(str);
if (*destp == '[') {
op = '[';
void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
{
- register gchar *srcp, *destp;
+ register guchar *srcp, *destp;
gint in_brace;
srcp = destp = str;
*destp = '\0';
}
-void extract_one_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
- gchar op, gchar cl)
-{
- register gchar *srcp, *destp;
- gint in_brace;
- gboolean in_quote = FALSE;
-
- srcp = destp = str;
-
- if ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
- memmove(destp, srcp + 1, strlen(srcp));
- in_brace = 1;
- while(*destp) {
- if (*destp == op && !in_quote)
- in_brace++;
- else if (*destp == cl && !in_quote)
- in_brace--;
- else if (*destp == quote_chr)
- in_quote ^= TRUE;
-
- if (in_brace == 0)
- break;
-
- destp++;
- }
- }
- *destp = '\0';
-}
-
void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
gchar op, gchar cl)
{
void eliminate_quote(gchar *str, gchar quote_chr)
{
- register gchar *srcp, *destp;
+ register guchar *srcp, *destp;
srcp = destp = str;
void eliminate_address_comment(gchar *str)
{
- register gchar *srcp, *destp;
+ register guchar *srcp, *destp;
gint in_brace;
srcp = destp = str;
g_strstrip(str);
}
+void extract_list_id_str(gchar *str)
+{
+ if (strchr_with_skip_quote(str, '"', '<'))
+ extract_parenthesis_with_skip_quote(str, '"', '<', '>');
+ g_strstrip(str);
+}
+
static GSList *address_list_append_real(GSList *addr_list, const gchar *str, gboolean removecomments)
{
gchar *work;
void remove_space(gchar *str)
{
- register gchar *p = str;
+ register guchar *p = str;
register gint spc;
while (*p) {
void unfold_line(gchar *str)
{
- register gchar *p = str;
+ register guchar *p = str;
register gint spc;
while (*p) {
subst_chars(str, " \t\r\n\"/\\", '_');
}
+void subst_for_shellsafe_filename(gchar *str)
+{
+ subst_for_filename(str);
+ subst_chars(str, "|&;()<>'!{}[]",'_');
+}
+
gboolean is_header_line(const gchar *str)
{
if (str[0] == ':') return FALSE;
gboolean is_ascii_str(const guchar *str)
{
+ g_return_val_if_fail(str, FALSE);
+
while (*str != '\0') {
if (*str != '\t' && *str != ' ' &&
*str != '\r' && *str != '\n' &&
gint get_quote_level(const gchar *str, const gchar *quote_chars)
{
- const gchar *first_pos;
- const gchar *last_pos;
- const gchar *p = str;
+ const guchar *first_pos;
+ const guchar *last_pos;
+ const guchar *p = str;
gint quote_level = -1;
/* speed up line processing by only searching to the last '>' */
if ((first_pos = line_has_quote_char(str, quote_chars)) != NULL) {
/* skip a line if it contains a '<' before the initial '>' */
- if (memchr(str, '<', first_pos - str) != NULL)
+ if (memchr(str, '<', first_pos - (const guchar *)str) != NULL)
return -1;
last_pos = line_has_quote_char_last(first_pos, quote_chars);
} else
n++;
str = s_cl + 1;
- while (*str && isspace(*str)) str++;
+ while (*str && isspace(*(guchar *)str)) str++;
if (*str != op) {
string_list = g_slist_prepend(string_list,
g_strdup(""));
GList *uri_list_extract_filenames(const gchar *uri_list)
{
GList *result = NULL;
- const gchar *p, *q;
+ const guchar *p, *q;
gchar *file;
p = uri_list;
return result;
}
-#define HEX_TO_INT(val, hex) \
-{ \
- gchar c = hex; \
- \
- if ('0' <= c && c <= '9') { \
- val = c - '0'; \
- } else if ('a' <= c && c <= 'f') { \
- val = c - 'a' + 10; \
- } else if ('A' <= c && c <= 'F') { \
- val = c - 'A' + 10; \
- } else { \
- val = 0; \
- } \
+/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
+ * characters
+ */
+static gint axtoi(const gchar *hexstr)
+{
+ gint hi, lo, result;
+
+ hi = hexstr[0];
+ if ('0' <= hi && hi <= '9') {
+ hi -= '0';
+ } else
+ if ('a' <= hi && hi <= 'f') {
+ hi -= ('a' - 10);
+ } else
+ if ('A' <= hi && hi <= 'F') {
+ hi -= ('A' - 10);
+ }
+
+ lo = hexstr[1];
+ if ('0' <= lo && lo <= '9') {
+ lo -= '0';
+ } else
+ if ('a' <= lo && lo <= 'f') {
+ lo -= ('a'-10);
+ } else
+ if ('A' <= lo && lo <= 'F') {
+ lo -= ('A' - 10);
+ }
+ result = lo + (16 * hi);
+ return result;
+}
+
+gboolean is_uri_string(const gchar *str)
+{
+ return (g_strncasecmp(str, "http://", 7) == 0 ||
+ g_strncasecmp(str, "https://", 8) == 0 ||
+ g_strncasecmp(str, "ftp://", 6) == 0 ||
+ g_strncasecmp(str, "www.", 4) == 0);
+}
+
+gchar *get_uri_path(const gchar *uri)
+{
+ if (g_strncasecmp(uri, "http://", 7) == 0)
+ return (gchar *)(uri + 7);
+ else if (g_strncasecmp(uri, "https://", 8) == 0)
+ return (gchar *)(uri + 8);
+ else if (g_strncasecmp(uri, "ftp://", 6) == 0)
+ return (gchar *)(uri + 6);
+ else
+ return (gchar *)uri;
+}
+
+/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
+ * plusses, and escape characters are used)
+ */
+void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
+{
+ gchar *dec = decoded_uri;
+ const gchar *enc = encoded_uri;
+
+ while (*enc) {
+ if (*enc == '%') {
+ enc++;
+ if (isxdigit((guchar)enc[0]) &&
+ isxdigit((guchar)enc[1])) {
+ *dec = axtoi(enc);
+ dec++;
+ enc += 2;
+ }
+ } else {
+ if (*enc == '+')
+ *dec = ' ';
+ else
+ *dec = *enc;
+ dec++;
+ enc++;
+ }
+ }
+
+ *dec = '\0';
}
gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
static gchar *domain_name = NULL;
if (!domain_name) {
- gchar buf[128] = "";
struct hostent *hp;
+ struct utsname uts;
- if (gethostname(buf, sizeof(buf)) < 0) {
+ if (uname(&uts) < 0) {
perror("gethostname");
domain_name = "unknown";
} else {
- buf[sizeof(buf) - 1] = '\0';
- if ((hp = my_gethostbyname(buf)) == NULL) {
+ if ((hp = my_gethostbyname(uts.nodename)) == NULL) {
perror("gethostbyname");
- domain_name = g_strdup(buf);
+ domain_name = g_strdup(uts.nodename);
} else {
domain_name = g_strdup(hp->h_name);
}
return TRUE;
}
+gboolean dirent_is_regular_file(struct dirent *d)
+{
+ struct stat s;
+
+#ifdef HAVE_DIRENT_D_TYPE
+ if (d->d_type == DT_REG)
+ return TRUE;
+ else if (d->d_type != DT_UNKNOWN)
+ return FALSE;
+#endif
+
+ return (stat(d->d_name, &s) == 0 && S_ISREG(s.st_mode));
+}
+
+gboolean dirent_is_directory(struct dirent *d)
+{
+ struct stat s;
+
+#ifdef HAVE_DIRENT_D_TYPE
+ if (d->d_type == DT_DIR)
+ return TRUE;
+ else if (d->d_type != DT_UNKNOWN)
+ return FALSE;
+#endif
+
+ return (stat(d->d_name, &s) == 0 && S_ISDIR(s.st_mode));
+}
+
gint change_dir(const gchar *dir)
{
gchar *prevdir = NULL;
!strcmp(d->d_name, ".."))
continue;
- if (stat(d->d_name, &s) < 0) {
- FILE_OP_ERROR(d->d_name, "stat");
- continue;
- }
-
/* g_print("removing %s\n", d->d_name); */
- if (S_ISDIR(s.st_mode)) {
+ if (dirent_is_directory(d)) {
if (remove_dir_recursive(d->d_name) < 0) {
g_warning("can't remove directory\n");
return -1;
return 0;
}
-gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
+gint copy_file_part_to_fp(FILE *fp, off_t offset, size_t length, FILE *dest_fp)
{
- FILE *dest_fp;
gint n_read;
gint bytes_left, to_read;
gchar buf[BUFSIZ];
- gboolean err = FALSE;
if (fseek(fp, offset, SEEK_SET) < 0) {
perror("fseek");
return -1;
}
- if ((dest_fp = fopen(dest, "wb")) == NULL) {
- FILE_OP_ERROR(dest, "fopen");
- return -1;
- }
-
- if (change_file_mode_rw(dest_fp, dest) < 0) {
- FILE_OP_ERROR(dest, "chmod");
- g_warning("can't change file mode\n");
- }
-
bytes_left = length;
to_read = MIN(bytes_left, sizeof(buf));
if (n_read < to_read && ferror(fp))
break;
if (fwrite(buf, n_read, 1, dest_fp) < 1) {
- g_warning("writing to %s failed.\n", dest);
- fclose(dest_fp);
- unlink(dest);
return -1;
}
bytes_left -= n_read;
if (ferror(fp)) {
perror("fread");
- err = TRUE;
+ return -1;
}
- if (fclose(dest_fp) == EOF) {
+
+ return 0;
+}
+
+gint copy_file_part(FILE *fp, off_t offset, size_t length, const gchar *dest)
+{
+ FILE *dest_fp;
+ gboolean err = FALSE;
+
+ if ((dest_fp = fopen(dest, "wb")) == NULL) {
+ FILE_OP_ERROR(dest, "fopen");
+ return -1;
+ }
+
+ if (change_file_mode_rw(dest_fp, dest) < 0) {
+ FILE_OP_ERROR(dest, "chmod");
+ g_warning("can't change file mode\n");
+ }
+
+ if (copy_file_part_to_fp(fp, offset, length, dest_fp) < 0)
+ err = TRUE;
+
+ if (!err && fclose(dest_fp) == EOF) {
FILE_OP_ERROR(dest, "fclose");
err = TRUE;
}
if (err) {
+ g_warning("writing to %s failed.\n", dest);
unlink(dest);
return -1;
}
return ret;
}
+/*
+ * Create a new boundary in a way that it is very unlikely that this
+ * will occur in the following text. It would be easy to ensure
+ * uniqueness if everything is either quoted-printable or base64
+ * encoded (note that conversion is allowed), but because MIME bodies
+ * may be nested, it may happen that the same boundary has already
+ * been used. We avoid scanning the message for conflicts and hope the
+ * best.
+ *
+ * boundary := 0*69<bchars> bcharsnospace
+ * bchars := bcharsnospace / " "
+ * bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
+ * "+" / "_" / "," / "-" / "." /
+ * "/" / ":" / "=" / "?"
+ *
+ * some special characters removed because of buggy MTAs
+ */
+
+gchar *generate_mime_boundary(const gchar *prefix)
+{
+ static gchar tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "1234567890+_./=";
+ gchar buf_uniq[17];
+ gchar buf_date[64];
+ gint i;
+ gint pid;
+
+ pid = getpid();
+
+ /* We make the boundary depend on the pid, so that all running
+ * processes generate different values even when they have been
+ * started within the same second and srandom(time(NULL)) has been
+ * used. I can't see whether this is really an advantage but it
+ * doesn't do any harm.
+ */
+ for (i = 0; i < sizeof(buf_uniq) - 1; i++)
+ buf_uniq[i] = tbl[(rand() ^ pid) % (sizeof(tbl) - 1)];
+ buf_uniq[i] = '\0';
+
+ get_rfc822_date(buf_date, sizeof(buf_date));
+ subst_char(buf_date, ' ', '_');
+ subst_char(buf_date, ',', '_');
+ subst_char(buf_date, ':', '_');
+
+ return g_strdup_printf("%s_%s_%s", prefix ? prefix : "Multipart",
+ buf_date, buf_uniq);
+}
+
gint change_file_mode_rw(FILE *fp, const gchar *file)
{
#if HAVE_FCHMOD
tmpdir = get_tmp_dir();
tmplen = strlen(tmpdir);
progname = g_get_prgname();
+ if (progname == NULL)
+ progname = "sylpheed-claws";
proglen = strlen(progname);
Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
return tmpfile());
return tmpfile();
}
+FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
+{
+ int fd;
+
+ *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
+ fd = mkstemp(*filename);
+
+ return fdopen(fd, "w+");
+}
+
FILE *str_open_as_stream(const gchar *str)
{
FILE *fp;
gint execute_async(gchar *const argv[])
{
pid_t pid;
+ gint status;
if ((pid = fork()) < 0) {
perror("fork");
_exit(0);
}
- waitpid(pid, NULL, 0);
+ waitpid(pid, &status, 0);
- return 0;
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ else
+ return -1;
}
gint execute_sync(gchar *const argv[])
{
pid_t pid;
+ gint status;
if ((pid = fork()) < 0) {
perror("fork");
_exit(1);
}
- waitpid(pid, NULL, 0);
+ waitpid(pid, &status, 0);
- return 0;
+ if (WIFEXITED(status))
+ return WEXITSTATUS(status);
+ else
+ return -1;
}
gint execute_command_line(const gchar *cmdline, gboolean async)
gchar **argv;
gint ret;
+ debug_print("executing: %s\n", cmdline);
+
argv = strsplit_with_quote(cmdline, " ", 0);
if (async)
ret = execute_async(argv);
else
ret = execute_sync(argv);
+
g_strfreev(argv);
return ret;
encoded_uri[k] = 0;
}
-/* Converts two-digit hexadecimal to decimal. Used for unescaping escaped
- * characters
- */
-static gint axtoi(const gchar *hexstr)
-{
- gint hi, lo, result;
-
- hi = hexstr[0];
- if ('0' <= hi && hi <= '9') {
- hi -= '0';
- } else
- if ('a' <= hi && hi <= 'f') {
- hi -= ('a' - 10);
- } else
- if ('A' <= hi && hi <= 'F') {
- hi -= ('A' - 10);
- }
-
- lo = hexstr[1];
- if ('0' <= lo && lo <= '9') {
- lo -= '0';
- } else
- if ('a' <= lo && lo <= 'f') {
- lo -= ('a'-10);
- } else
- if ('A' <= lo && lo <= 'F') {
- lo -= ('A' - 10);
- }
- result = lo + (16 * hi);
- return result;
-}
-
-
-/* Decodes URL-Encoded strings (i.e. strings in which spaces are replaced by
- * plusses, and escape characters are used)
- */
-
-void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
-{
- const gchar *encoded;
- gchar *decoded;
-
- encoded = encoded_uri;
- decoded = decoded_uri;
-
- while (*encoded) {
- if (*encoded == '%') {
- encoded++;
- if (isxdigit(encoded[0])
- && isxdigit(encoded[1])) {
- *decoded = (gchar) axtoi(encoded);
- decoded++;
- encoded += 2;
- }
- }
- else if (*encoded == '+') {
- *decoded = ' ';
- decoded++;
- encoded++;
- }
- else {
- *decoded = *encoded;
- decoded++;
- encoded++;
- }
- }
-
- *decoded = '\0';
-}
-
-
gint open_uri(const gchar *uri, const gchar *cmdline)
{
gchar buf[BUFFSIZE];
g_snprintf(buf, sizeof(buf), cmdline, encoded_uri);
else {
if (cmdline)
- g_warning("Open URI command line is invalid: `%s'",
+ g_warning("Open URI command line is invalid "
+ "(there must be only one '%%s'): %s",
cmdline);
g_snprintf(buf, sizeof(buf), DEFAULT_BROWSER_CMD, encoded_uri);
}
-
+
execute_command_line(buf, TRUE);
return 0;
} else if (!strncmp(zone, "UT" , 2) ||
!strncmp(zone, "GMT", 2)) {
remoteoffset = 0;
- } else if (strlen(zone3) == 3 &&
- (p = strstr(ustzstr, zone3)) != NULL &&
- (p - ustzstr) % 3 == 0) {
- iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
- remoteoffset = iustz * 3600;
+ } else if (strlen(zone3) == 3) {
+ for (p = ustzstr; *p != '\0'; p += 3) {
+ if (!g_strncasecmp(p, zone3, 3)) {
+ iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
+ remoteoffset = iustz * 3600;
+ break;
+ }
+ }
+ if (*p == '\0')
+ return -1;
} else if (strlen(zone3) == 1) {
switch (zone[0]) {
case 'Z': remoteoffset = 0; break;
default: remoteoffset = 0; break;
}
remoteoffset = remoteoffset * 3600;
- }
+ } else
+ return -1;
return remoteoffset;
}
debug_mode = mode;
}
-gboolean debug_get_mode()
+gboolean debug_get_mode(void)
{
return debug_mode;
}
{
if (subject == NULL)
subject = "";
-
- if (g_strncasecmp(subject, "Re: ", 4) == 0)
- return g_hash_table_lookup(subject_table, subject + 4);
else
- return g_hash_table_lookup(subject_table, subject);
+ subject += subject_get_prefix_length(subject);
+
+ return g_hash_table_lookup(subject_table, subject);
}
void subject_table_insert(GHashTable *subject_table, gchar * subject,
void * data)
{
- if (subject == NULL)
- return;
- if (* subject == 0)
+ if (subject == NULL || *subject == 0)
return;
- if (g_strcasecmp(subject, "Re:") == 0)
- return;
- if (g_strcasecmp(subject, "Re: ") == 0)
- return;
-
- if (g_strncasecmp(subject, "Re: ", 4) == 0)
- g_hash_table_insert(subject_table, subject + 4, data);
- else
- g_hash_table_insert(subject_table, subject, data);
+ subject += subject_get_prefix_length(subject);
+ g_hash_table_insert(subject_table, subject, data);
}
void subject_table_remove(GHashTable *subject_table, gchar * subject)
if (subject == NULL)
return;
- if (g_strncasecmp(subject, "Re: ", 4) == 0)
- g_hash_table_remove(subject_table, subject + 4);
+ subject += subject_get_prefix_length(subject);
+ g_hash_table_remove(subject_table, subject);
+}
+
+/*!
+ *\brief Check if a string is prefixed with known (combinations)
+ * of prefixes. The function assumes that each prefix
+ * is terminated by zero or exactly _one_ space.
+ *
+ *\param str String to check for a prefixes
+ *
+ *\return int Number of chars in the prefix that should be skipped
+ * for a "clean" subject line. If no prefix was found, 0
+ * is returned.
+ */
+int subject_get_prefix_length(const gchar *subject)
+{
+ /*!< Array with allowable reply prefixes regexps. */
+ static const gchar * const prefixes[] = {
+ "Re\\:", /* "Re:" */
+ "Re\\[[1-9][0-9]*\\]\\:", /* "Re[XXX]:" (non-conforming news mail clients) */
+ "Antw\\:", /* "Antw:" (Dutch / German Outlook) */
+ "Aw\\:", /* "Aw:" (German) */
+ "Antwort\\:", /* "Antwort:" (German Lotus Notes) */
+ "Res\\:", /* "Res:" (Brazilian Outlook) */
+ "Fw\\:", /* "Fw:" Forward */
+ "Enc\\:", /* "Enc:" Forward (Brazilian Outlook) */
+ "Odp\\:", /* "Odp:" Re (Polish Outlook) */
+ "Rif\\:" /* "Rif:" (Italian Outlook) */
+ /* add more */
+ };
+ const int PREFIXES = sizeof prefixes / sizeof prefixes[0];
+ int n;
+ regmatch_t pos;
+ static regex_t regex;
+ static gboolean init_;
+
+ if (!subject) return 0;
+ if (!*subject) return 0;
+
+ if (!init_) {
+ GString *s = g_string_new("");
+
+ for (n = 0; n < PREFIXES; n++)
+ /* Terminate each prefix regexpression by a
+ * "\ ?" (zero or ONE space), and OR them */
+ g_string_sprintfa(s, "(%s\\ ?)%s",
+ prefixes[n],
+ n < PREFIXES - 1 ?
+ "|" : "");
+
+ g_string_prepend(s, "(");
+ g_string_append(s, ")+"); /* match at least once */
+ g_string_prepend(s, "^\\ *"); /* from beginning of line */
+
+
+ /* We now have something like "^\ *((PREFIX1\ ?)|(PREFIX2\ ?))+"
+ * TODO: Should this be "^\ *(((PREFIX1)|(PREFIX2))\ ?)+" ??? */
+ if (regcomp(®ex, s->str, REG_EXTENDED | REG_ICASE)) {
+ debug_print("Error compiling regexp %s\n", s->str);
+ g_string_free(s, TRUE);
+ return 0;
+ } else {
+ init_ = TRUE;
+ g_string_free(s, TRUE);
+ }
+ }
+
+ if (!regexec(®ex, subject, 1, &pos, 0) && pos.rm_so != -1)
+ return pos.rm_eo;
else
- g_hash_table_remove(subject_table, subject);
+ return 0;
}
-gboolean subject_is_reply(const gchar *subject)
+guint g_stricase_hash(gconstpointer gptr)
{
- /* XXX: just simply here so someone can handle really
- * advanced Re: detection like "Re[4]", "ANTW:" or
- * Re: Re: Re: Re: Re: Re: Re: Re:" stuff. */
- if (subject == NULL) return FALSE;
- else return 0 == g_strncasecmp(subject, "Re: ", 4);
+ guint hash_result = 0;
+ const char *str;
+
+ for (str = gptr; str && *str; str++) {
+ if (isupper((guchar)*str)) hash_result += (*str + ' ');
+ else hash_result += *str;
+ }
+
+ return hash_result;
}
-FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
+gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
{
- int fd;
-
- *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
- fd = mkstemp(*filename);
+ const char *str1 = gptr1;
+ const char *str2 = gptr2;
- return fdopen(fd, "w+");
+ return !g_strcasecmp(str1, str2);
}
-/* allow Mutt-like patterns in quick search */
-gchar *expand_search_string(const gchar *search_string)
-{
- int i = 0;
- gchar term_char, save_char;
- gchar *cmd_start, *cmd_end;
- GString *matcherstr;
- gchar *returnstr = NULL;
- gchar *copy_str;
- gboolean casesens, dontmatch;
- /* list of allowed pattern abbreviations */
- struct {
- gchar *abbreviated; /* abbreviation */
- gchar *command; /* actual matcher command */
- gint numparams; /* number of params for cmd */
- gboolean qualifier; /* do we append regexpcase */
- gboolean quotes; /* do we need quotes */
- }
- cmds[] = {
- { "a", "all", 0, FALSE, FALSE },
- { "ag", "age_greater", 1, FALSE, FALSE },
- { "al", "age_lower", 1, FALSE, FALSE },
- { "b", "body_part", 1, TRUE, TRUE },
- { "B", "message", 1, TRUE, TRUE },
- { "c", "cc", 1, TRUE, TRUE },
- { "C", "to_or_cc", 1, TRUE, TRUE },
- { "D", "deleted", 0, FALSE, FALSE },
- { "e", "header \"Sender\"", 1, TRUE, TRUE },
- { "E", "execute", 1, FALSE, TRUE },
- { "f", "from", 1, TRUE, TRUE },
- { "F", "forwarded", 0, FALSE, FALSE },
- { "h", "headers_part", 1, TRUE, TRUE },
- { "i", "header \"Message-Id\"", 1, TRUE, TRUE },
- { "I", "inreplyto", 1, TRUE, TRUE },
- { "L", "locked", 0, FALSE, FALSE },
- { "n", "newsgroups", 1, TRUE, TRUE },
- { "N", "new", 0, FALSE, FALSE },
- { "O", "~new", 0, FALSE, FALSE },
- { "r", "replied", 0, FALSE, FALSE },
- { "R", "~unread", 0, FALSE, FALSE },
- { "s", "subject", 1, TRUE, TRUE },
- { "se", "score_equal", 1, FALSE, FALSE },
- { "sg", "score_greater", 1, FALSE, FALSE },
- { "sl", "score_lower", 1, FALSE, FALSE },
- { "Se", "size_equal", 1, FALSE, FALSE },
- { "Sg", "size_greater", 1, FALSE, FALSE },
- { "Ss", "size_smaller", 1, FALSE, FALSE },
- { "t", "to", 1, TRUE, TRUE },
- { "T", "marked", 0, FALSE, FALSE },
- { "U", "unread", 0, FALSE, FALSE },
- { "x", "header \"References\"", 1, TRUE, TRUE },
- { "y", "header \"X-Label\"", 1, TRUE, TRUE },
- { "&", "&", 0, FALSE, FALSE },
- { "|", "|", 0, FALSE, FALSE },
- { NULL, NULL, 0, FALSE, FALSE }
- };
+gint g_int_compare(gconstpointer a, gconstpointer b)
+{
+ return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
+}
- if (search_string == NULL)
- return NULL;
+gchar *generate_msgid(gchar *buf, gint len)
+{
+ struct tm *lt;
+ time_t t;
+ gchar *addr;
- copy_str = g_strdup(search_string);
-
- /* if it's a full command don't process it so users
- can still do something like from regexpcase "foo" */
- for (i = 0; cmds[i].command; i++) {
- const gchar *tmp_search_string = search_string;
- cmd_start = cmds[i].command;
- /* allow logical NOT */
- if (*tmp_search_string == '~')
- tmp_search_string++;
- if (!strncmp(tmp_search_string, cmd_start, strlen(cmd_start)))
- break;
- }
- if (cmds[i].command)
- return copy_str;
+ t = time(NULL);
+ lt = localtime(&t);
- matcherstr = g_string_sized_new(16);
- cmd_start = cmd_end = copy_str;
- while (cmd_end && *cmd_end) {
- /* skip all white spaces */
- while (*cmd_end && isspace(*cmd_end))
- cmd_end++;
+ addr = g_strconcat("@", get_domain_name(), NULL);
- /* extract a command */
- while (*cmd_end && !isspace(*cmd_end))
- cmd_end++;
+ g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%08x%s",
+ lt->tm_year + 1900, lt->tm_mon + 1,
+ lt->tm_mday, lt->tm_hour,
+ lt->tm_min, lt->tm_sec,
+ (guint) rand(), addr);
- /* save character */
- save_char = *cmd_end;
- *cmd_end = '\0';
+ g_free(addr);
+ return buf;
+}
- dontmatch = FALSE;
- casesens = FALSE;
- /* ~ and ! mean logical NOT */
- if (*cmd_start == '~' || *cmd_start == '!')
- {
- dontmatch = TRUE;
- cmd_start++;
+/*
+ quote_cmd_argument()
+
+ return a quoted string safely usable in argument of a command.
+
+ code is extracted and adapted from etPan! project -- DINH V. HoĆ .
+*/
+
+gint quote_cmd_argument(gchar * result, guint size,
+ const gchar * path)
+{
+ const gchar * p;
+ gchar * result_p;
+ guint remaining;
+
+ result_p = result;
+ remaining = size;
+
+ for(p = path ; * p != '\0' ; p ++) {
+
+ if (isalnum((guchar)*p) || (* p == '/')) {
+ if (remaining > 0) {
+ * result_p = * p;
+ result_p ++;
+ remaining --;
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
}
- /* % means case sensitive match */
- if (*cmd_start == '%')
- {
- casesens = TRUE;
- cmd_start++;
+ else {
+ if (remaining >= 2) {
+ * result_p = '\\';
+ result_p ++;
+ * result_p = * p;
+ result_p ++;
+ remaining -= 2;
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
}
+ }
+ if (remaining > 0) {
+ * result_p = '\0';
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
+
+ return 0;
+}
- /* find matching abbreviation */
- for (i = 0; cmds[i].command; i++) {
- if (!strcmp(cmd_start, cmds[i].abbreviated)) {
- /* restore character */
- *cmd_end = save_char;
-
- /* copy command */
- if (matcherstr->len > 0) {
- g_string_append(matcherstr, " ");
- }
- if (dontmatch)
- g_string_append(matcherstr, "~");
- g_string_append(matcherstr, cmds[i].command);
- g_string_append(matcherstr, " ");
-
- /* stop if no params required */
- if (cmds[i].numparams == 0)
- break;
-
- /* extract a parameter, allow quotes */
- cmd_end++;
- cmd_start = cmd_end;
- if (*cmd_start == '"') {
- term_char = '"';
- cmd_end++;
- }
- else
- term_char = ' ';
-
- /* extract actual parameter */
- while ((*cmd_end) && (*cmd_end != term_char))
- cmd_end++;
+typedef struct
+{
+ GNode *parent;
+ GNodeMapFunc func;
+ gpointer data;
+} GNodeMapData;
- if (*cmd_end && (*cmd_end != term_char))
- break;
+static void g_node_map_recursive(GNode *node, gpointer data)
+{
+ GNodeMapData *mapdata = (GNodeMapData *) data;
+ GNode *newnode;
+ GNodeMapData newmapdata;
+ gpointer newdata;
- if (*cmd_end == '"')
- cmd_end++;
+ newdata = mapdata->func(node->data, mapdata->data);
+ if (newdata != NULL) {
+ newnode = g_node_new(newdata);
+ g_node_append(mapdata->parent, newnode);
- save_char = *cmd_end;
- *cmd_end = '\0';
+ newmapdata.parent = newnode;
+ newmapdata.func = mapdata->func;
+ newmapdata.data = mapdata->data;
- if (cmds[i].qualifier) {
- if (casesens)
- g_string_append(matcherstr, "regexp ");
- else
- g_string_append(matcherstr, "regexpcase ");
- }
+ g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &newmapdata);
+ }
+}
- /* do we need to add quotes ? */
- if (cmds[i].quotes && term_char != '"')
- g_string_append(matcherstr, "\"");
+GNode *g_node_map(GNode *node, GNodeMapFunc func, gpointer data)
+{
+ GNode *root;
+ GNodeMapData mapdata;
- /* copy actual parameter */
- g_string_append(matcherstr, cmd_start);
+ g_return_val_if_fail(node != NULL, NULL);
+ g_return_val_if_fail(func != NULL, NULL);
- /* do we need to add quotes ? */
- if (cmds[i].quotes && term_char != '"')
- g_string_append(matcherstr, "\"");
+ root = g_node_new(func(node->data, data));
- /* restore original character */
- *cmd_end = save_char;
+ mapdata.parent = root;
+ mapdata.func = func;
+ mapdata.data = data;
- break;
- }
- }
+ g_node_children_foreach(node, G_TRAVERSE_ALL, g_node_map_recursive, &mapdata);
- if (*cmd_end) {
- cmd_end++;
- cmd_start = cmd_end;
- }
- }
+ return root;
+}
- g_free(copy_str);
- returnstr = matcherstr->str;
- g_string_free(matcherstr, FALSE);
- return returnstr;
+#define HEX_TO_INT(val, hex) \
+{ \
+ gchar c = hex; \
+ \
+ if ('0' <= c && c <= '9') { \
+ val = c - '0'; \
+ } else if ('a' <= c && c <= 'f') { \
+ val = c - 'a' + 10; \
+ } else if ('A' <= c && c <= 'F') { \
+ val = c - 'A' + 10; \
+ } else { \
+ val = -1; \
+ } \
}
-guint g_stricase_hash(gconstpointer gptr)
+gboolean get_hex_value(guchar *out, gchar c1, gchar c2)
{
- guint hash_result = 0;
- const char *str;
+ gint hi, lo;
- for (str = gptr; str && *str; str++) {
- if (isupper(*str)) hash_result += (*str + ' ');
- else hash_result += *str;
- }
+ HEX_TO_INT(hi, c1);
+ HEX_TO_INT(lo, c2);
- return hash_result;
-}
+ if (hi == -1 || lo == -1)
+ return FALSE;
-gint g_stricase_equal(gconstpointer gptr1, gconstpointer gptr2)
-{
- const char *str1 = gptr1;
- const char *str2 = gptr2;
+ *out = (hi << 4) + lo;
+ return TRUE;
+}
- return !strcasecmp(str1, str2);
+#define INT_TO_HEX(hex, val) \
+{ \
+ if ((val) < 10) \
+ hex = '0' + (val); \
+ else \
+ hex = 'A' + (val) - 10; \
}
-gint g_int_compare(gconstpointer a, gconstpointer b)
+void get_hex_str(gchar *out, guchar ch)
{
- return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b);
+ gchar hex;
+
+ INT_TO_HEX(hex, ch >> 4);
+ *out++ = hex;
+ INT_TO_HEX(hex, ch & 0x0f);
+ *out++ = hex;
}