[evolution-data-server] evo-#1433 - Message List columns for custom headers
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] evo-#1433 - Message List columns for custom headers
- Date: Thu, 5 Aug 2021 08:45:08 +0000 (UTC)
commit 23d04dfba832e0c71554edf1d79b971458051a54
Author: Milan Crha <mcrha redhat com>
Date: Thu Aug 5 10:44:44 2021 +0200
evo-#1433 - Message List columns for custom headers
Apart of adding custom headers into the folder summary, it also
adds a 'preview' property, to be used for message body preview
in the future. That's to have it under a single commit and
a single soname version bump.
Related to https://gitlab.gnome.org/GNOME/evolution/-/issues/1433
CMakeLists.txt | 2 +-
.../org.gnome.evolution-data-server.gschema.xml.in | 5 +
src/camel/camel-db.c | 58 ++-
src/camel/camel-db.h | 8 +
src/camel/camel-folder-summary.c | 24 +-
src/camel/camel-message-info-base.c | 153 ++++++++
src/camel/camel-message-info.c | 427 ++++++++++++++++++++-
src/camel/camel-message-info.h | 42 +-
src/camel/camel-utils.c | 244 ++++++++++++
src/camel/camel-utils.h | 15 +
src/camel/camel-vee-message-info.c | 47 +++
src/camel/camel.c | 8 +
src/camel/providers/local/camel-local-summary.c | 10 +-
13 files changed, 1013 insertions(+), 30 deletions(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4350bdbcd..56a27a5e2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,7 +48,7 @@ set(USER_PROMPTER_DBUS_SERVICE_NAME "org.gnome.evolution.dataserver.UserPrompter
# ******************************
# Library versioning
# ******************************
-set(LIBCAMEL_CURRENT 62)
+set(LIBCAMEL_CURRENT 63)
set(LIBCAMEL_REVISION 0)
set(LIBCAMEL_AGE 0)
diff --git a/data/org.gnome.evolution-data-server.gschema.xml.in
b/data/org.gnome.evolution-data-server.gschema.xml.in
index 98defd45a..dd0e5f4af 100644
--- a/data/org.gnome.evolution-data-server.gschema.xml.in
+++ b/data/org.gnome.evolution-data-server.gschema.xml.in
@@ -19,6 +19,11 @@
<_summary>Override SMTP HELO/EHLO argument</_summary>
<_description>When not empty, it's used as the SMTP HELO/EHLO argument, instead of the local host
name/IP.</_description>
</key>
+ <key name="camel-message-info-user-headers" type="as">
+ <default>[]</default>
+ <_summary>Array of user header names</_summary>
+ <_description>These headers can be stored in the folder summary, eventually being visible in the GUI.
The value can contain a pipe character ('|'), which delimits the display name from the header name. Example:
'Span Score|X-Spam-Score'</_description>
+ </key>
<key name="network-monitor-gio-name" type="s">
<default>''</default>
<_summary>GIO name of the GNetworkMonitor to use for an ENetworkMonitor instance</_summary>
diff --git a/src/camel/camel-db.c b/src/camel/camel-db.c
index db47c7868..8656359c7 100644
--- a/src/camel/camel-db.c
+++ b/src/camel/camel-db.c
@@ -38,6 +38,8 @@
#include "camel-db.h"
+#define MESSAGE_INFO_TABLE_VERSION 3
+
/* how long to wait before invoking sync on the file */
#define SYNC_TIMEOUT_SECONDS 5
@@ -1682,12 +1684,17 @@ camel_db_create_message_info_table (CamelDB *cdb,
"usertags TEXT , "
"cinfo TEXT , "
"bdata TEXT, "
+ "userheaders TEXT, "
+ "preview TEXT, "
"created TEXT, "
"modified TEXT)",
folder_name);
ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
sqlite3_free (table_creation_query);
+ if (ret != 0)
+ return ret;
+
/* FIXME: sqlize folder_name before you create the index */
safe_index = g_strdup_printf ("SINDEX-%s", folder_name);
table_creation_query = sqlite3_mprintf ("DROP INDEX IF EXISTS %Q", safe_index);
@@ -1695,6 +1702,9 @@ camel_db_create_message_info_table (CamelDB *cdb,
g_free (safe_index);
sqlite3_free (table_creation_query);
+ if (ret != 0)
+ return ret;
+
/* Index on deleted*/
safe_index = g_strdup_printf ("DELINDEX-%s", folder_name);
table_creation_query = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (deleted)", safe_index,
folder_name);
@@ -1702,6 +1712,9 @@ camel_db_create_message_info_table (CamelDB *cdb,
g_free (safe_index);
sqlite3_free (table_creation_query);
+ if (ret != 0)
+ return ret;
+
/* Index on Junk*/
safe_index = g_strdup_printf ("JUNKINDEX-%s", folder_name);
table_creation_query = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (junk)", safe_index,
folder_name);
@@ -1709,6 +1722,9 @@ camel_db_create_message_info_table (CamelDB *cdb,
g_free (safe_index);
sqlite3_free (table_creation_query);
+ if (ret != 0)
+ return ret;
+
/* Index on unread*/
safe_index = g_strdup_printf ("READINDEX-%s", folder_name);
table_creation_query = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (read)", safe_index,
folder_name);
@@ -1733,13 +1749,17 @@ camel_db_migrate_folder_prepare (CamelDB *cdb,
if (version < 0) {
ret = camel_db_create_message_info_table (cdb, folder_name, error);
g_clear_error (error);
- } else if (version < 1) {
+ } else if (version < 3) {
/* Between version 0-1 the following things are changed
* ADDED: created: time
* ADDED: modified: time
* RENAMED: msg_security to dirty
- * */
+ *
+ * Between version 2-3 the following things are changed
+ * ADDED: userheaders: text
+ * ADDED: preview: text
+ */
table_creation_query = sqlite3_mprintf ("DROP TABLE IF EXISTS 'mem.%q'", folder_name);
ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
@@ -1773,6 +1793,8 @@ camel_db_migrate_folder_prepare (CamelDB *cdb,
"usertags TEXT , "
"cinfo TEXT , "
"bdata TEXT, "
+ "userheaders TEXT, "
+ "preview TEXT, "
"created TEXT, "
"modified TEXT )",
folder_name);
@@ -1787,7 +1809,7 @@ camel_db_migrate_folder_prepare (CamelDB *cdb,
"size , dsent , dreceived , subject , mail_from , "
"mail_to , mail_cc , mlist , followup_flag , "
"followup_completed_on , followup_due_by , "
- "part , labels , usertags , cinfo , bdata , "
+ "part , labels , usertags , cinfo , bdata , '', '', "
"strftime(\"%%s\", 'now'), "
"strftime(\"%%s\", 'now') FROM %Q",
folder_name, folder_name);
@@ -1820,7 +1842,7 @@ camel_db_migrate_folder_recreate (CamelDB *cdb,
/* Migration stage two: writing back the old data */
- if (version < 2) {
+ if (version < 3) {
GError *local_error = NULL;
table_creation_query = sqlite3_mprintf (
@@ -1830,7 +1852,7 @@ camel_db_migrate_folder_recreate (CamelDB *cdb,
"subject , mail_from , mail_to , mail_cc , mlist , "
"followup_flag , followup_completed_on , "
"followup_due_by , part , labels , usertags , "
- "cinfo , bdata, created, modified FROM 'mem.%q'",
+ "cinfo , bdata, userheaders, preview, created, modified FROM 'mem.%q'",
folder_name, folder_name);
ret = camel_db_add_to_transaction (cdb, table_creation_query, &local_error);
sqlite3_free (table_creation_query);
@@ -1910,9 +1932,9 @@ camel_db_write_folder_version (CamelDB *cdb,
version_creation_query = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS '%q_version' ( version TEXT )",
folder_name);
if (old_version == -1)
- version_insert_query = sqlite3_mprintf ("INSERT INTO '%q_version' VALUES ('2')", folder_name);
+ version_insert_query = sqlite3_mprintf ("INSERT INTO '%q_version' VALUES ('" G_STRINGIFY
(MESSAGE_INFO_TABLE_VERSION) "')", folder_name);
else
- version_insert_query = sqlite3_mprintf ("UPDATE '%q_version' SET version='2'", folder_name);
+ version_insert_query = sqlite3_mprintf ("UPDATE '%q_version' SET version='" G_STRINGIFY
(MESSAGE_INFO_TABLE_VERSION) "'", folder_name);
ret = camel_db_add_to_transaction (cdb, version_creation_query, error);
ret = camel_db_add_to_transaction (cdb, version_insert_query, error);
@@ -1989,6 +2011,10 @@ camel_db_prepare_message_info_table (CamelDB *cdb,
current_version = -1;
}
+ /* Avoid the migration when not needed */
+ if (current_version == MESSAGE_INFO_TABLE_VERSION)
+ goto exit;
+
camel_db_begin_transaction (cdb, &err);
in_transaction = TRUE;
@@ -2053,7 +2079,7 @@ camel_db_write_message_info_record (CamelDB *cdb,
"INSERT OR REPLACE INTO %Q VALUES ("
"%Q, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, "
"%lld, %lld, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, "
- "%Q, %Q, %Q, %Q, %Q, "
+ "%Q, %Q, %Q, %Q, %Q, %Q, %Q, "
"strftime(\"%%s\", 'now'), "
"strftime(\"%%s\", 'now') )",
folder_name,
@@ -2082,7 +2108,9 @@ camel_db_write_message_info_record (CamelDB *cdb,
record->labels,
record->usertags,
record->cinfo,
- record->bdata);
+ record->bdata,
+ record->userheaders,
+ record->preview);
ret = camel_db_add_to_transaction (cdb, ins_query, error);
@@ -2274,7 +2302,7 @@ camel_db_read_message_info_record_with_uid (CamelDB *cdb,
query = sqlite3_mprintf (
"SELECT uid, flags, size, dsent, dreceived, subject, "
"mail_from, mail_to, mail_cc, mlist, part, labels, "
- "usertags, cinfo, bdata FROM %Q WHERE uid = %Q",
+ "usertags, cinfo, bdata, userheaders, preview FROM %Q WHERE uid = %Q",
folder_name, uid);
ret = camel_db_select (cdb, query, callback, user_data, error);
sqlite3_free (query);
@@ -2309,7 +2337,7 @@ camel_db_read_message_info_records (CamelDB *cdb,
query = sqlite3_mprintf (
"SELECT uid, flags, size, dsent, dreceived, subject, "
"mail_from, mail_to, mail_cc, mlist, part, labels, "
- "usertags, cinfo, bdata FROM %Q ", folder_name);
+ "usertags, cinfo, bdata, userheaders, preview FROM %Q ", folder_name);
ret = camel_db_select (cdb, query, callback, user_data, error);
sqlite3_free (query);
@@ -2577,6 +2605,8 @@ camel_db_camel_mir_free (CamelMIRecord *record)
g_free (record->usertags);
g_free (record->cinfo);
g_free (record->bdata);
+ g_free (record->userheaders);
+ g_free (record->preview);
g_free (record);
}
@@ -2720,7 +2750,9 @@ camel_db_start_in_memory_transactions (CamelDB *cdb,
"labels TEXT , "
"usertags TEXT , "
"cinfo TEXT , "
- "bdata TEXT )",
+ "bdata TEXT, "
+ "userheaders TEXT, "
+ "preview TEXT)",
CAMEL_DB_IN_MEMORY_TABLE);
ret = camel_db_command (cdb, cmd, error);
if (ret != 0 )
@@ -2792,6 +2824,7 @@ static struct _known_column_names {
{ "mlist", CAMEL_DB_COLUMN_MLIST },
{ "nextuid", CAMEL_DB_COLUMN_NEXTUID },
{ "part", CAMEL_DB_COLUMN_PART },
+ { "preview", CAMEL_DB_COLUMN_PREVIEW },
{ "read", CAMEL_DB_COLUMN_READ },
{ "replied", CAMEL_DB_COLUMN_REPLIED },
{ "saved_count", CAMEL_DB_COLUMN_SAVED_COUNT },
@@ -2800,6 +2833,7 @@ static struct _known_column_names {
{ "time", CAMEL_DB_COLUMN_TIME },
{ "uid", CAMEL_DB_COLUMN_UID },
{ "unread_count", CAMEL_DB_COLUMN_UNREAD_COUNT },
+ { "userheaders", CAMEL_DB_COLUMN_USERHEADERS },
{ "usertags", CAMEL_DB_COLUMN_USERTAGS },
{ "version", CAMEL_DB_COLUMN_VERSION },
{ "visible_count", CAMEL_DB_COLUMN_VISIBLE_COUNT },
diff --git a/src/camel/camel-db.h b/src/camel/camel-db.h
index c40b3ecbe..ec42bf516 100644
--- a/src/camel/camel-db.h
+++ b/src/camel/camel-db.h
@@ -160,6 +160,8 @@ typedef gint (* CamelDBCollate)(gpointer enc, gint length1, gconstpointer data1,
* @usertags: composite string of user tags
* @cinfo: content info string - composite string
* @bdata: provider specific data
+ * @userheaders: value for user-defined message headers; Since: 3.42
+ * @preview: message body preview; Since: 3.42
*
* The extensive DB format, supporting basic searching and sorting.
*
@@ -192,6 +194,8 @@ typedef struct _CamelMIRecord {
gchar *usertags;
gchar *cinfo;
gchar *bdata;
+ gchar *userheaders;
+ gchar *preview;
} CamelMIRecord;
/**
@@ -254,6 +258,7 @@ typedef struct _CamelFIRecord {
* @CAMEL_DB_COLUMN_MLIST: mlist
* @CAMEL_DB_COLUMN_NEXTUID: nextuid
* @CAMEL_DB_COLUMN_PART: part
+ * @CAMEL_DB_COLUMN_PREVIEW: preview
* @CAMEL_DB_COLUMN_READ: read
* @CAMEL_DB_COLUMN_REPLIED: replied
* @CAMEL_DB_COLUMN_SAVED_COUNT: saved_count
@@ -262,6 +267,7 @@ typedef struct _CamelFIRecord {
* @CAMEL_DB_COLUMN_TIME: time
* @CAMEL_DB_COLUMN_UID: uid
* @CAMEL_DB_COLUMN_UNREAD_COUNT: unread_count
+ * @CAMEL_DB_COLUMN_USERHEADERS: userheaders
* @CAMEL_DB_COLUMN_USERTAGS: usertags
* @CAMEL_DB_COLUMN_VERSION: version
* @CAMEL_DB_COLUMN_VISIBLE_COUNT: visible_count
@@ -296,6 +302,7 @@ typedef enum {
CAMEL_DB_COLUMN_MLIST,
CAMEL_DB_COLUMN_NEXTUID,
CAMEL_DB_COLUMN_PART,
+ CAMEL_DB_COLUMN_PREVIEW,
CAMEL_DB_COLUMN_READ,
CAMEL_DB_COLUMN_REPLIED,
CAMEL_DB_COLUMN_SAVED_COUNT,
@@ -304,6 +311,7 @@ typedef enum {
CAMEL_DB_COLUMN_TIME,
CAMEL_DB_COLUMN_UID,
CAMEL_DB_COLUMN_UNREAD_COUNT,
+ CAMEL_DB_COLUMN_USERHEADERS,
CAMEL_DB_COLUMN_USERTAGS,
CAMEL_DB_COLUMN_VERSION,
CAMEL_DB_COLUMN_VISIBLE_COUNT,
diff --git a/src/camel/camel-folder-summary.c b/src/camel/camel-folder-summary.c
index af80a658e..8cb6dba9f 100644
--- a/src/camel/camel-folder-summary.c
+++ b/src/camel/camel-folder-summary.c
@@ -52,6 +52,7 @@
#include "camel-stream-null.h"
#include "camel-string-utils.h"
#include "camel-store.h"
+#include "camel-utils.h"
#include "camel-vee-folder.h"
#include "camel-vtrash-folder.h"
#include "camel-mime-part-utils.h"
@@ -1814,7 +1815,6 @@ camel_folder_summary_load (CamelFolderSummary *summary,
CamelStore *parent_store;
const gchar *full_name;
gint ret = 0;
- GError *local_error = NULL;
g_return_val_if_fail (CAMEL_IS_FOLDER_SUMMARY (summary), FALSE);
@@ -1844,18 +1844,10 @@ camel_folder_summary_load (CamelFolderSummary *summary,
cdb = camel_store_get_db (parent_store);
- ret = camel_db_get_folder_uids (
- cdb, full_name, klass->sort_by, klass->collate,
- summary->priv->uids, &local_error);
+ ret = camel_db_prepare_message_info_table (cdb, full_name, error);
- if (local_error != NULL && local_error->message != NULL &&
- strstr (local_error->message, "no such table") != NULL) {
- g_clear_error (&local_error);
-
- /* create table the first time it is accessed and missing */
- ret = camel_db_prepare_message_info_table (cdb, full_name, error);
- } else if (local_error != NULL)
- g_propagate_error (error, local_error);
+ if (ret == 0)
+ ret = camel_db_get_folder_uids (cdb, full_name, klass->sort_by, klass->collate,
summary->priv->uids, error);
camel_folder_summary_unlock (summary);
@@ -1950,6 +1942,12 @@ mir_from_cols (CamelMIRecord *mir,
case CAMEL_DB_COLUMN_BDATA:
mir->bdata = cols[i];
break;
+ case CAMEL_DB_COLUMN_USERHEADERS:
+ mir->userheaders = cols[i];
+ break;
+ case CAMEL_DB_COLUMN_PREVIEW:
+ mir->preview = cols[i];
+ break;
default:
g_warn_if_reached ();
break;
@@ -2981,6 +2979,8 @@ message_info_new_from_headers (CamelFolderSummary *summary,
g_free (cc);
g_free (mlist);
+ camel_util_fill_message_info_user_headers (mi, headers);
+
if ((date = camel_name_value_array_get_named (headers, CAMEL_COMPARE_CASE_INSENSITIVE, "Date")))
camel_message_info_set_date_sent (mi, camel_header_decode_date (date, NULL));
else
diff --git a/src/camel/camel-message-info-base.c b/src/camel/camel-message-info-base.c
index dac7834ee..432655b84 100644
--- a/src/camel/camel-message-info-base.c
+++ b/src/camel/camel-message-info-base.c
@@ -40,6 +40,8 @@ struct _CamelMessageInfoBasePrivate {
guint64 message_id;
GArray *references; /* guint64, aka CamelSummaryMessageID */
CamelNameValueArray *headers;
+ CamelNameValueArray *user_headers;
+ gchar *preview;
};
G_DEFINE_TYPE_WITH_PRIVATE (CamelMessageInfoBase, camel_message_info_base, CAMEL_TYPE_MESSAGE_INFO)
@@ -779,6 +781,102 @@ message_info_base_take_headers (CamelMessageInfo *mi,
if (changed) {
camel_name_value_array_free (bmi->priv->headers);
bmi->priv->headers = headers;
+
+ /* Automatically fill user headers from the known message headers */
+ if (headers)
+ camel_util_fill_message_info_user_headers (mi, headers);
+ } else {
+ camel_name_value_array_free (headers);
+ }
+
+ camel_message_info_property_unlock (mi);
+
+ return changed;
+}
+
+static const gchar *
+message_info_base_get_user_header (const CamelMessageInfo *mi,
+ const gchar *name)
+{
+ CamelMessageInfoBase *bmi;
+ const gchar *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO_BASE (mi), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ bmi = CAMEL_MESSAGE_INFO_BASE (mi);
+
+ camel_message_info_property_lock (mi);
+ if (bmi->priv->user_headers)
+ result = camel_name_value_array_get_named (bmi->priv->user_headers,
CAMEL_COMPARE_CASE_INSENSITIVE, name);
+ else
+ result = NULL;
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+static gboolean
+message_info_base_set_user_header (CamelMessageInfo *mi,
+ const gchar *name,
+ const gchar *value)
+{
+ CamelMessageInfoBase *bmi;
+ gboolean changed;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO_BASE (mi), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ bmi = CAMEL_MESSAGE_INFO_BASE (mi);
+
+ camel_message_info_property_lock (mi);
+ if (!bmi->priv->user_headers)
+ bmi->priv->user_headers = camel_name_value_array_new ();
+
+ if (value)
+ changed = camel_name_value_array_set_named (bmi->priv->user_headers,
CAMEL_COMPARE_CASE_INSENSITIVE, name, value);
+ else
+ changed = camel_name_value_array_remove_named (bmi->priv->user_headers,
CAMEL_COMPARE_CASE_INSENSITIVE, name, TRUE);
+ camel_message_info_property_unlock (mi);
+
+ return changed;
+}
+
+static const CamelNameValueArray *
+message_info_base_get_user_headers (const CamelMessageInfo *mi)
+{
+ CamelMessageInfoBase *bmi;
+ const CamelNameValueArray *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO_BASE (mi), NULL);
+
+ bmi = CAMEL_MESSAGE_INFO_BASE (mi);
+
+ camel_message_info_property_lock (mi);
+ result = bmi->priv->user_headers;
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+static gboolean
+message_info_base_take_user_headers (CamelMessageInfo *mi,
+ CamelNameValueArray *headers)
+{
+ CamelMessageInfoBase *bmi;
+ gboolean changed;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO_BASE (mi), FALSE);
+
+ bmi = CAMEL_MESSAGE_INFO_BASE (mi);
+
+ camel_message_info_property_lock (mi);
+
+ changed = !camel_name_value_array_equal (bmi->priv->user_headers, headers,
CAMEL_COMPARE_CASE_INSENSITIVE);
+
+ if (changed) {
+ camel_name_value_array_free (bmi->priv->user_headers);
+ bmi->priv->user_headers = headers;
} else {
camel_name_value_array_free (headers);
}
@@ -788,6 +886,51 @@ message_info_base_take_headers (CamelMessageInfo *mi,
return changed;
}
+static const gchar *
+message_info_base_get_preview (const CamelMessageInfo *mi)
+{
+ CamelMessageInfoBase *bmi;
+ const gchar *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO_BASE (mi), NULL);
+
+ bmi = CAMEL_MESSAGE_INFO_BASE (mi);
+
+ camel_message_info_property_lock (mi);
+ result = bmi->priv->preview;
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+static gboolean
+message_info_base_set_preview (CamelMessageInfo *mi,
+ const gchar *preview)
+{
+ CamelMessageInfoBase *bmi;
+ gboolean changed;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO_BASE (mi), FALSE);
+
+ bmi = CAMEL_MESSAGE_INFO_BASE (mi);
+
+ camel_message_info_property_lock (mi);
+
+ if (preview && !*preview)
+ preview = NULL;
+
+ changed = g_strcmp0 (bmi->priv->preview, preview) != 0;
+
+ if (changed) {
+ g_free (bmi->priv->preview);
+ bmi->priv->preview = g_strdup (preview);
+ }
+
+ camel_message_info_property_unlock (mi);
+
+ return changed;
+}
+
static void
message_info_base_dispose (GObject *object)
{
@@ -804,12 +947,16 @@ message_info_base_dispose (GObject *object)
g_clear_pointer (&bmi->priv->to, (GDestroyNotify) camel_pstring_free);
g_clear_pointer (&bmi->priv->cc, (GDestroyNotify) camel_pstring_free);
g_clear_pointer (&bmi->priv->mlist, (GDestroyNotify) camel_pstring_free);
+ g_clear_pointer (&bmi->priv->preview, g_free);
g_clear_pointer (&bmi->priv->references, g_array_unref);
camel_name_value_array_free (bmi->priv->headers);
bmi->priv->headers = NULL;
+ camel_name_value_array_free (bmi->priv->user_headers);
+ bmi->priv->user_headers = NULL;
+
/* Chain up to parent's method. */
G_OBJECT_CLASS (camel_message_info_base_parent_class)->dispose (object);
}
@@ -855,6 +1002,12 @@ camel_message_info_base_class_init (CamelMessageInfoBaseClass *class)
mi_class->take_references = message_info_base_take_references;
mi_class->get_headers = message_info_base_get_headers;
mi_class->take_headers = message_info_base_take_headers;
+ mi_class->get_user_header = message_info_base_get_user_header;
+ mi_class->set_user_header = message_info_base_set_user_header;
+ mi_class->get_user_headers = message_info_base_get_user_headers;
+ mi_class->take_user_headers = message_info_base_take_user_headers;
+ mi_class->get_preview = message_info_base_get_preview;
+ mi_class->set_preview = message_info_base_set_preview;
object_class = G_OBJECT_CLASS (class);
object_class->dispose = message_info_base_dispose;
diff --git a/src/camel/camel-message-info.c b/src/camel/camel-message-info.c
index 70e4bedc9..44586a82e 100644
--- a/src/camel/camel-message-info.c
+++ b/src/camel/camel-message-info.c
@@ -63,7 +63,9 @@ enum {
PROP_DATE_RECEIVED,
PROP_MESSAGE_ID,
PROP_REFERENCES,
- PROP_HEADERS
+ PROP_HEADERS,
+ PROP_USER_HEADERS,
+ PROP_PREVIEW
};
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (CamelMessageInfo, camel_message_info, G_TYPE_OBJECT)
@@ -108,6 +110,7 @@ message_info_clone (const CamelMessageInfo *mi,
camel_message_info_set_to (result, camel_message_info_get_to (mi));
camel_message_info_set_cc (result, camel_message_info_get_cc (mi));
camel_message_info_set_mlist (result, camel_message_info_get_mlist (mi));
+ camel_message_info_set_preview (result, camel_message_info_get_preview (mi));
camel_message_info_set_size (result, camel_message_info_get_size (mi));
camel_message_info_set_date_sent (result, camel_message_info_get_date_sent (mi));
camel_message_info_set_date_received (result, camel_message_info_get_date_received (mi));
@@ -135,6 +138,12 @@ message_info_clone (const CamelMessageInfo *mi,
camel_name_value_array_copy (headers));
}
+ headers = camel_message_info_get_user_headers (mi);
+ if (headers) {
+ camel_message_info_take_user_headers (result,
+ camel_name_value_array_copy (headers));
+ }
+
/* Set flags as the last, to not overwrite 'folder-flagged' flag by
the "changes" when copying fields. */
camel_message_info_set_flags (result, ~0, camel_message_info_get_flags (mi));
@@ -173,6 +182,7 @@ message_info_load (CamelMessageInfo *mi,
camel_message_info_set_to (mi, record->to);
camel_message_info_set_cc (mi, record->cc);
camel_message_info_set_mlist (mi, record->mlist);
+ camel_message_info_set_preview (mi, record->preview);
/* Extract Message id & References */
part = record->part;
@@ -220,6 +230,11 @@ message_info_load (CamelMessageInfo *mi,
if (label && *label)
camel_named_flags_insert (user_flags, label);
+ if (camel_named_flags_get_length (user_flags) == 0) {
+ camel_named_flags_free (user_flags);
+ user_flags = NULL;
+ }
+
camel_message_info_take_user_flags (mi, user_flags);
}
@@ -245,9 +260,44 @@ message_info_load (CamelMessageInfo *mi,
g_free (value);
}
+ if (camel_name_value_array_get_length (user_tags) == 0) {
+ camel_name_value_array_free (user_tags);
+ user_tags = NULL;
+ }
+
camel_message_info_take_user_tags (mi, user_tags);
}
+ /* Extract User headers */
+ part = record->userheaders;
+ if (part) {
+ CamelNameValueArray *user_headers;
+
+ count = camel_util_bdata_get_number (&part, 0);
+
+ user_headers = camel_name_value_array_new_sized (count);
+
+ for (ii = 0; ii < count; ii++) {
+ gchar *name, *value;
+
+ name = camel_util_bdata_get_string (&part, NULL);
+ value = camel_util_bdata_get_string (&part, NULL);
+
+ if (name)
+ camel_name_value_array_set_named (user_headers, CAMEL_COMPARE_CASE_SENSITIVE,
name, value ? value : "");
+
+ g_free (name);
+ g_free (value);
+ }
+
+ if (camel_name_value_array_get_length (user_headers) == 0) {
+ camel_name_value_array_free (user_headers);
+ user_headers = NULL;
+ }
+
+ camel_message_info_take_user_headers (mi, user_headers);
+ }
+
return TRUE;
}
@@ -307,6 +357,7 @@ message_info_save (const CamelMessageInfo *mi,
record->to = camel_pstring_strdup (camel_message_info_get_to (mi));
record->cc = camel_pstring_strdup (camel_message_info_get_cc (mi));
record->mlist = camel_pstring_strdup (camel_message_info_get_mlist (mi));
+ record->preview = g_strdup (camel_message_info_get_preview (mi));
record->followup_flag = g_strdup (camel_message_info_get_user_tag (mi, "follow-up"));
record->followup_completed_on = g_strdup (camel_message_info_get_user_tag (mi, "completed-on"));
@@ -373,6 +424,28 @@ message_info_save (const CamelMessageInfo *mi,
}
record->usertags = g_string_free (tmp, FALSE);
+ tmp = g_string_new (NULL);
+ user_tags = camel_message_info_get_user_headers (mi);
+ if (user_tags) {
+ guint32 ii, count;
+
+ count = camel_name_value_array_get_length (user_tags);
+ g_string_append_printf (tmp, "%" G_GUINT32_FORMAT, count);
+
+ for (ii = 0; ii < count; ii++) {
+ const gchar *name = NULL, *value = NULL;
+
+ if (camel_name_value_array_get (user_tags, ii, &name, &value) && name && value) {
+ g_string_append_printf (tmp, " %" G_GUINT32_FORMAT "-%s %" G_GUINT32_FORMAT
"-%s",
+ (guint32) strlen (name), name,
+ (guint32) strlen (value), value);
+ }
+ }
+ } else {
+ g_string_append_c (tmp, '0');
+ }
+ record->userheaders = g_string_free (tmp, FALSE);
+
return TRUE;
}
@@ -460,6 +533,14 @@ message_info_set_property (GObject *object,
case PROP_HEADERS:
camel_message_info_take_headers (mi, g_value_dup_boxed (value));
return;
+
+ case PROP_USER_HEADERS:
+ camel_message_info_take_user_headers (mi, g_value_dup_boxed (value));
+ return;
+
+ case PROP_PREVIEW:
+ camel_message_info_set_preview (mi, g_value_get_string (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -553,6 +634,14 @@ message_info_get_property (GObject *object,
case PROP_HEADERS:
g_value_take_boxed (value, camel_message_info_dup_headers (mi));
return;
+
+ case PROP_USER_HEADERS:
+ g_value_take_boxed (value, camel_message_info_dup_user_headers (mi));
+ return;
+
+ case PROP_PREVIEW:
+ g_value_take_string (value, camel_message_info_dup_preview (mi));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -993,6 +1082,44 @@ camel_message_info_class_init (CamelMessageInfoClass *class)
G_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS));
+
+ /**
+ * CamelMessageInfo:user-headers
+ *
+ * User-defined headers of the associated message. Can be %NULL.
+ *
+ * Since: 3.42
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_USER_HEADERS,
+ g_param_spec_boxed (
+ "user-headers",
+ "User Headers",
+ NULL,
+ CAMEL_TYPE_NAME_VALUE_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * CamelMessageInfo:preview
+ *
+ * Body preview of the associated message. Can be %NULL.
+ *
+ * Since: 3.42
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_PREVIEW,
+ g_param_spec_string (
+ "preview",
+ "Preview",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
}
static void
@@ -3024,6 +3151,304 @@ camel_message_info_take_headers (CamelMessageInfo *mi,
return changed;
}
+/**
+ * camel_message_info_get_user_header:
+ * @mi: a #CamelMessageInfo
+ * @name: header name
+ *
+ * Returns: (transfer none) (nullable): Value of the header named @name from
+ * the user-defined message headers of the associated message, or %NULL,
+ * when not available.
+ *
+ * Since: 3.42
+ **/
+const gchar *
+camel_message_info_get_user_header (const CamelMessageInfo *mi,
+ const gchar *name)
+{
+ CamelMessageInfoClass *klass;
+ const gchar *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ klass = CAMEL_MESSAGE_INFO_GET_CLASS (mi);
+ g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (klass->get_user_header != NULL, NULL);
+
+ camel_message_info_property_lock (mi);
+ result = klass->get_user_header (mi, name);
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+/**
+ * camel_message_info_dup_user_header:
+ * @mi: a #CamelMessageInfo
+ * @name: header name
+ *
+ * Returns: (transfer full) (nullable): Value of the header named @name from
+ * the user-defined message headers of the associated message, or %NULL,
+ * when not available. Free the returned string with g_free(), when no longer
+ * needed.
+ *
+ * Since: 3.42
+ **/
+gchar *
+camel_message_info_dup_user_header (const CamelMessageInfo *mi,
+ const gchar *name)
+{
+ gchar *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), NULL);
+
+ camel_message_info_property_lock (mi);
+ result = g_strdup (camel_message_info_get_user_header (mi, name));
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+/**
+ * camel_message_info_set_user_header:
+ * @mi: a #CamelMessageInfo
+ * @name: header name
+ * @value: (nullable): header value, or %NULL
+ *
+ * Set @value for a single user-defined message header of the associated message.
+ * When the @value is %NULL, the header @name is removed from the user-defined
+ * headers.
+ *
+ * If the @mi changed, the 'dirty' flag is set automatically, unless the @mi is
+ * aborting notifications. There is not emitted folder's "changed" signal for this @mi.
+ *
+ * Returns: Whether the value changed.
+ *
+ * Since: 3.42
+ **/
+gboolean
+camel_message_info_set_user_header (CamelMessageInfo *mi,
+ const gchar *name,
+ const gchar *value)
+{
+ CamelMessageInfoClass *klass;
+ gboolean changed, abort_notifications;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), FALSE);
+
+ klass = CAMEL_MESSAGE_INFO_GET_CLASS (mi);
+ g_return_val_if_fail (klass != NULL, FALSE);
+ g_return_val_if_fail (klass->set_user_header != NULL, FALSE);
+
+ camel_message_info_property_lock (mi);
+ changed = klass->set_user_header (mi, name, value);
+ abort_notifications = mi->priv->abort_notifications;
+ camel_message_info_property_unlock (mi);
+
+ if (changed && !abort_notifications) {
+ g_object_notify (G_OBJECT (mi), "user-headers");
+ camel_message_info_set_dirty (mi, TRUE);
+ }
+
+ return changed;
+}
+
+/**
+ * camel_message_info_get_user_headers:
+ * @mi: a #CamelMessageInfo
+ *
+ * Returns: (transfer none) (nullable): All the user-defined message headers
+ * of the associated message, or %NULL, when none are available.
+ *
+ * Since: 3.42
+ **/
+const CamelNameValueArray *
+camel_message_info_get_user_headers (const CamelMessageInfo *mi)
+{
+ CamelMessageInfoClass *klass;
+ const CamelNameValueArray *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), NULL);
+
+ klass = CAMEL_MESSAGE_INFO_GET_CLASS (mi);
+ g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (klass->get_user_headers != NULL, NULL);
+
+ camel_message_info_property_lock (mi);
+ result = klass->get_user_headers (mi);
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+/**
+ * camel_message_info_dup_user_headers:
+ * @mi: a #CamelMessageInfo
+ *
+ * Returns: (transfer full) (nullable): All the user-defined message headers
+ * of the associated message, or %NULL, when none are available. Free returned
+ * array with camel_name_value_array_free() when no longer needed.
+ *
+ * Since: 3.42
+ **/
+CamelNameValueArray *
+camel_message_info_dup_user_headers (const CamelMessageInfo *mi)
+{
+ const CamelNameValueArray *arr;
+ CamelNameValueArray *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), NULL);
+
+ camel_message_info_property_lock (mi);
+ arr = camel_message_info_get_user_headers (mi);
+ if (arr) {
+ result = camel_name_value_array_copy (arr);
+ } else {
+ result = NULL;
+ }
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+/**
+ * camel_message_info_take_user_headers:
+ * @mi: a #CamelMessageInfo
+ * @headers: (transfer full) (nullable): headers to set, as #CamelNameValueArray, or %NULL
+ *
+ * Takes user-defined message headers of the associated message.
+ *
+ * If the @mi changed, the 'dirty' flag is set automatically, unless the @mi is
+ * aborting notifications. There is not emitted folder's "changed" signal for this @mi.
+ *
+ * Note that it's not safe to use the @headers after the call to this function,
+ * because it can be freed due to no change.
+ *
+ * Returns: Whether the value changed.
+ *
+ * Since: 3.42
+ **/
+gboolean
+camel_message_info_take_user_headers (CamelMessageInfo *mi,
+ CamelNameValueArray *headers)
+{
+ CamelMessageInfoClass *klass;
+ gboolean changed, abort_notifications;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), FALSE);
+
+ klass = CAMEL_MESSAGE_INFO_GET_CLASS (mi);
+ g_return_val_if_fail (klass != NULL, FALSE);
+ g_return_val_if_fail (klass->take_user_headers != NULL, FALSE);
+
+ camel_message_info_property_lock (mi);
+ changed = klass->take_user_headers (mi, headers);
+ abort_notifications = mi->priv->abort_notifications;
+ camel_message_info_property_unlock (mi);
+
+ if (changed && !abort_notifications) {
+ g_object_notify (G_OBJECT (mi), "user-headers");
+ camel_message_info_set_dirty (mi, TRUE);
+ }
+
+ return changed;
+}
+
+/**
+ * camel_message_info_get_preview:
+ * @mi: a #CamelMessageInfo
+ *
+ * Returns: (transfer none) (nullable): Body preview of the associated
+ * message, or %NULL, when not available.
+ *
+ * Since: 3.42
+ **/
+const gchar *
+camel_message_info_get_preview (const CamelMessageInfo *mi)
+{
+ CamelMessageInfoClass *klass;
+ const gchar *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), NULL);
+
+ klass = CAMEL_MESSAGE_INFO_GET_CLASS (mi);
+ g_return_val_if_fail (klass != NULL, NULL);
+ g_return_val_if_fail (klass->get_preview != NULL, NULL);
+
+ camel_message_info_property_lock (mi);
+ result = klass->get_preview (mi);
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+/**
+ * camel_message_info_dup_preview:
+ * @mi: a #CamelMessageInfo
+ * @name: header name
+ *
+ * Returns: (transfer none) (nullable): Body preview of the associated
+ * message, or %NULL, when not available. Free the returned string
+ * with g_free(), when no longer needed.
+ *
+ * Since: 3.42
+ **/
+gchar *
+camel_message_info_dup_preview (const CamelMessageInfo *mi)
+{
+ gchar *result;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), NULL);
+
+ camel_message_info_property_lock (mi);
+ result = g_strdup (camel_message_info_get_preview (mi));
+ camel_message_info_property_unlock (mi);
+
+ return result;
+}
+
+/**
+ * camel_message_info_set_preview:
+ * @mi: a #CamelMessageInfo
+ * @preview: (nullable): message body preview, or %NULL
+ *
+ * Set @preview as the body preview of the associated message. Use %NULL or an empty
+ * string to unset the value.
+ *
+ * If the @mi changed, the 'dirty' flag is set automatically, unless the @mi is
+ * aborting notifications. There is not emitted folder's "changed" signal for this @mi.
+ *
+ * Returns: Whether the value changed.
+ *
+ * Since: 3.42
+ **/
+gboolean
+camel_message_info_set_preview (CamelMessageInfo *mi,
+ const gchar *preview)
+{
+ CamelMessageInfoClass *klass;
+ gboolean changed, abort_notifications;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (mi), FALSE);
+
+ klass = CAMEL_MESSAGE_INFO_GET_CLASS (mi);
+ g_return_val_if_fail (klass != NULL, FALSE);
+ g_return_val_if_fail (klass->set_preview != NULL, FALSE);
+
+ camel_message_info_property_lock (mi);
+ changed = klass->set_preview (mi, preview);
+ abort_notifications = mi->priv->abort_notifications;
+ camel_message_info_property_unlock (mi);
+
+ if (changed && !abort_notifications) {
+ g_object_notify (G_OBJECT (mi), "preview");
+ camel_message_info_set_dirty (mi, TRUE);
+ }
+
+ return changed;
+}
+
/**
* camel_message_info_dump:
* @mi: a #CamelMessageInfo
diff --git a/src/camel/camel-message-info.h b/src/camel/camel-message-info.h
index 44cc2e451..5cee1e9ea 100644
--- a/src/camel/camel-message-info.h
+++ b/src/camel/camel-message-info.h
@@ -26,7 +26,6 @@
#include <camel/camel-named-flags.h>
#include <camel/camel-name-value-array.h>
-#include <camel/camel-utils.h>
/* Standard GObject macros */
#define CAMEL_TYPE_MESSAGE_INFO \
@@ -188,9 +187,25 @@ struct _CamelMessageInfoClass {
(* get_headers) (const CamelMessageInfo *mi);
gboolean (* take_headers)(CamelMessageInfo *mi,
CamelNameValueArray *headers);
+ const gchar * (* get_user_header)
+ (const CamelMessageInfo *mi,
+ const gchar *name);
+ gboolean (* set_user_header)
+ (CamelMessageInfo *mi,
+ const gchar *name,
+ const gchar *value);
+ const CamelNameValueArray *
+ (* get_user_headers)
+ (const CamelMessageInfo *mi);
+ gboolean (* take_user_headers)
+ (CamelMessageInfo *mi,
+ CamelNameValueArray *headers);
+ const gchar * (* get_preview) (const CamelMessageInfo *mi);
+ gboolean (* set_preview) (CamelMessageInfo *mi,
+ const gchar *preview);
/* Padding for future expansion */
- gpointer reserved[20];
+ gpointer reserved[14];
};
GType camel_message_info_get_type (void);
@@ -319,6 +334,29 @@ CamelNameValueArray *
camel_message_info_dup_headers (const CamelMessageInfo *mi);
gboolean camel_message_info_take_headers (CamelMessageInfo *mi,
CamelNameValueArray *headers);
+const gchar * camel_message_info_get_user_header
+ (const CamelMessageInfo *mi,
+ const gchar *name);
+gchar * camel_message_info_dup_user_header
+ (const CamelMessageInfo *mi,
+ const gchar *name);
+gboolean camel_message_info_set_user_header
+ (CamelMessageInfo *mi,
+ const gchar *name,
+ const gchar *value);
+const CamelNameValueArray *
+ camel_message_info_get_user_headers
+ (const CamelMessageInfo *mi);
+CamelNameValueArray *
+ camel_message_info_dup_user_headers
+ (const CamelMessageInfo *mi);
+gboolean camel_message_info_take_user_headers
+ (CamelMessageInfo *mi,
+ CamelNameValueArray *headers);
+const gchar * camel_message_info_get_preview (const CamelMessageInfo *mi);
+gchar * camel_message_info_dup_preview (const CamelMessageInfo *mi);
+gboolean camel_message_info_set_preview (CamelMessageInfo *mi,
+ const gchar *preview);
/* Debugging functions */
void camel_message_info_dump (CamelMessageInfo *mi);
diff --git a/src/camel/camel-utils.c b/src/camel/camel-utils.c
index 98693197f..e61160ca4 100644
--- a/src/camel/camel-utils.c
+++ b/src/camel/camel-utils.c
@@ -19,7 +19,9 @@
#include <stdio.h>
#include <string.h>
+#include <gio/gio.h>
+#include "camel-mime-utils.h"
#include "camel-utils.h"
/**
@@ -284,3 +286,245 @@ camel_utils_weak_ref_free (GWeakRef *weak_ref)
g_weak_ref_clear (weak_ref);
g_slice_free (GWeakRef, weak_ref);
}
+
+G_LOCK_DEFINE_STATIC (mi_user_headers);
+static GSettings *mi_user_headers_settings = NULL;
+static gchar **mi_user_headers = NULL;
+
+static void
+mi_user_headers_settings_changed_cb (GSettings *settings,
+ const gchar *key,
+ gpointer user_data)
+{
+ G_LOCK (mi_user_headers);
+
+ if (mi_user_headers_settings) {
+ gboolean changed;
+ gchar **strv;
+ guint ii, jj = 0;
+
+ strv = g_settings_get_strv (mi_user_headers_settings, "camel-message-info-user-headers");
+ changed = (!mi_user_headers && strv && strv[0]) || (mi_user_headers && (!strv || !strv[0]));
+
+ if (mi_user_headers && strv && !changed) {
+ for (ii = 0, jj = 0; strv[ii] && mi_user_headers[jj] && jj <
CAMEL_UTILS_MAX_USER_HEADERS; ii++) {
+ const gchar *name = NULL;
+
+ camel_util_decode_user_header_setting (strv[ii], NULL, &name);
+
+ if (name && *name) {
+ if (g_ascii_strcasecmp (mi_user_headers[jj], name) != 0) {
+ changed = TRUE;
+ break;
+ }
+ jj++;
+ }
+ }
+
+ changed = changed || (strv[ii] && jj < CAMEL_UTILS_MAX_USER_HEADERS) || (!strv[ii] &&
jj < CAMEL_UTILS_MAX_USER_HEADERS && mi_user_headers[jj]);
+ }
+
+ if (changed) {
+ GPtrArray *array;
+
+ array = g_ptr_array_sized_new (jj + 2);
+
+ for (ii = 0, jj = 0; strv && strv[ii] && jj < CAMEL_UTILS_MAX_USER_HEADERS; ii++) {
+ const gchar *name = NULL;
+
+ camel_util_decode_user_header_setting (strv[ii], NULL, &name);
+
+ if (name && *name) {
+ g_ptr_array_add (array, g_strdup (name));
+ jj++;
+ }
+ }
+
+ /* NULL-terminated */
+ g_ptr_array_add (array, NULL);
+
+ g_strfreev (mi_user_headers);
+ mi_user_headers = (gchar **) g_ptr_array_free (array, FALSE);
+ }
+
+ g_strfreev (strv);
+ }
+
+ G_UNLOCK (mi_user_headers);
+}
+
+/* private functions */
+void _camel_utils_initialize (void);
+void _camel_utils_shutdown (void);
+
+/* <private> */
+void
+_camel_utils_initialize (void)
+{
+ G_LOCK (mi_user_headers);
+ mi_user_headers_settings = g_settings_new ("org.gnome.evolution-data-server");
+ g_signal_connect (mi_user_headers_settings, "changed::camel-message-info-user-headers",
+ G_CALLBACK (mi_user_headers_settings_changed_cb), NULL);
+ G_UNLOCK (mi_user_headers);
+ mi_user_headers_settings_changed_cb (NULL, NULL, NULL);
+}
+
+/* <private> */
+void
+_camel_utils_shutdown (void)
+{
+ G_LOCK (mi_user_headers);
+ if (mi_user_headers_settings) {
+ g_clear_object (&mi_user_headers_settings);
+ g_strfreev (mi_user_headers);
+ mi_user_headers = NULL;
+ }
+ G_UNLOCK (mi_user_headers);
+}
+
+/**
+ * camel_util_fill_message_info_user_headers:
+ * @info: a #CamelMessageInfo
+ * @headers: a #CamelNameValueArray with the headers to read from
+ *
+ * Fill @info 's user-headers with the user-defined headers from
+ * the @headers array.
+ *
+ * Returns: Whether the @info's user headers changed
+ *
+ * Since: 3.42
+ **/
+gboolean
+camel_util_fill_message_info_user_headers (CamelMessageInfo *info,
+ const CamelNameValueArray *headers)
+{
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_MESSAGE_INFO (info), FALSE);
+ g_return_val_if_fail (headers != NULL, FALSE);
+
+ camel_message_info_freeze_notifications (info);
+
+ G_LOCK (mi_user_headers);
+
+ if (mi_user_headers) {
+ CamelNameValueArray *array;
+ guint ii;
+
+ array = camel_name_value_array_new ();
+
+ for (ii = 0; mi_user_headers[ii]; ii++) {
+ const gchar *value;
+ gchar *str;
+
+ value = camel_name_value_array_get_named (headers, CAMEL_COMPARE_CASE_INSENSITIVE,
mi_user_headers[ii]);
+ if (!value)
+ continue;
+
+ while (*value && g_ascii_isspace (*value))
+ value++;
+
+ str = camel_header_unfold (value);
+
+ if (str && *str)
+ camel_name_value_array_set_named (array, CAMEL_COMPARE_CASE_INSENSITIVE,
mi_user_headers[ii], str);
+ else
+ camel_name_value_array_remove_named (array, CAMEL_COMPARE_CASE_INSENSITIVE,
mi_user_headers[ii], TRUE);
+
+ g_free (str);
+ }
+
+ if (camel_name_value_array_get_length (array) == 0) {
+ camel_name_value_array_free (array);
+ array = NULL;
+ }
+
+ changed = camel_message_info_take_user_headers (info, array);
+ }
+
+ G_UNLOCK (mi_user_headers);
+
+ camel_message_info_thaw_notifications (info);
+
+ return changed;
+}
+
+/**
+ * camel_util_encode_user_header_setting:
+ * @display_name: (nullable): display name for the header name, or %NULL
+ * @header_name: the header name
+ *
+ * Encode the optional @display_name and the @header_name to a value suitable
+ * for GSettings schema org.gnome.evolution-data-server and key camel-message-info-user-headers.
+ *
+ * Free the returned string with g_free(), when no longer needed.
+ *
+ * Returns: (transfer full): a newly allocated string with encoded @display_name
+ * and @header_name
+ *
+ * Since: 3.42
+ **/
+gchar *
+camel_util_encode_user_header_setting (const gchar *display_name,
+ const gchar *header_name)
+{
+ g_return_val_if_fail (header_name && *header_name, NULL);
+
+ if (display_name && *display_name)
+ return g_strconcat (display_name, "|", header_name, NULL);
+
+ return g_strdup (header_name);
+}
+
+/**
+ * camel_util_decode_user_header_setting:
+ * @setting_value: the value to decode
+ * @out_display_name: (out) (transfer full) (nullable): location for the decoded display name, or %NULL when
not needed
+ * @out_header_name: (out): the location for the decoded header name
+ *
+ * Decode the values previously encoded by camel_util_encode_user_header_setting().
+ * The @out_header_name points to the @setting_value, thus it's valid as long
+ * as the @setting_value is valid and unchanged.
+ *
+ * The @out_header_name can result in %NULL when the @setting_value
+ * contains invalid data.
+ *
+ * The @out_display_name can result in %NULL when the @setting_value
+ * does not contain the display name. In such case the header name can
+ * be used as the display name.
+ *
+ * Since: 3.42
+ **/
+void
+camel_util_decode_user_header_setting (const gchar *setting_value,
+ gchar **out_display_name,
+ const gchar **out_header_name)
+{
+ const gchar *ptr;
+
+ g_return_if_fail (setting_value != NULL);
+ g_return_if_fail (out_header_name != NULL);
+
+ *out_header_name = NULL;
+
+ if (out_display_name)
+ *out_display_name = NULL;
+
+ if (!*setting_value)
+ return;
+
+ ptr = strchr (setting_value, '|');
+
+ /* Nothing after the pipe means no header name */
+ if (ptr && !ptr[1])
+ return;
+
+ if (ptr) {
+ if (out_display_name && ptr != setting_value)
+ *out_display_name = g_strndup (setting_value, ptr - setting_value);
+
+ *out_header_name = ptr + 1;
+ } else {
+ *out_header_name = setting_value;
+ }
+}
diff --git a/src/camel/camel-utils.h b/src/camel/camel-utils.h
index 8fde25095..f26f597b2 100644
--- a/src/camel/camel-utils.h
+++ b/src/camel/camel-utils.h
@@ -25,6 +25,10 @@
#include <glib-object.h>
#include <time.h>
#include <camel/camel-enums.h>
+#include <camel/camel-message-info.h>
+#include <camel/camel-name-value-array.h>
+
+#define CAMEL_UTILS_MAX_USER_HEADERS 3
G_BEGIN_DECLS
@@ -44,6 +48,17 @@ time_t camel_time_value_apply (time_t src_time,
GWeakRef * camel_utils_weak_ref_new (gpointer object);
void camel_utils_weak_ref_free (GWeakRef *weak_ref);
+gboolean camel_util_fill_message_info_user_headers
+ (CamelMessageInfo *info,
+ const CamelNameValueArray *headers);
+gchar * camel_util_encode_user_header_setting
+ (const gchar *display_name,
+ const gchar *header_name);
+void camel_util_decode_user_header_setting
+ (const gchar *setting_value,
+ gchar **out_display_name,
+ const gchar **out_header_name);
+
G_END_DECLS
#endif /* CAMEL_UTILS_H */
diff --git a/src/camel/camel-vee-message-info.c b/src/camel/camel-vee-message-info.c
index 926a37849..10355de22 100644
--- a/src/camel/camel-vee-message-info.c
+++ b/src/camel/camel-vee-message-info.c
@@ -430,6 +430,47 @@ vee_message_info_take_headers (CamelMessageInfo *mi,
vee_call_from_parent_mi (FALSE, gboolean, camel_message_info_take_headers, (orig_mi, headers), TRUE);
}
+static const gchar *
+vee_message_info_get_user_header (const CamelMessageInfo *mi,
+ const gchar *name)
+{
+ vee_call_from_parent_mi (NULL, const gchar *, camel_message_info_get_user_header, (orig_mi, name),
FALSE);
+}
+
+static gboolean
+vee_message_info_set_user_header (CamelMessageInfo *mi,
+ const gchar *name,
+ const gchar *value)
+{
+ vee_call_from_parent_mi (FALSE, gboolean, camel_message_info_set_user_header, (orig_mi, name, value),
TRUE);
+}
+
+static const CamelNameValueArray *
+vee_message_info_get_user_headers (const CamelMessageInfo *mi)
+{
+ vee_call_from_parent_mi (NULL, const CamelNameValueArray *, camel_message_info_get_user_headers,
(orig_mi), FALSE);
+}
+
+static gboolean
+vee_message_info_take_user_headers (CamelMessageInfo *mi,
+ CamelNameValueArray *headers)
+{
+ vee_call_from_parent_mi (FALSE, gboolean, camel_message_info_take_user_headers, (orig_mi, headers),
TRUE);
+}
+
+static const gchar *
+vee_message_info_get_preview (const CamelMessageInfo *mi)
+{
+ vee_call_from_parent_mi (NULL, const gchar *, camel_message_info_get_preview, (orig_mi), FALSE);
+}
+
+static gboolean
+vee_message_info_set_preview (CamelMessageInfo *mi,
+ const gchar *preview)
+{
+ vee_call_from_parent_mi (FALSE, gboolean, camel_message_info_set_preview, (orig_mi, preview), TRUE);
+}
+
#undef vee_call_from_parent_mi
static void
@@ -485,6 +526,12 @@ camel_vee_message_info_class_init (CamelVeeMessageInfoClass *class)
mi_class->take_references = vee_message_info_take_references;
mi_class->get_headers = vee_message_info_get_headers;
mi_class->take_headers = vee_message_info_take_headers;
+ mi_class->get_user_header = vee_message_info_get_user_header;
+ mi_class->set_user_header = vee_message_info_set_user_header;
+ mi_class->get_user_headers = vee_message_info_get_user_headers;
+ mi_class->take_user_headers = vee_message_info_take_user_headers;
+ mi_class->get_preview = vee_message_info_get_preview;
+ mi_class->set_preview = vee_message_info_set_preview;
object_class = G_OBJECT_CLASS (class);
object_class->dispose = vee_message_info_dispose;
diff --git a/src/camel/camel.c b/src/camel/camel.c
index 1d0421ab3..3fbc17c9a 100644
--- a/src/camel/camel.c
+++ b/src/camel/camel.c
@@ -37,6 +37,10 @@
#include "camel-provider.h"
#include "camel-win32.h"
+/* private functions from camel-utils.c */
+void _camel_utils_initialize (void);
+void _camel_utils_shutdown (void);
+
/* To protect NSS initialization and shutdown. This prevents
* concurrent calls to shutdown () and init () by different threads */
PRLock *nss_initlock = NULL;
@@ -232,6 +236,8 @@ skip_nss_init:
g_object_unref (certdb);
+ _camel_utils_initialize ();
+
initialised = TRUE;
return 0;
@@ -265,6 +271,8 @@ camel_shutdown (void)
PR_Unlock (nss_initlock);
}
+ _camel_utils_shutdown ();
+
initialised = FALSE;
}
diff --git a/src/camel/providers/local/camel-local-summary.c b/src/camel/providers/local/camel-local-summary.c
index 09a897f38..60d0ba1a2 100644
--- a/src/camel/providers/local/camel-local-summary.c
+++ b/src/camel/providers/local/camel-local-summary.c
@@ -477,11 +477,17 @@ local_summary_sync (CamelLocalSummary *cls,
GError **error)
{
CamelFolderSummary *folder_summary;
+ GError *local_error = NULL;
folder_summary = CAMEL_FOLDER_SUMMARY (cls);
- if (!camel_folder_summary_save (folder_summary, error)) {
- g_warning ("Could not save summary for local providers");
+ if (!camel_folder_summary_save (folder_summary, &local_error)) {
+ CamelFolder *folder = camel_folder_summary_get_folder (folder_summary);
+ g_warning ("Could not save summary for local providers folder '%s': %s",
+ folder ? camel_folder_get_full_name (folder) : "???",
+ local_error ? local_error->message : "Unknown error");
+ if (local_error)
+ g_propagate_error (error, local_error);
return -1;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]