/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
* Copyright (C) 2012 Intel Corporation
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
* Authors: Rodrigo Moya
* Tristan Van Berkom
*/
#include "config.h"
#include
#include
#include
#include
#ifdef G_OS_WIN32
#include
#endif
#include
#include "e-source.h"
#include "e-source-enumtypes.h"
#include "e-source-registry.h"
#include "camel/camel.h"
#include "e-data-server-util.h"
/**
* e_get_user_cache_dir:
*
* Returns a base directory in which to store user-specific,
* non-essential cached data for Evolution or Evolution-Data-Server.
*
* The returned string is owned by libedataserver and must not be
* modified or freed.
*
* Returns: base directory for user-specific, non-essential data
*
* Since: 2.32
**/
const gchar *
e_get_user_cache_dir (void)
{
static gchar *dirname = NULL;
if (G_UNLIKELY (dirname == NULL)) {
const gchar *cache_dir = g_get_user_cache_dir ();
dirname = g_build_filename (cache_dir, "evolution", NULL);
g_mkdir_with_parents (dirname, 0700);
}
return dirname;
}
/**
* e_get_user_config_dir:
*
* Returns a base directory in which to store user-specific configuration
* information for Evolution or Evolution-Data-Server.
*
* The returned string is owned by libedataserver and must not be
* modified or freed.
*
* Returns: base directory for user-specific configuration information
*
* Since: 2.32
**/
const gchar *
e_get_user_config_dir (void)
{
static gchar *dirname = NULL;
if (G_UNLIKELY (dirname == NULL)) {
const gchar *config_dir = g_get_user_config_dir ();
dirname = g_build_filename (config_dir, "evolution", NULL);
g_mkdir_with_parents (dirname, 0700);
}
return dirname;
}
/**
* e_get_user_data_dir:
*
* Returns a base directory in which to store user-specific data for
* Evolution or Evolution-Data-Server.
*
* The returned string is owned by libedataserver and must not be
* modified or freed.
*
* Returns: base directory for user-specific data
*
* Since: 2.32
**/
const gchar *
e_get_user_data_dir (void)
{
static gchar *dirname = NULL;
if (G_UNLIKELY (dirname == NULL)) {
const gchar *data_dir = g_get_user_data_dir ();
dirname = g_build_filename (data_dir, "evolution", NULL);
g_mkdir_with_parents (dirname, 0700);
}
return dirname;
}
/**
* e_util_strv_equal:
* @v1: (allow-none): a %NULL-terminated string array, or %NULL
* @v2: (allow-none): another %NULL-terminated string array, or %NULL
*
* Compares @v1 and @v2 for equality, handling %NULL gracefully.
*
* The arguments types are generic for compatibility with #GEqualFunc.
*
* Returns: whether @v1 and @v2 are identical
*
* Since: 3.12
**/
gboolean
e_util_strv_equal (gconstpointer v1,
gconstpointer v2)
{
gchar **strv1 = (gchar **) v1;
gchar **strv2 = (gchar **) v2;
guint length1, length2, ii;
if (strv1 == strv2)
return TRUE;
if (strv1 == NULL || strv2 == NULL)
return FALSE;
length1 = g_strv_length (strv1);
length2 = g_strv_length (strv2);
if (length1 != length2)
return FALSE;
for (ii = 0; ii < length1; ii++) {
if (!g_str_equal (strv1[ii], strv2[ii]))
return FALSE;
}
return TRUE;
}
/**
* e_util_strdup_strip:
* @string: (allow-none): a string value, or %NULL
*
* Duplicates @string and strips off any leading or trailing whitespace.
* The resulting string is returned unless it is empty or %NULL, in which
* case the function returns %NULL.
*
* Free the returned string with g_free().
*
* Returns: a newly-allocated, stripped copy of @string, or %NULL
*
* Since: 3.6
**/
gchar *
e_util_strdup_strip (const gchar *string)
{
gchar *duplicate;
duplicate = g_strdup (string);
if (duplicate != NULL) {
g_strstrip (duplicate);
if (*duplicate == '\0') {
g_free (duplicate);
duplicate = NULL;
}
}
return duplicate;
}
/**
* e_util_strstrcase:
* @haystack: The string to search in.
* @needle: The string to search for.
*
* Find the first instance of @needle in @haystack, ignoring case for
* bytes that are ASCII characters.
*
* Returns: A pointer to the start of @needle in @haystack, or NULL if
* @needle is not found.
**/
gchar *
e_util_strstrcase (const gchar *haystack,
const gchar *needle)
{
/* find the needle in the haystack neglecting case */
const gchar *ptr;
guint len;
g_return_val_if_fail (haystack != NULL, NULL);
g_return_val_if_fail (needle != NULL, NULL);
len = strlen (needle);
if (len > strlen (haystack))
return NULL;
if (len == 0)
return (gchar *) haystack;
for (ptr = haystack; *(ptr + len - 1) != '\0'; ptr++)
if (!g_ascii_strncasecmp (ptr, needle, len))
return (gchar *) ptr;
return NULL;
}
/**
* e_util_unicode_get_utf8:
* @text: The string to take the UTF-8 character from.
* @out: The location to store the UTF-8 character in.
*
* Get a UTF-8 character from the beginning of @text.
*
* Returns: A pointer to the next character in @text after @out.
**/
gchar *
e_util_unicode_get_utf8 (const gchar *text,
gunichar *out)
{
g_return_val_if_fail (text != NULL, NULL);
g_return_val_if_fail (out != NULL, NULL);
*out = g_utf8_get_char (text);
return (*out == (gunichar) -1) ? NULL : g_utf8_next_char (text);
}
/**
* e_util_utf8_strstrcase:
* @haystack: The string to search in.
* @needle: The string to search for.
*
* Find the first instance of @needle in @haystack, ignoring case. (No
* proper case folding or decomposing is done.) Both @needle and
* @haystack are UTF-8 strings.
*
* Returns: A pointer to the first instance of @needle in @haystack, or
* %NULL if no match is found, or if either of the strings are
* not legal UTF-8 strings.
**/
const gchar *
e_util_utf8_strstrcase (const gchar *haystack,
const gchar *needle)
{
gunichar *nuni, unival;
gint nlen;
const gchar *o, *p;
if (haystack == NULL)
return NULL;
if (needle == NULL)
return NULL;
if (strlen (needle) == 0)
return haystack;
if (strlen (haystack) == 0)
return NULL;
nuni = g_alloca (sizeof (gunichar) * strlen (needle));
nlen = 0;
for (p = e_util_unicode_get_utf8 (needle, &unival);
p && unival;
p = e_util_unicode_get_utf8 (p, &unival)) {
nuni[nlen++] = g_unichar_tolower (unival);
}
/* NULL means there was illegal utf-8 sequence */
if (!p) return NULL;
o = haystack;
for (p = e_util_unicode_get_utf8 (o, &unival);
p && unival;
p = e_util_unicode_get_utf8 (p, &unival)) {
gunichar sc;
sc = g_unichar_tolower (unival);
/* We have valid stripped gchar */
if (sc == nuni[0]) {
const gchar *q = p;
gint npos = 1;
while (npos < nlen) {
q = e_util_unicode_get_utf8 (q, &unival);
if (!q || !unival) return NULL;
sc = g_unichar_tolower (unival);
if (sc != nuni[npos]) break;
npos++;
}
if (npos == nlen) {
return o;
}
}
o = p;
}
return NULL;
}
static gunichar
stripped_char (gunichar ch)
{
gunichar decomp[4];
gunichar retval;
GUnicodeType utype;
gsize dlen;
utype = g_unichar_type (ch);
switch (utype) {
case G_UNICODE_CONTROL:
case G_UNICODE_FORMAT:
case G_UNICODE_UNASSIGNED:
case G_UNICODE_SPACING_MARK:
/* Ignore those */
return 0;
default:
/* Convert to lowercase, fall through */
ch = g_unichar_tolower (ch);
case G_UNICODE_LOWERCASE_LETTER:
if ((dlen = g_unichar_fully_decompose (ch, FALSE, decomp, 4))) {
retval = decomp[0];
return retval;
}
break;
}
return 0;
}
/**
* e_util_utf8_strstrcasedecomp:
* @haystack: The string to search in.
* @needle: The string to search for.
*
* Find the first instance of @needle in @haystack, where both @needle
* and @haystack are UTF-8 strings. Both strings are stripped and
* decomposed for comparison, and case is ignored.
*
* Returns: A pointer to the first instance of @needle in @haystack, or
* %NULL if either of the strings are not legal UTF-8 strings.
**/
const gchar *
e_util_utf8_strstrcasedecomp (const gchar *haystack,
const gchar *needle)
{
gunichar *nuni;
gunichar unival;
gint nlen;
const gchar *o, *p;
if (haystack == NULL)
return NULL;
if (needle == NULL)
return NULL;
if (strlen (needle) == 0)
return haystack;
if (strlen (haystack) == 0)
return NULL;
nuni = g_alloca (sizeof (gunichar) * strlen (needle));
nlen = 0;
for (p = e_util_unicode_get_utf8 (needle, &unival);
p && unival;
p = e_util_unicode_get_utf8 (p, &unival)) {
gunichar sc;
sc = stripped_char (unival);
if (sc) {
nuni[nlen++] = sc;
}
}
/* NULL means there was illegal utf-8 sequence */
if (!p) return NULL;
/* If everything is correct, we have decomposed,
* lowercase, stripped needle */
if (nlen < 1)
return haystack;
o = haystack;
for (p = e_util_unicode_get_utf8 (o, &unival);
p && unival;
p = e_util_unicode_get_utf8 (p, &unival)) {
gunichar sc;
sc = stripped_char (unival);
if (sc) {
/* We have valid stripped gchar */
if (sc == nuni[0]) {
const gchar *q = p;
gint npos = 1;
while (npos < nlen) {
q = e_util_unicode_get_utf8 (q, &unival);
if (!q || !unival) return NULL;
sc = stripped_char (unival);
if ((!sc) || (sc != nuni[npos])) break;
npos++;
}
if (npos == nlen) {
return o;
}
}
}
o = p;
}
return NULL;
}
/**
* e_util_utf8_strcasecmp:
* @s1: a UTF-8 string
* @s2: another UTF-8 string
*
* Compares two UTF-8 strings using approximate case-insensitive ordering.
*
* Returns: < 0 if @s1 compares before @s2, 0 if they compare equal,
* > 0 if @s1 compares after @s2
**/
gint
e_util_utf8_strcasecmp (const gchar *s1,
const gchar *s2)
{
gchar *folded_s1, *folded_s2;
gint retval;
g_return_val_if_fail (s1 != NULL && s2 != NULL, -1);
if (strcmp (s1, s2) == 0)
return 0;
folded_s1 = g_utf8_casefold (s1, -1);
folded_s2 = g_utf8_casefold (s2, -1);
retval = g_utf8_collate (folded_s1, folded_s2);
g_free (folded_s2);
g_free (folded_s1);
return retval;
}
/**
* e_util_utf8_remove_accents:
* @str: a UTF-8 string, or %NULL
*
* Returns a newly-allocated copy of @str with accents removed.
*
* Returns: a newly-allocated string
*
* Since: 2.28
**/
gchar *
e_util_utf8_remove_accents (const gchar *str)
{
gchar *res;
gint i, j;
if (str == NULL)
return NULL;
res = g_utf8_normalize (str, -1, G_NORMALIZE_NFD);
if (!res)
return g_strdup (str);
for (i = 0, j = 0; res[i]; i++) {
if ((guchar) res[i] != 0xCC || res[i + 1] == 0) {
res[j] = res[i];
j++;
} else {
i++;
}
}
res[j] = 0;
return res;
}
/**
* e_util_utf8_make_valid:
* @str: a UTF-8 string
*
* Returns a newly-allocated copy of @str, with invalid characters
* replaced by Unicode replacement characters (U+FFFD).
* For %NULL @str returns newly allocated empty string ("").
*
* Returns: a newly-allocated string
*
* Since: 3.0
**/
gchar *
e_util_utf8_make_valid (const gchar *str)
{
if (!str)
return g_strdup ("");
return e_util_utf8_data_make_valid (str, strlen (str));
}
/**
* e_util_utf8_data_make_valid:
* @data: UTF-8 binary data
* @data_bytes: length of the binary data
*
* Returns a newly-allocated NULL-terminated string with invalid characters
* replaced by Unicode replacement characters (U+FFFD).
* For %NULL @data returns newly allocated empty string ("").
*
* Returns: a newly-allocated string
*
* Since: 3.6
*/
gchar *
e_util_utf8_data_make_valid (const gchar *data,
gsize data_bytes)
{
/* almost identical copy of glib's _g_utf8_make_valid() */
GString *string;
const gchar *remainder, *invalid;
gint remaining_bytes, valid_bytes;
if (!data)
return g_strdup ("");
string = NULL;
remainder = (gchar *) data,
remaining_bytes = data_bytes;
while (remaining_bytes != 0) {
if (g_utf8_validate (remainder, remaining_bytes, &invalid))
break;
valid_bytes = invalid - remainder;
if (string == NULL)
string = g_string_sized_new (remaining_bytes);
g_string_append_len (string, remainder, valid_bytes);
/* append U+FFFD REPLACEMENT CHARACTER */
g_string_append (string, "\357\277\275");
remaining_bytes -= valid_bytes + 1;
remainder = invalid + 1;
}
if (string == NULL)
return g_strndup ((gchar *) data, data_bytes);
g_string_append (string, remainder);
g_warn_if_fail (g_utf8_validate (string->str, -1, NULL));
return g_string_free (string, FALSE);
}
/**
* e_util_utf8_normalize:
* @str: a UTF-8 string
*
* Normalizes @str by making it all lower case and removing any accents from it.
*
* Returns: The normalized version of @str, or %NULL if @str was not valid UTF-8
*
* Since: 3.8
*/
gchar *
e_util_utf8_normalize (const gchar *str)
{
gchar *valid = NULL;
gchar *normal, *casefolded = NULL;
if (str == NULL)
return NULL;
if (!g_utf8_validate (str, -1, NULL)) {
valid = e_util_utf8_make_valid (str);
str = valid;
}
normal = e_util_utf8_remove_accents (str);
if (normal)
casefolded = g_utf8_casefold (normal, -1);
g_free (valid);
g_free (normal);
return casefolded;
}
/**
* e_util_ensure_gdbus_string:
* @str: a possibly invalid UTF-8 string, or %NULL
* @gdbus_str: return location for the corrected string
*
* If @str is a valid UTF-8 string, the function returns @str and does
* not set @gdbus_str.
*
* If @str is an invalid UTF-8 string, the function calls
* e_util_utf8_make_valid() and points @gdbus_str to the newly-allocated,
* valid UTF-8 string, and also returns it. The caller should free the
* string pointed to by @gdbus_str with g_free().
*
* If @str is %NULL, the function returns an empty string and does not
* set @gdbus_str.
*
* Admittedly, the function semantics are a little awkward. The example
* below illustrates the easiest way to cope with the @gdbus_str argument:
*
*
*
* const gchar *trusted_utf8;
* gchar *allocated = NULL;
*
* trusted_utf8 = e_util_ensure_gdbus_string (untrusted_utf8, &allocated);
*
* Do stuff with trusted_utf8, then clear it.
*
* trusted_utf8 = NULL;
*
* g_free (allocated);
* allocated = NULL;
*
*
*
* Returns: a valid UTF-8 string
*
* Since: 3.0
**/
const gchar *
e_util_ensure_gdbus_string (const gchar *str,
gchar **gdbus_str)
{
g_return_val_if_fail (gdbus_str != NULL, NULL);
*gdbus_str = NULL;
if (!str || !*str)
return "";
if (g_utf8_validate (str, -1, NULL))
return str;
*gdbus_str = e_util_utf8_make_valid (str);
return *gdbus_str;
}
/**
* e_strftime:
* @string: The string array to store the result in.
* @max: The size of array @s.
* @fmt: The formatting to use on @tm.
* @tm: The time value to format.
*
* This function is a wrapper around the strftime (3) function, which
* converts the %l and %k (12h and 24h) format variables
* if necessary.
*
* Returns: The number of characters placed in @s.
**/
gsize
e_strftime (gchar *string,
gsize max,
const gchar *fmt,
const struct tm *tm)
{
#ifndef HAVE_LKSTRFTIME
gchar *c, *ffmt, *ff;
#endif
gsize ret;
g_return_val_if_fail (string != NULL, 0);
g_return_val_if_fail (fmt != NULL, 0);
g_return_val_if_fail (tm != NULL, 0);
#ifdef HAVE_LKSTRFTIME
ret = strftime (string, max, fmt, tm);
#else
ffmt = g_strdup (fmt);
ff = ffmt;
while ((c = strstr (ff, "%l")) != NULL) {
c[1] = 'I';
ff = c;
}
ff = ffmt;
while ((c = strstr (ff, "%k")) != NULL) {
c[1] = 'H';
ff = c;
}
#ifdef G_OS_WIN32
/* The Microsoft strftime () doesn't have %e either */
ff = ffmt;
while ((c = strstr (ff, "%e")) != NULL) {
c[1] = 'd';
ff = c;
}
#endif
ret = strftime (string, max, ffmt, tm);
g_free (ffmt);
#endif
if (ret == 0 && max > 0)
string[0] = '\0';
return ret;
}
/**
* e_utf8_strftime:
* @string: The string array to store the result in.
* @max: The size of array @s.
* @fmt: The formatting to use on @tm.
* @tm: The time value to format.
*
* The UTF-8 equivalent of e_strftime ().
*
* Returns: The number of characters placed in @s.
**/
gsize
e_utf8_strftime (gchar *string,
gsize max,
const gchar *fmt,
const struct tm *tm)
{
gsize sz, ret;
gchar *locale_fmt, *buf;
g_return_val_if_fail (string != NULL, 0);
g_return_val_if_fail (fmt != NULL, 0);
g_return_val_if_fail (tm != NULL, 0);
locale_fmt = g_locale_from_utf8 (fmt, -1, NULL, &sz, NULL);
if (!locale_fmt)
return 0;
ret = e_strftime (string, max, locale_fmt, tm);
if (!ret) {
g_free (locale_fmt);
return 0;
}
buf = g_locale_to_utf8 (string, ret, NULL, &sz, NULL);
if (!buf) {
g_free (locale_fmt);
return 0;
}
if (sz >= max) {
gchar *tmp = buf + max - 1;
tmp = g_utf8_find_prev_char (buf, tmp);
if (tmp)
sz = tmp - buf;
else
sz = 0;
}
memcpy (string, buf, sz);
string[sz] = '\0';
g_free (locale_fmt);
g_free (buf);
return sz;
}
/**
* e_util_gthread_id:
* @thread: A #GThread pointer
*
* Returns a 64-bit integer hopefully uniquely identifying the
* thread. To be used in debugging output and logging only.
* The returned value is just a cast of a pointer to the 64-bit integer.
*
* There is no guarantee that calling e_util_gthread_id () on one
* thread first and later after that thread has dies on another won't
* return the same integer.
*
* On Linux and Win32, known to really return a unique id for each
* thread existing at a certain time. No guarantee that ids won't be
* reused after a thread has terminated, though.
*
* Returns: A 64-bit integer.
*
* Since: 2.32
*/
guint64
e_util_gthread_id (GThread *thread)
{
#if GLIB_SIZEOF_VOID_P == 8
/* 64-bit Windows */
return (guint64) thread;
#else
return (gint) thread;
#endif
}
/* This only makes a filename safe for usage as a filename.
* It still may have shell meta-characters in it. */
/* This code is rather misguided and mostly pointless, but can't be
* changed because of backward compatibility, I guess.
*
* It replaces some perfectly safe characters like '%' with an
* underscore. (Recall that on Unix, the only bytes not allowed in a
* file name component are '\0' and '/'.) On the other hand, the UTF-8
* for a printable non-ASCII Unicode character (that thus consists of
* several very nonprintable non-ASCII bytes) is let through as
* such. But those bytes are of course also allowed in filenames, so
* it doesn't matter as such...
*/
void
e_filename_make_safe (gchar *string)
{
gchar *p, *ts;
gunichar c;
#ifdef G_OS_WIN32
const gchar *unsafe_chars = " /'\"`&();|<>$%{}!\\:*?#";
#else
const gchar *unsafe_chars = " /'\"`&();|<>$%{}!#";
#endif
g_return_if_fail (string != NULL);
p = string;
while (p && *p) {
c = g_utf8_get_char (p);
ts = p;
p = g_utf8_next_char (p);
/* I wonder what this code is supposed to actually
* achieve, and whether it does that as currently
* written?
*/
if (!g_unichar_isprint (c) ||
(c < 0xff && strchr (unsafe_chars, c & 0xff))) {
while (ts < p)
*ts++ = '_';
}
}
}
/**
* e_filename_mkdir_encoded:
* @basepath: base path of a file name; this is left unchanged
* @fileprefix: prefix for the filename; this is encoded
* @filename: file name to use; this is encoded; can be %NULL
* @fileindex: used when @filename is NULL, then the filename
* is generated as "file" + fileindex
*
* Creates a local path constructed from @basepath / @fileprefix + "-" + @filename,
* and makes sure the path @basepath exists. If creation of
* the path fails, then NULL is returned.
*
* Returns: Full local path like g_build_filename() except that @fileprefix
* and @filename are encoded to create a proper file elements for
* a file system. Free returned pointer with g_free().
*
* Since: 3.4
**/
gchar *
e_filename_mkdir_encoded (const gchar *basepath,
const gchar *fileprefix,
const gchar *filename,
gint fileindex)
{
gchar *elem1, *elem2, *res, *fn;
g_return_val_if_fail (basepath != NULL, NULL);
g_return_val_if_fail (*basepath != 0, NULL);
g_return_val_if_fail (fileprefix != NULL, NULL);
g_return_val_if_fail (*fileprefix != 0, NULL);
g_return_val_if_fail (!filename || *filename, NULL);
if (g_mkdir_with_parents (basepath, 0700) < 0)
return NULL;
elem1 = g_strdup (fileprefix);
if (filename)
elem2 = g_strdup (filename);
else
elem2 = g_strdup_printf ("file%d", fileindex);
e_filename_make_safe (elem1);
e_filename_make_safe (elem2);
fn = g_strconcat (elem1, "-", elem2, NULL);
res = g_build_filename (basepath, fn, NULL);
g_free (fn);
g_free (elem1);
g_free (elem2);
return res;
}
/**
* e_util_slist_to_strv:
* @strings: (element-type utf8): a #GSList of strings (const gchar *)
*
* Convert list of strings into NULL-terminates array of strings.
*
* Returns: (transfer full): Newly allocated %NULL-terminated array of strings.
* Returned pointer should be freed with g_strfreev().
*
* Note: Pair function for this is e_util_strv_to_slist().
*
* Since: 3.4
**/
gchar **
e_util_slist_to_strv (const GSList *strings)
{
const GSList *iter;
GPtrArray *array;
array = g_ptr_array_sized_new (g_slist_length ((GSList *) strings) + 1);
for (iter = strings; iter; iter = iter->next) {
const gchar *str = iter->data;
if (str)
g_ptr_array_add (array, g_strdup (str));
}
/* NULL-terminated */
g_ptr_array_add (array, NULL);
return (gchar **) g_ptr_array_free (array, FALSE);
}
/**
* e_util_strv_to_slist:
* @strv: a NULL-terminated array of strings (const gchar *)
*
* Convert NULL-terminated array of strings to a list of strings.
*
* Returns: (transfer full) (element-type utf8): Newly allocated #GSList of
* newly allocated strings. The returned pointer should be freed with
* e_util_free_string_slist().
*
* Note: Pair function for this is e_util_slist_to_strv().
*
* Since: 3.4
**/
GSList *
e_util_strv_to_slist (const gchar * const *strv)
{
GSList *slist = NULL;
gint ii;
if (!strv)
return NULL;
for (ii = 0; strv[ii]; ii++) {
slist = g_slist_prepend (slist, g_strdup (strv[ii]));
}
return g_slist_reverse (slist);
}
/**
* e_util_copy_string_slist:
* @copy_to: (element-type utf8) (allow-none): Where to copy; can be %NULL
* @strings: (element-type utf8): #GSList of strings to be copied
*
* Copies #GSList of strings at the end of @copy_to.
*
* Returns: (transfer full) (element-type utf8): New head of @copy_to.
* Returned pointer can be freed with e_util_free_string_slist().
*
* Since: 3.4
*
* Deprecated: 3.8: Use g_slist_copy_deep() instead, and optionally
* g_slist_concat() to concatenate the copied list
* to another #GSList.
**/
GSList *
e_util_copy_string_slist (GSList *copy_to,
const GSList *strings)
{
GSList *copied_list;
copied_list = g_slist_copy_deep (
(GSList *) strings, (GCopyFunc) g_strdup, NULL);
return g_slist_concat (copy_to, copied_list);
}
/**
* e_util_copy_object_slist:
* @copy_to: (element-type GObject) (allow-none): Where to copy; can be %NULL
* @objects: (element-type GObject): #GSList of #GObjects to be copied
*
* Copies #GSList of #GObjects at the end of @copy_to.
*
* Returns: (transfer full) (element-type GObject): New head of @copy_to.
* Returned pointer can be freed with e_util_free_object_slist().
*
* Since: 3.4
*
* Deprecated: 3.8: Use g_slist_copy_deep() instead, and optionally
* g_slist_concat() to concatenate the copied list
* to another #GSList.
**/
GSList *
e_util_copy_object_slist (GSList *copy_to,
const GSList *objects)
{
GSList *copied_list;
copied_list = g_slist_copy_deep (
(GSList *) objects, (GCopyFunc) g_object_ref, NULL);
return g_slist_concat (copy_to, copied_list);
}
/**
* e_util_free_string_slist:
* @strings: (element-type utf8): a #GSList of strings (gchar *)
*
* Frees memory previously allocated by e_util_strv_to_slist().
*
* Since: 3.4
*
* Deprecated: 3.8: Use g_slist_free_full() instead.
**/
void
e_util_free_string_slist (GSList *strings)
{
g_slist_free_full (strings, (GDestroyNotify) g_free);
}
/**
* e_util_free_object_slist:
* @objects: (element-type GObject): a #GSList of #GObjects
*
* Calls g_object_unref() on each member of @objects and then frees
* also @objects itself.
*
* Since: 3.4
*
* Deprecated: 3.8: Use g_slist_free_full() instead.
**/
void
e_util_free_object_slist (GSList *objects)
{
g_slist_free_full (objects, (GDestroyNotify) g_object_unref);
}
/**
* e_util_free_nullable_object_slist:
* @objects: (element-type GObject): a #GSList of nullable #GObjects
*
* Calls g_object_unref() on each member of @objects if non-%NULL and then frees
* also @objects itself.
*
* Since: 3.6
**/
void
e_util_free_nullable_object_slist (GSList *objects)
{
const GSList *l;
for (l = objects; l; l = l->next) {
if (l->data)
g_object_unref (l->data);
}
g_slist_free (objects);
}
/**
* e_util_safe_free_string:
* @str: a string to free
*
* Calls g_free() on @string, but before it rewrites its content with zeros.
* This is suitable to free strings with passwords.
*
* Since: 3.16
**/
void
e_util_safe_free_string (gchar *str)
{
if (!str)
return;
if (*str)
memset (str, 0, sizeof (gchar) * strlen (str));
g_free (str);
}
/**
* e_queue_transfer:
* @src_queue: a source #GQueue
* @dst_queue: a destination #GQueue
*
* Transfers the contents of @src_queue to the tail of @dst_queue.
* When the operation is complete, @src_queue will be empty.
*
* Since: 3.8
**/
void
e_queue_transfer (GQueue *src_queue,
GQueue *dst_queue)
{
g_return_if_fail (src_queue != NULL);
g_return_if_fail (dst_queue != NULL);
dst_queue->head = g_list_concat (dst_queue->head, src_queue->head);
dst_queue->tail = g_list_last (dst_queue->head);
dst_queue->length += src_queue->length;
src_queue->head = NULL;
src_queue->tail = NULL;
src_queue->length = 0;
}
/**
* e_weak_ref_new:
* @object: (allow-none): a #GObject or %NULL
*
* Allocates a new #GWeakRef and calls g_weak_ref_set() with @object.
*
* Free the returned #GWeakRef with e_weak_ref_free().
*
* Returns: a new #GWeakRef
*
* Since: 3.10
**/
GWeakRef *
e_weak_ref_new (gpointer object)
{
GWeakRef *weak_ref;
weak_ref = g_slice_new0 (GWeakRef);
g_weak_ref_init (weak_ref, object);
return weak_ref;
}
/**
* e_weak_ref_free:
* @weak_ref: a #GWeakRef
*
* Frees a #GWeakRef created by e_weak_ref_new().
*
* Since: 3.10
**/
void
e_weak_ref_free (GWeakRef *weak_ref)
{
g_return_if_fail (weak_ref != NULL);
g_weak_ref_clear (weak_ref);
g_slice_free (GWeakRef, weak_ref);
}
/* Helper for e_file_recursive_delete() */
static void
file_recursive_delete_thread (GSimpleAsyncResult *simple,
GObject *object,
GCancellable *cancellable)
{
GError *error = NULL;
e_file_recursive_delete_sync (G_FILE (object), cancellable, &error);
if (error != NULL)
g_simple_async_result_take_error (simple, error);
}
/**
* e_file_recursive_delete_sync:
* @file: a #GFile to delete
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Deletes @file. If @file is a directory, its contents are deleted
* recursively before @file itself is deleted. The recursive delete
* operation will stop on the first error.
*
* If @cancellable is not %NULL, then the operation can be cancelled
* by triggering the cancellable object from another thread. If the
* operation was cancelled, the error #G_IO_ERROR_CANCELLED will be
* returned.
*
* Returns: %TRUE if the file was deleted, %FALSE otherwise
*
* Since: 3.6
**/
gboolean
e_file_recursive_delete_sync (GFile *file,
GCancellable *cancellable,
GError **error)
{
GFileEnumerator *file_enumerator;
GFileInfo *file_info;
GFileType file_type;
gboolean success = TRUE;
GError *local_error = NULL;
g_return_val_if_fail (G_IS_FILE (file), FALSE);
file_type = g_file_query_file_type (
file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable);
/* If this is not a directory, delete like normal. */
if (file_type != G_FILE_TYPE_DIRECTORY)
return g_file_delete (file, cancellable, error);
/* Note, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS is critical here
* so we only delete files inside the directory being deleted. */
file_enumerator = g_file_enumerate_children (
file, G_FILE_ATTRIBUTE_STANDARD_NAME,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable, error);
if (file_enumerator == NULL)
return FALSE;
file_info = g_file_enumerator_next_file (
file_enumerator, cancellable, &local_error);
while (file_info != NULL) {
GFile *child;
const gchar *name;
name = g_file_info_get_name (file_info);
/* Here's the recursive part. */
child = g_file_get_child (file, name);
success = e_file_recursive_delete_sync (
child, cancellable, error);
g_object_unref (child);
g_object_unref (file_info);
if (!success)
break;
file_info = g_file_enumerator_next_file (
file_enumerator, cancellable, &local_error);
}
if (local_error != NULL) {
g_propagate_error (error, local_error);
success = FALSE;
}
g_object_unref (file_enumerator);
if (!success)
return FALSE;
/* The directory should be empty now. */
return g_file_delete (file, cancellable, error);
}
/**
* e_file_recursive_delete:
* @file: a #GFile to delete
* @io_priority: the I/O priority of the request
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* Asynchronously deletes @file. If @file is a directory, its contents
* are deleted recursively before @file itself is deleted. The recursive
* delete operation will stop on the first error.
*
* If @cancellable is not %NULL, then the operation can be cancelled
* by triggering the cancellable object before the operation finishes.
*
* When the operation is finished, @callback will be called. You can then
* call e_file_recursive_delete_finish() to get the result of the operation.
*
* Since: 3.6
**/
void
e_file_recursive_delete (GFile *file,
gint io_priority,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
g_return_if_fail (G_IS_FILE (file));
simple = g_simple_async_result_new (
G_OBJECT (file), callback, user_data,
e_file_recursive_delete);
g_simple_async_result_set_check_cancellable (simple, cancellable);
g_simple_async_result_run_in_thread (
simple, file_recursive_delete_thread,
io_priority, cancellable);
g_object_unref (simple);
}
/**
* e_file_recursive_delete_finish:
* @file: a #GFile to delete
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with e_file_recursive_delete().
*
* If the operation was cancelled, the error #G_IO_ERROR_CANCELLED will be
* returned.
*
* Returns: %TRUE if the file was deleted, %FALSE otherwise
*
* Since: 3.6
**/
gboolean
e_file_recursive_delete_finish (GFile *file,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
g_return_val_if_fail (
g_simple_async_result_is_valid (
result, G_OBJECT (file), e_file_recursive_delete), FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
/* Assume success unless a GError is set. */
return !g_simple_async_result_propagate_error (simple, error);
}
/**
* e_binding_bind_property:
*
* Thread safe variant of g_object_bind_property(). See its documentation
* for more information on arguments and return value.
*
* Since: 3.16
**/
GBinding *
e_binding_bind_property (gpointer source,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags)
{
return camel_binding_bind_property (source, source_property, target, target_property, flags);
}
/**
* e_binding_bind_property_full:
*
* Thread safe variant of g_object_bind_property_full(). See its documentation
* for more information on arguments and return value.
*
* Since: 3.16
**/
GBinding *
e_binding_bind_property_full (gpointer source,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GBindingTransformFunc transform_to,
GBindingTransformFunc transform_from,
gpointer user_data,
GDestroyNotify notify)
{
return camel_binding_bind_property_full (source, source_property, target, target_property, flags,
transform_to, transform_from, user_data, notify);
}
/**
* e_binding_bind_property_with_closures:
*
* Thread safe variant of g_object_bind_property_with_closures(). See its
* documentation for more information on arguments and return value.
*
* Since: 3.16
**/
GBinding *
e_binding_bind_property_with_closures (gpointer source,
const gchar *source_property,
gpointer target,
const gchar *target_property,
GBindingFlags flags,
GClosure *transform_to,
GClosure *transform_from)
{
return camel_binding_bind_property_with_closures (source, source_property, target, target_property, flags,
transform_to, transform_from);
}
/**
* e_binding_transform_enum_value_to_nick:
* @binding: a #GBinding
* @source_value: a #GValue whose type is derived from #G_TYPE_ENUM
* @target_value: a #GValue of type #G_TYPE_STRING
* @not_used: not used
*
* Transforms an enumeration value to its corresponding nickname.
*
* Returns: %TRUE if the enum value has a corresponding nickname
*
* Since: 3.4
**/
gboolean
e_binding_transform_enum_value_to_nick (GBinding *binding,
const GValue *source_value,
GValue *target_value,
gpointer not_used)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
gint value;
gboolean success = FALSE;
g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
enum_class = g_type_class_peek (G_VALUE_TYPE (source_value));
g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), FALSE);
value = g_value_get_enum (source_value);
enum_value = g_enum_get_value (enum_class, value);
if (enum_value != NULL) {
g_value_set_string (target_value, enum_value->value_nick);
success = TRUE;
}
return success;
}
/**
* e_binding_transform_enum_nick_to_value:
* @binding: a #GBinding
* @source_value: a #GValue of type #G_TYPE_STRING
* @target_value: a #GValue whose type is derived from #G_TYPE_ENUM
* @not_used: not used
*
* Transforms an enumeration nickname to its corresponding value.
*
* Returns: %TRUE if the enum nickname has a corresponding value
*
* Since: 3.4
**/
gboolean
e_binding_transform_enum_nick_to_value (GBinding *binding,
const GValue *source_value,
GValue *target_value,
gpointer not_used)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
const gchar *string;
gboolean success = FALSE;
g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
enum_class = g_type_class_peek (G_VALUE_TYPE (target_value));
g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), FALSE);
string = g_value_get_string (source_value);
enum_value = g_enum_get_value_by_nick (enum_class, string);
if (enum_value != NULL) {
g_value_set_enum (target_value, enum_value->value);
success = TRUE;
}
return success;
}
/**
* e_enum_from_string:
* @enum_type: The enum type
* @string: The string containing the enum value or nick
* @enum_value: A return location to store the result
*
* Fetches the appropriate enumeration value for @string in the given
* enum type @type and stores the result in @enum_value
*
* Returns: %TRUE if the string was a valid name or nick
* for the given @type, %FALSE if the conversion failed.
*
* Since: 3.8
*/
gboolean
e_enum_from_string (GType enum_type,
const gchar *string,
gint *enum_value)
{
GEnumClass *enum_class;
GEnumValue *ev;
gchar *endptr;
gint value;
gboolean retval = TRUE;
g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), FALSE);
g_return_val_if_fail (string != NULL, FALSE);
value = g_ascii_strtoull (string, &endptr, 0);
if (endptr != string)
/* parsed a number */
*enum_value = value;
else {
enum_class = g_type_class_ref (enum_type);
ev = g_enum_get_value_by_name (enum_class, string);
if (!ev)
ev = g_enum_get_value_by_nick (enum_class, string);
if (ev)
*enum_value = ev->value;
else
retval = FALSE;
g_type_class_unref (enum_class);
}
return retval;
}
/**
* e_enum_to_string:
* @enum_type: An enum type
* @enum_value: The enum value to convert
*
* Converts an enum value to a string using strings from the GType system.
*
* Returns: the string representing @eval
*
* Since: 3.8
*/
const gchar *
e_enum_to_string (GType enum_type,
gint enum_value)
{
GEnumClass *enum_class;
const gchar *string = NULL;
guint i;
enum_class = g_type_class_ref (enum_type);
g_return_val_if_fail (enum_class != NULL, NULL);
for (i = 0; i < enum_class->n_values; i++) {
if (enum_value == enum_class->values[i].value) {
string = enum_class->values[i].value_nick;
break;
}
}
g_type_class_unref (enum_class);
return string;
}
/**
* EAsyncClosure:
*
* #EAsyncClosure provides a simple way to run an asynchronous function
* synchronously without blocking a running #GMainLoop or using threads.
*
* 1) Create an #EAsyncClosure with e_async_closure_new().
*
* 2) Call the asynchronous function passing e_async_closure_callback() as
* the #GAsyncReadyCallback argument and the #EAsyncClosure as the data
* argument.
*
* 3) Call e_async_closure_wait() and collect the #GAsyncResult.
*
* 4) Call the corresponding asynchronous "finish" function, passing the
* #GAsyncResult returned by e_async_closure_wait().
*
* 5) If needed, repeat steps 2-4 for additional asynchronous functions
* using the same #EAsyncClosure.
*
* 6) Finally, free the #EAsyncClosure with e_async_closure_free().
*
* Since: 3.6
**/
struct _EAsyncClosure {
GMainLoop *loop;
GMainContext *context;
GAsyncResult *result;
gboolean finished;
GMutex lock;
};
/**
* e_async_closure_new:
*
* Creates a new #EAsyncClosure for use with asynchronous functions.
*
* Returns: a new #EAsyncClosure
*
* Since: 3.6
**/
EAsyncClosure *
e_async_closure_new (void)
{
EAsyncClosure *closure;
closure = g_slice_new0 (EAsyncClosure);
closure->context = g_main_context_new ();
closure->loop = g_main_loop_new (closure->context, FALSE);
closure->finished = FALSE;
g_mutex_init (&closure->lock);
g_main_context_push_thread_default (closure->context);
return closure;
}
static gboolean
e_async_closure_unlock_mutex_cb (gpointer user_data)
{
EAsyncClosure *closure = user_data;
g_return_val_if_fail (closure != NULL, FALSE);
g_mutex_unlock (&closure->lock);
return FALSE;
}
/**
* e_async_closure_wait:
* @closure: an #EAsyncClosure
*
* Call this function immediately after starting an asynchronous operation.
* The function waits for the asynchronous operation to complete and returns
* its #GAsyncResult to be passed to the operation's "finish" function.
*
* This function can be called repeatedly on the same #EAsyncClosure to
* easily string together multiple asynchronous operations.
*
* Returns: (transfer none): a #GAsyncResult which is owned by the closure
*
* Since: 3.6
**/
GAsyncResult *
e_async_closure_wait (EAsyncClosure *closure)
{
g_return_val_if_fail (closure != NULL, NULL);
g_mutex_lock (&closure->lock);
if (closure->finished) {
g_mutex_unlock (&closure->lock);
} else {
GSource *idle_source;
/* Unlock the closure->lock in the main loop, to ensure thread safety.
It should be processed before anything else, otherwise deadlock happens. */
idle_source = g_idle_source_new ();
g_source_set_callback (idle_source, e_async_closure_unlock_mutex_cb, closure, NULL);
g_source_set_priority (idle_source, G_PRIORITY_HIGH * 2);
g_source_attach (idle_source, closure->context);
g_source_unref (idle_source);
g_main_loop_run (closure->loop);
}
return closure->result;
}
/**
* e_async_closure_free:
* @closure: an #EAsyncClosure
*
* Frees the @closure and the resources it holds.
*
* Since: 3.6
**/
void
e_async_closure_free (EAsyncClosure *closure)
{
g_return_if_fail (closure != NULL);
g_main_context_pop_thread_default (closure->context);
g_main_loop_unref (closure->loop);
g_main_context_unref (closure->context);
g_mutex_lock (&closure->lock);
g_clear_object (&closure->result);
g_mutex_unlock (&closure->lock);
g_mutex_clear (&closure->lock);
g_slice_free (EAsyncClosure, closure);
}
/**
* e_async_closure_callback:
* @object: a #GObject or %NULL, it is not used by the function at all
* @result: a #GAsyncResult
* @closure: an #EAsyncClosure
*
* Pass this function as the #GAsyncReadyCallback argument of an asynchronous
* function, and the #EAsyncClosure as the data argument.
*
* This causes e_async_closure_wait() to terminate and return @result.
*
* Since: 3.6
**/
void
e_async_closure_callback (GObject *object,
GAsyncResult *result,
gpointer closure)
{
EAsyncClosure *real_closure;
g_return_if_fail (G_IS_ASYNC_RESULT (result));
g_return_if_fail (closure != NULL);
real_closure = closure;
g_mutex_lock (&real_closure->lock);
/* Replace any previous result. */
if (real_closure->result != NULL)
g_object_unref (real_closure->result);
real_closure->result = g_object_ref (result);
real_closure->finished = TRUE;
g_mutex_unlock (&real_closure->lock);
g_main_loop_quit (real_closure->loop);
}
#ifdef G_OS_WIN32
#include
#include
#include
#include
#ifndef PROCESS_DEP_ENABLE
#define PROCESS_DEP_ENABLE 0x00000001
#endif
#ifndef PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
#define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION 0x00000002
#endif
static const gchar *prefix = NULL;
static const gchar *cp_prefix;
static const gchar *localedir;
static const gchar *extensiondir;
static const gchar *imagesdir;
static const gchar *ui_uidir;
static const gchar *credentialmoduledir;
static HMODULE hmodule;
G_LOCK_DEFINE_STATIC (mutex);
/* Silence gcc with a prototype. Yes, this is silly. */
BOOL WINAPI DllMain (HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved);
/* Minimal DllMain that just tucks away the DLL's HMODULE */
BOOL WINAPI
DllMain (HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
hmodule = hinstDLL;
break;
}
return TRUE;
}
gchar *
e_util_replace_prefix (const gchar *configure_time_prefix,
const gchar *runtime_prefix,
const gchar *configure_time_path)
{
gchar *c_t_prefix_slash;
gchar *retval;
c_t_prefix_slash = g_strconcat (configure_time_prefix, "/", NULL);
if (runtime_prefix &&
!g_str_has_prefix (configure_time_path, c_t_prefix_slash)) {
gint ii;
gchar *path;
path = g_strdup (configure_time_path);
for (ii = 0; ii < 3; ii++) {
const gchar *pos;
gchar *last_slash;
last_slash = strrchr (path, '/');
if (!last_slash)
break;
*last_slash = '\0';
pos = strstr (configure_time_prefix, path);
if (pos && pos[strlen(path)] == '/') {
g_free (c_t_prefix_slash);
c_t_prefix_slash = g_strconcat (configure_time_prefix + (pos - configure_time_prefix), "/", NULL);
break;
}
}
g_free (path);
}
if (runtime_prefix &&
g_str_has_prefix (configure_time_path, c_t_prefix_slash)) {
retval = g_strconcat (
runtime_prefix,
configure_time_path + strlen (c_t_prefix_slash) - 1,
NULL);
} else
retval = g_strdup (configure_time_path);
g_free (c_t_prefix_slash);
return retval;
}
static gchar *
replace_prefix (const gchar *runtime_prefix,
const gchar *configure_time_path)
{
return e_util_replace_prefix (
E_DATA_SERVER_PREFIX, runtime_prefix, configure_time_path);
}
static void
setup (void)
{
gchar *full_pfx;
gchar *cp_pfx;
G_LOCK (mutex);
if (prefix != NULL) {
G_UNLOCK (mutex);
return;
}
/* This requires that the libedataserver DLL is installed in $bindir */
full_pfx = g_win32_get_package_installation_directory_of_module (hmodule);
cp_pfx = g_win32_locale_filename_from_utf8 (full_pfx);
prefix = g_strdup (full_pfx);
cp_prefix = g_strdup (cp_pfx);
g_free (full_pfx);
g_free (cp_pfx);
localedir = replace_prefix (cp_prefix, E_DATA_SERVER_LOCALEDIR);
extensiondir = replace_prefix (prefix, E_DATA_SERVER_EXTENSIONDIR);
imagesdir = replace_prefix (prefix, E_DATA_SERVER_IMAGESDIR);
ui_uidir = replace_prefix (prefix, E_DATA_SERVER_UI_UIDIR);
credentialmoduledir = replace_prefix (prefix, E_DATA_SERVER_CREDENTIALMODULEDIR);
G_UNLOCK (mutex);
}
#include "libedataserver-private.h" /* For prototypes */
#define GETTER_IMPL(varbl) \
{ \
setup (); \
return varbl; \
}
#define PRIVATE_GETTER(varbl) \
const gchar * \
_libedataserver_get_##varbl (void) \
GETTER_IMPL (varbl)
#define PUBLIC_GETTER(varbl) \
const gchar * \
e_util_get_##varbl (void) \
GETTER_IMPL (varbl)
PRIVATE_GETTER (extensiondir)
PRIVATE_GETTER (imagesdir)
PRIVATE_GETTER (ui_uidir)
PRIVATE_GETTER (credentialmoduledir);
PUBLIC_GETTER (prefix)
PUBLIC_GETTER (cp_prefix)
PUBLIC_GETTER (localedir)
/**
* e_util_win32_initialize:
*
* Initializes win32 environment. This might be called in main().
**/
void
e_util_win32_initialize (void)
{
gchar module_filename[2048 + 1];
DWORD chars;
/* Reduce risks */
{
typedef BOOL (WINAPI *t_SetDllDirectoryA) (LPCSTR lpPathName);
t_SetDllDirectoryA p_SetDllDirectoryA;
p_SetDllDirectoryA = GetProcAddress (
GetModuleHandle ("kernel32.dll"),
"SetDllDirectoryA");
if (p_SetDllDirectoryA != NULL)
p_SetDllDirectoryA ("");
}
#ifndef _WIN64
{
typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags);
t_SetProcessDEPPolicy p_SetProcessDEPPolicy;
p_SetProcessDEPPolicy = GetProcAddress (
GetModuleHandle ("kernel32.dll"),
"SetProcessDEPPolicy");
if (p_SetProcessDEPPolicy != NULL)
p_SetProcessDEPPolicy (
PROCESS_DEP_ENABLE |
PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
}
#endif
if (fileno (stdout) != -1 && _get_osfhandle (fileno (stdout)) != -1) {
/* stdout is fine, presumably redirected to a file or pipe */
} else {
typedef BOOL (* WINAPI AttachConsole_t) (DWORD);
AttachConsole_t p_AttachConsole =
(AttachConsole_t) GetProcAddress (
GetModuleHandle ("kernel32.dll"), "AttachConsole");
if (p_AttachConsole && p_AttachConsole (ATTACH_PARENT_PROCESS)) {
freopen ("CONOUT$", "w", stdout);
dup2 (fileno (stdout), 1);
freopen ("CONOUT$", "w", stderr);
dup2 (fileno (stderr), 2);
}
}
chars = GetModuleFileNameA (hmodule, module_filename, 2048);
if (chars > 0) {
gchar *path;
module_filename[chars] = '\0';
path = strrchr (module_filename, '\\');
if (path)
path[1] = '\0';
path = g_build_path (";", module_filename, g_getenv ("PATH"), NULL);
if (!g_setenv ("PATH", path, TRUE))
g_warning ("Could not set PATH for Evolution and its child processes");
g_free (path);
}
/* Make sure D-Bus is running. The executable makes sure the daemon
is not restarted, thus it's safe to be called witn D-Bus already
running. */
if (system ("dbus-launch.exe") != 0) {
/* Ignore, just to mute compiler warning */;
}
}
#endif /* G_OS_WIN32 */
static gint default_dbus_timeout = -1;
/**
* e_data_server_util_set_dbus_call_timeout:
* @timeout_msec: default timeout for D-Bus calls in miliseconds
*
* Sets default timeout, in milliseconds, for calls of g_dbus_proxy_call()
* family functions.
*
* -1 means the default value as set by D-Bus itself.
* G_MAXINT means no timeout at all.
*
* Default value is set also by configure option --with-dbus-call-timeout=ms
* and -1 is used when not set.
*
* Since: 3.0
*
* Deprecated: 3.8: This value is not used anywhere.
**/
void
e_data_server_util_set_dbus_call_timeout (gint timeout_msec)
{
default_dbus_timeout = timeout_msec;
}
/**
* e_data_server_util_get_dbus_call_timeout:
*
* Returns the value set by e_data_server_util_set_dbus_call_timeout().
*
* Returns: the D-Bus call timeout in milliseconds
*
* Since: 3.0
*
* Deprecated: 3.8: This value is not used anywhere.
**/
gint
e_data_server_util_get_dbus_call_timeout (void)
{
return default_dbus_timeout;
}
/**
* e_named_parameters_new:
*
* Creates a new instance of an #ENamedParameters. This should be freed
* with e_named_parameters_free(), when no longer needed. Names are
* compared case insensitively.
*
* The structure is not thread safe, if the caller requires thread safety,
* then it should provide it on its own.
*
* Returns: newly allocated #ENamedParameters
*
* Since: 3.8
**/
ENamedParameters *
e_named_parameters_new (void)
{
return (ENamedParameters *) g_ptr_array_new_with_free_func ((GDestroyNotify) e_util_safe_free_string);
}
/**
* e_named_parameters_new_strv:
* @strv: NULL-terminated string array to be used as a content of a newly
* created #ENamedParameters
*
* Creates a new instance of an #ENamedParameters, with initial content
* being taken from @strv. This should be freed with e_named_parameters_free(),
* when no longer needed. Names are compared case insensitively.
*
* The structure is not thread safe, if the caller requires thread safety,
* then it should provide it on its own.
*
* Returns: newly allocated #ENamedParameters
*
* Since: 3.8
**/
ENamedParameters *
e_named_parameters_new_strv (const gchar * const *strv)
{
ENamedParameters *parameters;
gint ii;
g_return_val_if_fail (strv != NULL, NULL);
parameters = e_named_parameters_new ();
for (ii = 0; strv[ii]; ii++) {
g_ptr_array_add ((GPtrArray *) parameters, g_strdup (strv[ii]));
}
return parameters;
}
/**
* e_named_parameters_new_string:
* @str: a string to be used as a content of a newly created #ENamedParameters
*
* Creates a new instance of an #ENamedParamters, with initial content being
* taken from @str. This should be freed with e_named_parameters_free(),
* when no longer needed. Names are compared case insensitively.
*
* The @str should be created with e_named_parameters_to_string(), to be
* properly encoded.
*
* The structure is not thread safe, if the caller requires thread safety,
* then it should provide it on its own.
*
* Returns: (transfer full): newly allocated #ENamedParameters
*
* Since: 3.18
**/
ENamedParameters *
e_named_parameters_new_string (const gchar *str)
{
ENamedParameters *parameters;
gchar **split;
gint ii;
g_return_val_if_fail (str != NULL, NULL);
split = g_strsplit (str, "\n", -1);
parameters = e_named_parameters_new ();
for (ii = 0; split && split[ii]; ii++) {
g_ptr_array_add ((GPtrArray *) parameters, g_strcompress (split[ii]));
}
g_strfreev (split);
return parameters;
}
/**
* e_named_parameters_new_clone:
* @parameters: an #ENamedParameters to be used as a content of a newly
* created #ENamedParameters
*
* Creates a new instance of an #ENamedParameters, with initial content
* being taken from @parameters. This should be freed with e_named_parameters_free(),
* when no longer needed. Names are compared case insensitively.
*
* The structure is not thread safe, if the caller requires thread safety,
* then it should provide it on its own.
*
* Returns: newly allocated #ENamedParameters
*
* Since: 3.16
**/
ENamedParameters *
e_named_parameters_new_clone (const ENamedParameters *parameters)
{
ENamedParameters *clone;
clone = e_named_parameters_new ();
if (parameters)
e_named_parameters_assign (clone, parameters);
return clone;
}
/**
* e_named_parameters_free:
* @parameters: an #ENamedParameters
*
* Frees an instance of #ENamedParameters, previously allocated
* with e_named_parameters_new(). Function does nothing, if
* @parameters is %NULL.
*
* Since: 3.8
**/
void
e_named_parameters_free (ENamedParameters *parameters)
{
if (!parameters)
return;
g_ptr_array_unref ((GPtrArray *) parameters);
}
/**
* e_named_parameters_clear:
* @parameters: an #ENamedParameters
*
* Removes all stored parameters from @parameters.
*
* Since: 3.8
**/
void
e_named_parameters_clear (ENamedParameters *parameters)
{
GPtrArray *array;
g_return_if_fail (parameters != NULL);
array = (GPtrArray *) parameters;
if (array->len)
g_ptr_array_remove_range (array, 0, array->len);
}
/**
* e_named_parameters_assign:
* @parameters: an #ENamedParameters to assign values to
* @from: (allow-none): an #ENamedParameters to get values from, or %NULL
*
* Makes content of the @parameters the same as @from.
* Functions clears content of @parameters if @from is %NULL.
*
* Since: 3.8
**/
void
e_named_parameters_assign (ENamedParameters *parameters,
const ENamedParameters *from)
{
g_return_if_fail (parameters != NULL);
e_named_parameters_clear (parameters);
if (from) {
gint ii;
GPtrArray *from_array = (GPtrArray *) from;
for (ii = 0; ii < from_array->len; ii++) {
g_ptr_array_add (
(GPtrArray *) parameters,
g_strdup (from_array->pdata[ii]));
}
}
}
static gint
get_parameter_index (const ENamedParameters *parameters,
const gchar *name)
{
GPtrArray *array;
gint ii, name_len;
g_return_val_if_fail (parameters != NULL, -1);
g_return_val_if_fail (name != NULL, -1);
name_len = strlen (name);
array = (GPtrArray *) parameters;
for (ii = 0; ii < array->len; ii++) {
const gchar *name_and_value = g_ptr_array_index (array, ii);
if (name_and_value == NULL || strlen (name_and_value) <= name_len)
continue;
if (name_and_value[name_len] != ':')
continue;
if (g_ascii_strncasecmp (name_and_value, name, name_len) == 0)
return ii;
}
return -1;
}
/**
* e_named_parameters_set:
* @parameters: an #ENamedParameters
* @name: name of a parameter to set
* @value: (allow-none): value to set, or %NULL to unset
*
* Sets parameter named @name to value @value. If @value is NULL,
* then the parameter is removed. @value can be an empty string.
*
* Note: There is a restriction on parameter names, it cannot be empty or
* contain a colon character (':'), otherwise it can be pretty much anything.
*
* Since: 3.8
**/
void
e_named_parameters_set (ENamedParameters *parameters,
const gchar *name,
const gchar *value)
{
GPtrArray *array;
gint index;
gchar *name_and_value;
g_return_if_fail (parameters != NULL);
g_return_if_fail (name != NULL);
g_return_if_fail (strchr (name, ':') == NULL);
g_return_if_fail (*name != '\0');
array = (GPtrArray *) parameters;
index = get_parameter_index (parameters, name);
if (!value) {
if (index != -1)
g_ptr_array_remove_index (array, index);
return;
}
name_and_value = g_strconcat (name, ":", value, NULL);
if (index != -1) {
g_free (array->pdata[index]);
array->pdata[index] = name_and_value;
} else {
g_ptr_array_add (array, name_and_value);
}
}
/**
* e_named_parameters_get:
* @parameters: an #ENamedParameters
* @name: name of a parameter to get
*
* Returns current value of a parameter with name @name. If not such
* exists, then returns %NULL.
*
* Returns: value of a parameter named @name, or %NULL.
*
* Since: 3.8
**/
const gchar *
e_named_parameters_get (const ENamedParameters *parameters,
const gchar *name)
{
gint index;
const gchar *name_and_value;
g_return_val_if_fail (parameters != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
index = get_parameter_index (parameters, name);
if (index == -1)
return NULL;
name_and_value = g_ptr_array_index ((GPtrArray *) parameters, index);
return name_and_value + strlen (name) + 1;
}
/**
* e_named_parameters_test:
* @parameters: an #ENamedParameters
* @name: name of a parameter to test
* @value: value to test
* @case_sensitively: whether to compare case sensitively
*
* Compares current value of parameter named @name with given @value
* and returns whether they are equal, either case sensitively or
* insensitively, based on @case_sensitively argument. Function
* returns %FALSE, if no such parameter exists.
*
* Returns: Whether parameter of given name has stored value of given value.
*
* Since: 3.8
**/
gboolean
e_named_parameters_test (const ENamedParameters *parameters,
const gchar *name,
const gchar *value,
gboolean case_sensitively)
{
const gchar *stored_value;
g_return_val_if_fail (parameters != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
stored_value = e_named_parameters_get (parameters, name);
if (!stored_value)
return FALSE;
if (case_sensitively)
return strcmp (stored_value, value) == 0;
return g_ascii_strcasecmp (stored_value, value) == 0;
}
/**
* e_named_parameters_to_strv:
* @parameters: an #ENamedParameters
*
* Returns: (transfer full): Contents of @parameters as a null-terminated strv
*
* Since: 3.8
*/
gchar **
e_named_parameters_to_strv (const ENamedParameters *parameters)
{
GPtrArray *array = (GPtrArray *) parameters;
GPtrArray *ret = g_ptr_array_new ();
if (array) {
guint i;
for (i = 0; i < array->len; i++) {
g_ptr_array_add (ret, g_strdup (array->pdata[i]));
}
}
g_ptr_array_add (ret, NULL);
return (gchar **) g_ptr_array_free (ret, FALSE);
}
/**
* e_named_parameters_to_string:
* @parameters: an #ENamedParameters
*
* Returns: (transfer full): Contents of @parameters as a string
*
* Since: 3.18
*/
gchar *
e_named_parameters_to_string (const ENamedParameters *parameters)
{
gchar **strv, *str;
gint ii;
strv = e_named_parameters_to_strv (parameters);
if (!strv)
return NULL;
for (ii = 0; strv[ii]; ii++) {
gchar *name_and_value = strv[ii];
strv[ii] = g_strescape (name_and_value, "");
g_free (name_and_value);
}
str = g_strjoinv ("\n", strv);
g_strfreev (strv);
return str;
}
/**
* e_named_parameters_exists:
* @parameters: an #ENamedParameters
* @name: name of the parameter whose existence to check
*
* Returns: Whether @parameters holds a parameter named @name
*
* Since: 3.18
**/
gboolean
e_named_parameters_exists (const ENamedParameters *parameters,
const gchar *name)
{
g_return_val_if_fail (parameters != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
return get_parameter_index (parameters, name) != -1;
}
/**
* e_named_parameters_count:
* @parameters: an #ENamedParameters
*
* Returns: The number of stored named parameters in @parameters
*
* Since: 3.18
**/
guint
e_named_parameters_count (const ENamedParameters *parameters)
{
g_return_val_if_fail (parameters != NULL, 0);
return ((GPtrArray *) parameters)->len;
}
/**
* e_named_parameters_get_name:
* @parameters: an #ENamedParameters
* @index: an index of the parameter whose name to retrieve
*
* Returns: (transfer full): The name of the parameters at index @index,
* or %NULL, of the @index is out of bounds or other error. The returned
* string should be freed with g_free() when done with it.
*
* Since: 3.18
**/
gchar *
e_named_parameters_get_name (const ENamedParameters *parameters,
gint index)
{
const gchar *name_and_value, *colon;
g_return_val_if_fail (parameters != NULL, NULL);
g_return_val_if_fail (index >= 0 && index < e_named_parameters_count (parameters), NULL);
name_and_value = g_ptr_array_index ((GPtrArray *) parameters, index);
colon = name_and_value ? strchr (name_and_value, ':') : NULL;
if (!colon || colon == name_and_value)
return NULL;
return g_strndup (name_and_value, colon - name_and_value);
}
static ENamedParameters *
e_named_parameters_ref (ENamedParameters *params)
{
return (ENamedParameters *) g_ptr_array_ref ((GPtrArray *) params);
}
static void
e_named_parameters_unref (ENamedParameters *params)
{
g_ptr_array_unref ((GPtrArray *) params);
}
G_DEFINE_BOXED_TYPE (
ENamedParameters,
e_named_parameters,
e_named_parameters_ref,
e_named_parameters_unref);
/**
* e_named_timeout_add:
* @interval: the time between calls to the function, in milliseconds
* (1/1000ths of a second)
* @function: function to call
* @data: data to pass to @function
*
* Similar to g_timeout_add(), but also names the #GSource for use in
* debugging and profiling. The name is formed from @function and the
* PACKAGE definintion from a <config.h> file.
*
* Returns: the ID (greater than 0) of the event source
*
* Since: 3.12
**/
/**
* e_named_timeout_add_full:
* @priority: the priority of the timeout source, typically in the
* range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH
* @interval: the time between calls to the function, in milliseconds
* (1/1000ths of a second)
* @function: function to call
* @data: data to pass to @function
* @notify: function to call when the timeout is removed, or %NULL
*
* Similar to g_timeout_add_full(), but also names the #GSource for use
* in debugging and profiling. The name is formed from @function and the
* PACKAGE definition from a <config.h> file.
*
* Returns: the ID (greater than 0) of the event source
*
* Since: 3.12
**/
/**
* e_named_timeout_add_seconds:
* @interval: the time between calls to the function, in seconds
* @function: function to call
* @data: data to pass to @function
*
* Similar to g_timeout_add_seconds(), but also names the #GSource for use
* in debugging and profiling. The name is formed from @function and the
* PACKAGE definition from a <config.h> file.
*
* Returns: the ID (greater than 0) of the event source
*
* Since: 3.12
**/
/**
* e_named_timeout_add_seconds_full:
* @priority: the priority of the timeout source, typically in the
* range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH
* @interval: the time between calls to the function, in seconds
* @function: function to call
* @data: data to pass to @function
* @notify: function to call when the timeout is removed, or %NULL
*
* Similar to g_timeout_add_seconds_full(), but also names the #GSource for
* use in debugging and profiling. The name is formed from @function and the
* PACKAGE definition from a <config.h> file.
*
* Returns: the ID (greater than 0) of the event source
*
* Since: 3.12
**/
/**
* e_timeout_add_with_name:
* @priority: the priority of the timeout source, typically in the
* range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH
* @interval: the time between calls to the function, in milliseconds
* (1/1000ths of a second)
* @name: (allow-none): debug name for the source
* @function: function to call
* @data: data to pass to @function
* @notify: (allow-none): function to call when the timeout is removed,
* or %NULL
*
* Similar to g_timeout_add_full(), but also names the #GSource as @name.
*
* You might find e_named_timeout_add() or e_named_timeout_add_full() more
* convenient. Those macros name the #GSource implicitly.
*
* Returns: the ID (greather than 0) of the event source
*
* Since: 3.12
**/
guint
e_timeout_add_with_name (gint priority,
guint interval,
const gchar *name,
GSourceFunc function,
gpointer data,
GDestroyNotify notify)
{
guint tag;
g_return_val_if_fail (function != NULL, 0);
tag = g_timeout_add_full (
priority, interval, function, data, notify);
g_source_set_name_by_id (tag, name);
return tag;
}
/**
* e_timeout_add_seconds_with_name:
* @priority: the priority of the timeout source, typically in the
* range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH
* @interval: the time between calls to the function, in seconds
* @name: (allow-none): debug name for the source
* @function: function to call
* @data: data to pass to @function
* @notify: (allow-none): function to call when the timeout is removed,
* or %NULL
*
* Similar to g_timeout_add_seconds_full(), but also names the #GSource as
* %name.
*
* You might find e_named_timeout_add_seconds() or
* e_named_timeout_add_seconds_full() more convenient. Those macros name
* the #GSource implicitly.
*
* Returns: the ID (greater than 0) of the event source
*
* Since: 3.12
**/
guint
e_timeout_add_seconds_with_name (gint priority,
guint interval,
const gchar *name,
GSourceFunc function,
gpointer data,
GDestroyNotify notify)
{
guint tag;
g_return_val_if_fail (function != NULL, 0);
tag = g_timeout_add_seconds_full (
priority, interval, function, data, notify);
g_source_set_name_by_id (tag, name);
return tag;
}
/**
* e_source_registry_debug_enabled:
*
* Returns: Whether debugging is enabled, that is,
* whether e_source_registry_debug_print() will produce any output.
*
* Since: 3.16
**/
gboolean
e_source_registry_debug_enabled (void)
{
static gint esr_debug = -1;
if (esr_debug == -1)
esr_debug = g_strcmp0 (g_getenv ("ESR_DEBUG"), "1") == 0 ? 1 : 0;
return esr_debug == 1;
}
/**
* e_source_registry_debug_print:
* @format: a format string to print
* @...: other arguments for the format
*
* Prints the text only if a debugging is enabled with an environment
* variable ESR_DEBUG=1.
*
* Since: 3.16
**/
void
e_source_registry_debug_print (const gchar *format,
...)
{
GString *str;
va_list args;
if (!e_source_registry_debug_enabled ())
return;
str = g_string_new ("");
va_start (args, format);
g_string_vprintf (str, format, args);
va_end (args);
g_print ("%s", str->str);
g_string_free (str, TRUE);
}
/**
* e_type_traverse:
* @parent_type: the root #GType to traverse from
* @func: the function to call for each visited #GType
* @user_data: user data to pass to the function
*
* Calls @func for all instantiable subtypes of @parent_type.
*
* This is often useful for extending functionality by way of #EModule.
* A module may register a subtype of @parent_type in its e_module_load()
* function. Then later on the application will call e_type_traverse()
* to instantiate all registered subtypes of @parent_type.
*
* Since: 3.4
**/
void
e_type_traverse (GType parent_type,
ETypeFunc func,
gpointer user_data)
{
GType *children;
guint n_children, ii;
g_return_if_fail (func != NULL);
children = g_type_children (parent_type, &n_children);
for (ii = 0; ii < n_children; ii++) {
GType type = children[ii];
/* Recurse over the child's children. */
e_type_traverse (type, func, user_data);
/* Skip abstract types. */
if (G_TYPE_IS_ABSTRACT (type))
continue;
func (type, user_data);
}
g_free (children);
}
/**
* e_util_get_source_full_name:
* @registry: an #ESourceRegistry
* @source: an #ESource
*
* Constructs a full name of the @source with all of its parents
* of the form: "<account-name> : <parent>/<source>" where
* the "<parent>/" part can be repeated zero or more times, depending
* on the deep level of the @source.
*
* Returns: (transfer full): Full name of the @source as a newly allocated
* string, which should be freed with g_free() when done with it.
*
* Since 3.18
**/
gchar *
e_util_get_source_full_name (ESourceRegistry *registry,
ESource *source)
{
GString *fullname;
GSList *parts, *link;
g_return_val_if_fail (E_IS_SOURCE (source), NULL);
if (!registry)
return g_strdup (e_source_get_display_name (source));
parts = NULL;
parts = g_slist_prepend (parts, g_strdup (e_source_get_display_name (source)));
g_object_ref (source);
while (source) {
const gchar *parent_id;
ESource *parent;
parent_id = e_source_get_parent (source);
if (!parent_id || !*parent_id)
break;
parent = e_source_registry_ref_source (registry, parent_id);
g_object_unref (source);
source = parent;
if (source) {
const gchar *display_name = e_source_get_display_name (source);
if (!display_name || !*display_name)
break;
parts = g_slist_prepend (parts, g_strdup (display_name));
}
}
g_object_unref (source);
fullname = g_string_new ("");
for (link = parts; link; link = link->next) {
const gchar *part = link->data;
if (fullname->len) {
if (link == parts->next)
g_string_append (fullname, " : ");
else
g_string_append_c (fullname, '/');
}
g_string_append (fullname, part);
}
g_slist_free_full (parts, g_free);
return g_string_free (fullname, FALSE);
}