/*
- * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2020 The Claws Mail Team and Hiroyuki Yamamoto
+ * Claws Mail -- a GTK based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2024 The Claws Mail Team and Hiroyuki Yamamoto
*
* 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
#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
-#include <dirent.h>
#include <time.h>
#include <regex.h>
#ifdef G_OS_WIN32
# include <direct.h>
# include <io.h>
-# include <w32lib.h>
#endif
#include "utils.h"
static gboolean debug_mode = FALSE;
-GSList *slist_copy_deep(GSList *list, GCopyFunc func)
-{
-#if GLIB_CHECK_VERSION(2, 34, 0)
- return g_slist_copy_deep(list, func, NULL);
-#else
- GSList *res = g_slist_copy(list);
- GSList *walk = res;
- while (walk) {
- walk->data = func(walk->data, NULL);
- walk = walk->next;
- }
- return res;
-#endif
-}
-
void list_free_strings_full(GList *list)
{
g_list_free_full(list, (GDestroyNotify)g_free);
register int t = 0, r = 0;
if (b_format == NULL) {
b_format = _("%dB");
- kb_format = _("%d.%02dKB");
- mb_format = _("%d.%02dMB");
- gb_format = _("%.2fGB");
+ kb_format = _("%d.%02dKiB");
+ mb_format = _("%d.%02dMiB");
+ gb_format = _("%.2fGiB");
}
if (size < (goffset)1024) {
return str;
}
+/* truncates string at first CR (carriage return) or LF (line feed) */
+gchar *strcrlftrunc(gchar *str)
+{
+ gchar *p = NULL;
+
+ if ((str == NULL) || (!*str)) return str;
+
+ if ((p = strstr(str, "\r")) != NULL)
+ *p = '\0';
+ if ((p = strstr(str, "\n")) != NULL)
+ *p = '\0';
+
+ return str;
+}
+
#ifndef HAVE_STRCASESTR
/* Similar to `strstr' but this function ignores the case of both strings. */
gchar *strcasestr(const gchar *haystack, const gchar *needle)
trim_subject_for_sort(str1);
trim_subject_for_sort(str2);
+ if (!g_utf8_validate(str1, -1, NULL)) {
+ g_warning("message subject \"%s\" failed UTF-8 validation", str1);
+ return 0;
+ } else if (!g_utf8_validate(str2, -1, NULL)) {
+ g_warning("message subject \"%s\" failed UTF-8 validation", str2);
+ return 0;
+ }
+
return g_utf8_collate(str1, str2);
}
if (str == NULL || *str == '\0')
return str;
+ g_strstrip(str);
+ if (*str == '\0')
+ return str;
/* search for unescaped quote_chr */
p = str;
if (*p == quote_chr)
return (g_ascii_strncasecmp(str, "http://", 7) == 0 ||
g_ascii_strncasecmp(str, "https://", 8) == 0 ||
g_ascii_strncasecmp(str, "ftp://", 6) == 0 ||
+ g_ascii_strncasecmp(str, "ftps://", 7) == 0 ||
+ g_ascii_strncasecmp(str, "sftp://", 7) == 0 ||
+ g_ascii_strncasecmp(str, "ftp.", 4) == 0 ||
+ g_ascii_strncasecmp(str, "webcal://", 9) == 0 ||
+ g_ascii_strncasecmp(str, "webcals://", 10) == 0 ||
g_ascii_strncasecmp(str, "www.", 4) == 0);
}
return (gchar *)(uri + 8);
else if (g_ascii_strncasecmp(uri, "ftp://", 6) == 0)
return (gchar *)(uri + 6);
+ else if (g_ascii_strncasecmp(uri, "ftps://", 7) == 0)
+ return (gchar *)(uri + 7);
+ else if (g_ascii_strncasecmp(uri, "sftp://", 7) == 0)
+ return (gchar *)(uri + 7);
+ else if (g_ascii_strncasecmp(uri, "webcal://", 9) == 0)
+ return (gchar *)(uri + 7);
+ else if (g_ascii_strncasecmp(uri, "webcals://", 10) == 0)
+ return (gchar *)(uri + 7);
else
return (gchar *)uri;
}
if (is_uri_string(str)) {
for (p = str; *p != '\0'; p++) {
- if (!g_ascii_isgraph(*p) || strchr("()<>\"", *p))
+ if (strchr("<>\"", *p))
break;
}
return p - str;
if (tmp) {
if (!is_file_entry_regular(tmp)) {
- g_warning("Refusing to insert '%s', not a regular file\n", tmp);
+ g_warning("refusing to insert '%s', not a regular file", tmp);
} else if (!g_file_get_contents(tmp, body, NULL, NULL)) {
g_warning("couldn't set insert file '%s' in body", value);
}
g_print("Refusing to attach '%s', potential private data leak\n",
tmp);
g_free(tmp);
- g_free(my_att);
tmp = NULL;
break;
}
my_att[num_attach] = NULL;
*attach = my_att;
}
+ else
+ g_free(my_att);
} else if (inreplyto && !*inreplyto &&
!g_ascii_strcasecmp(field, "in-reply-to")) {
*inreplyto = decode_uri_gdup(value);
return g_file_test(file, G_FILE_TEST_IS_REGULAR);
}
-gboolean dirent_is_regular_file(struct dirent *d)
-{
-#if !defined(G_OS_WIN32) && defined(HAVE_DIRENT_D_TYPE)
- if (d->d_type == DT_REG)
- return TRUE;
- else if (d->d_type != DT_UNKNOWN)
- return FALSE;
-#endif
-
- return g_file_test(d->d_name, G_FILE_TEST_IS_REGULAR);
-}
-
gint change_dir(const gchar *dir)
{
gchar *prevdir = NULL;
if ((ret = remove_dir_recursive(dir_name)) < 0) {
g_warning("can't remove directory: %s", dir_name);
+ g_dir_close(dp);
return ret;
}
} else {
{
gchar buf[BUFFSIZE];
GString *str;
- gchar *ret;
str = g_string_new(NULL);
g_string_append(str, "\r\n");
}
- ret = str->str;
- g_string_free(str, FALSE);
-
- return ret;
+ return g_string_free(str, FALSE);
}
/*
gchar **argv;
gint ret;
- debug_print("execute_command_line(): executing: %s\n", cmdline?cmdline:"(null)");
+ cm_return_val_if_fail(cmdline != NULL, -1);
+
+ debug_print("execute_command_line(): executing: %s\n", cmdline);
argv = strsplit_with_quote(cmdline, " ", 0);
return child_stdout;
}
+FILE *get_command_output_stream(const char* cmdline)
+{
+ GPid pid;
+ GError *err = NULL;
+ gchar **argv = NULL;
+ int fd;
+
+ cm_return_val_if_fail(cmdline != NULL, NULL);
+
+ debug_print("get_command_output_stream(): executing: %s\n", cmdline);
+
+ /* turn the command-line string into an array */
+ if (!g_shell_parse_argv(cmdline, NULL, &argv, &err)) {
+ g_warning("could not parse command line from '%s': %s", cmdline, err->message);
+ g_error_free(err);
+ return NULL;
+ }
+
+ if (!g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
+ NULL, NULL, &pid, NULL, &fd, NULL, &err)
+ && err)
+ {
+ g_warning("could not spawn '%s': %s", cmdline, err->message);
+ g_error_free(err);
+ g_strfreev(argv);
+ return NULL;
+ }
+
+ g_strfreev(argv);
+ return fdopen(fd, "r");
+}
+
+#ifndef G_OS_WIN32
static gint is_unchanged_uri_char(char c)
{
switch (c) {
}
encoded_uri[k] = 0;
}
+#endif
gint open_uri(const gchar *uri, const gchar *cmdline)
{
return debug_mode;
}
+#ifdef HAVE_VA_OPT
+void debug_print_real(const char *file, int line, const gchar *format, ...)
+{
+ va_list args;
+ gchar buf[BUFFSIZE];
+ gint prefix_len;
+
+ if (!debug_mode) return;
+
+ prefix_len = g_snprintf(buf, sizeof(buf), "%s:%d:", debug_srcname(file), line);
+
+ va_start(args, format);
+ g_vsnprintf(buf + prefix_len, sizeof(buf) - prefix_len, format, args);
+ va_end(args);
+
+ g_print("%s", buf);
+}
+#else
void debug_print_real(const gchar *format, ...)
{
va_list args;
g_print("%s", buf);
}
+#endif
const char * debug_srcname(const char *file)
g_free(ptr);
}
-void replace_returns(gchar *str)
-{
- if (!str)
- return;
-
- while (strstr(str, "\n")) {
- *strstr(str, "\n") = ' ';
- }
- while (strstr(str, "\r")) {
- *strstr(str, "\r") = ' ';
- }
-}
-
/* get_uri_part() - retrieves a URI starting from scanpos.
Returns TRUE if successful */
gboolean get_uri_part(const gchar *start, const gchar *scanpos,
* should pass some URI type to this function and decide on that whether
* to perform punctuation stripping */
-#define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("/?=-_~)", ch))
+#define IS_REAL_PUNCT(ch) (g_ascii_ispunct(ch) && !strchr("$/?=-_~)", ch))
for (; ep_ - 1 > scanpos + 1 &&
IS_REAL_PUNCT(*(ep_ - 1));
else {
if(claws_fputs(buf, outfp) == EOF) {
err = TRUE;
+ g_strfreev(parts);
break;
}
}
{
FILE *fp = NULL;
gchar buffer[2048];
- gint i = 0;
gint score = 0;
if (filename == NULL)
return FALSE;
if ((fp = claws_fopen(filename, "rb")) == NULL)
return FALSE;
- while (i < 60 && score < 3
+ while (score < 3
&& claws_fgets(buffer, sizeof (buffer), fp) != NULL) {
if (!strncmp(buffer, "From:", strlen("From:")))
score++;
score++;
else if (!strncmp(buffer, "Subject:", strlen("Subject:")))
score++;
- i++;
+ else if (!strcmp(buffer, "\r\n")) {
+ debug_print("End of headers\n");
+ break;
+ }
}
claws_fclose(fp);
return (score >= 3);
*curpos++ = '0'+(lt->tm_min % 10);
break;
case 's':
- snprintf(subbuf, 64, "%lld", (long long)mktime(lt));
+ snprintf(subbuf, 64, "%" CM_TIME_FORMAT, mktime(lt));
len = strlen(subbuf); CHECK_SIZE();
strncpy2(curpos, subbuf, buflen - total_done);
break;
#define WEXITSTATUS(x) (x)
#endif
-GMutex *cm_mutex_new(void) {
-#if GLIB_CHECK_VERSION(2,32,0)
- GMutex *m = g_new0(GMutex, 1);
- g_mutex_init(m);
- return m;
-#else
- return g_mutex_new();
-#endif
-}
-
-void cm_mutex_free(GMutex *mutex) {
-#if GLIB_CHECK_VERSION(2,32,0)
- g_mutex_clear(mutex);
- g_free(mutex);
-#else
- g_mutex_free(mutex);
-#endif
-}
-
static gchar *canonical_list_to_file(GSList *list)
{
GString *result = g_string_new(NULL);
GSList *pathlist = g_slist_reverse(g_slist_copy(list));
GSList *cur;
- gchar *str;
#ifndef G_OS_WIN32
result = g_string_append(result, G_DIR_SEPARATOR_S);
}
g_slist_free(pathlist);
- str = result->str;
- g_string_free(result, FALSE);
-
- return str;
+ return g_string_free(result, FALSE);
}
static GSList *cm_split_path(const gchar *filename, int depth)
else if (!strcmp(path_parts[i], "..")) {
if (i == 0) {
errno =ENOTDIR;
+ g_strfreev(path_parts);
return NULL;
}
else /* Remove the last inserted element */
g_free(tmp);
if (strlen(out) != *out_len) {
- g_warning ("strlen(out) %"G_GSIZE_FORMAT" != *out_len %"G_GSIZE_FORMAT, strlen(out), *out_len);
+ g_warning("strlen(out) %"G_GSIZE_FORMAT" != *out_len %"G_GSIZE_FORMAT, strlen(out), *out_len);
}
return out;
}
-#if !GLIB_CHECK_VERSION(2, 30, 0)
-/**
- * g_utf8_substring:
- * @str: a UTF-8 encoded string
- * @start_pos: a character offset within @str
- * @end_pos: another character offset within @str
- *
- * Copies a substring out of a UTF-8 encoded string.
- * The substring will contain @end_pos - @start_pos
- * characters.
- *
- * Returns: a newly allocated copy of the requested
- * substring. Free with g_free() when no longer needed.
- *
- * Since: GLIB 2.30
- */
-gchar *
-g_utf8_substring (const gchar *str,
- glong start_pos,
- glong end_pos)
-{
- gchar *start, *end, *out;
-
- start = g_utf8_offset_to_pointer (str, start_pos);
- end = g_utf8_offset_to_pointer (start, end_pos - start_pos);
-
- out = g_malloc (end - start + 1);
- memcpy (out, start, end - start);
- out[end - start] = 0;
-
- return out;
-}
-#endif
-
/* Attempts to read count bytes from a PRNG into memory area starting at buf.
* It is up to the caller to make sure there is at least count bytes
* available at buf. */