* lib: cleanups: remove mu-msg-cache (WIP)
This commit is contained in:
@ -58,8 +58,6 @@ libmu_la_SOURCES= \
|
||||
mu-log.h \
|
||||
mu-maildir.c \
|
||||
mu-maildir.h \
|
||||
mu-msg-cache.c \
|
||||
mu-msg-cache.h \
|
||||
${crypto} \
|
||||
mu-msg-crypto.h \
|
||||
mu-msg-doc.cc \
|
||||
@ -96,7 +94,6 @@ libmu_la_SOURCES= \
|
||||
mu-util.c \
|
||||
mu-util.h
|
||||
|
||||
|
||||
libmu_la_LIBADD= \
|
||||
$(XAPIAN_LIBS) \
|
||||
$(GMIME_LIBS) \
|
||||
|
||||
@ -1,262 +0,0 @@
|
||||
/*
|
||||
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify it
|
||||
** under the terms of the GNU General Public License as published by the
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program 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 General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#include "mu-flags.h"
|
||||
#include "mu-msg-prio.h"
|
||||
#include "mu-msg-cache.h"
|
||||
#include "mu-str.h"
|
||||
|
||||
struct _MuMsgCache {
|
||||
|
||||
/* all string properties */
|
||||
char *_str[MU_MSG_STRING_FIELD_ID_NUM];
|
||||
|
||||
GSList *_refs, *_tags;
|
||||
|
||||
time_t _timestamp, _date;
|
||||
size_t _size;
|
||||
MuFlags _flags;
|
||||
MuMsgPrio _prio;
|
||||
|
||||
/* <private> */
|
||||
unsigned _cached, _allocated;
|
||||
};
|
||||
|
||||
/* _cached and _allocated have a bit for each MuMsgFieldId to remember
|
||||
* which ones have been cached, and which ones have been allocated. */
|
||||
|
||||
#define is_allocated(C,MFID) ((C)->_allocated & (1 << (MFID)))
|
||||
#define is_cached(C,MFID) ((C)->_cached & (1 << (MFID)))
|
||||
|
||||
#define set_allocated(C,MFID) ((C)->_allocated |= (1 << (MFID)))
|
||||
#define set_cached(C,MFID) ((C)->_cached |= (1 << (MFID)))
|
||||
|
||||
#define reset_allocated(C,MFID) ((C)->_allocated &= ~(1 << (MFID)))
|
||||
#define reset_cached(C,MFID) ((C)->_cached &= ~(1 << (MFID)))
|
||||
|
||||
|
||||
static void
|
||||
cache_clear (MuMsgCache *self)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i != MU_MSG_STRING_FIELD_ID_NUM; ++i)
|
||||
self->_str[i] = NULL;
|
||||
|
||||
self->_timestamp = (time_t)-1;
|
||||
self->_size = (size_t)-1;
|
||||
self->_flags = MU_FLAG_NONE;
|
||||
self->_prio = MU_MSG_PRIO_NONE;
|
||||
self->_date = (time_t)-1;
|
||||
|
||||
self->_refs = self->_tags = NULL;
|
||||
|
||||
self->_cached = 0;
|
||||
self->_allocated = 0;
|
||||
}
|
||||
|
||||
|
||||
MuMsgCache*
|
||||
mu_msg_cache_new (void)
|
||||
{
|
||||
MuMsgCache *self;
|
||||
|
||||
self = g_slice_new0 (MuMsgCache);
|
||||
cache_clear (self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mu_msg_cache_destroy (MuMsgCache *self)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
g_return_if_fail (self);
|
||||
|
||||
for (i = 0; i != MU_MSG_STRING_FIELD_ID_NUM; ++i)
|
||||
if (is_allocated(self, i))
|
||||
g_free (self->_str[i]);
|
||||
|
||||
mu_str_free_list (self->_tags);
|
||||
mu_str_free_list (self->_refs);
|
||||
|
||||
g_slice_free (MuMsgCache, self);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char*
|
||||
mu_msg_cache_set_str (MuMsgCache *self, MuMsgFieldId mfid, char *str,
|
||||
gboolean do_free)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (mu_msg_field_is_string(mfid), NULL);
|
||||
|
||||
/* maybe there was an old, allocated value there already? */
|
||||
if (is_allocated(self, mfid))
|
||||
g_free (self->_str[mfid]);
|
||||
|
||||
self->_str[mfid] = str;
|
||||
set_cached (self, mfid);
|
||||
|
||||
if (do_free)
|
||||
set_allocated (self, mfid);
|
||||
else
|
||||
reset_allocated (self, mfid);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
mu_msg_cache_str (MuMsgCache *cache, MuMsgFieldId mfid)
|
||||
{
|
||||
g_return_val_if_fail (mu_msg_field_is_string(mfid), NULL);
|
||||
return cache->_str[mfid];
|
||||
}
|
||||
|
||||
|
||||
|
||||
const GSList*
|
||||
mu_msg_cache_set_str_list (MuMsgCache *self, MuMsgFieldId mfid,
|
||||
GSList *lst, gboolean do_free)
|
||||
{
|
||||
g_return_val_if_fail(self, NULL);
|
||||
g_return_val_if_fail(mu_msg_field_is_string_list(mfid), NULL);
|
||||
|
||||
switch (mfid) {
|
||||
case MU_MSG_FIELD_ID_REFS:
|
||||
if (is_allocated(self, mfid))
|
||||
mu_str_free_list (self->_refs);
|
||||
self->_refs = lst;
|
||||
break;
|
||||
|
||||
case MU_MSG_FIELD_ID_TAGS:
|
||||
if (is_allocated(self, mfid))
|
||||
mu_str_free_list (self->_tags);
|
||||
self->_tags = lst;
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
set_cached (self, mfid);
|
||||
|
||||
if (do_free)
|
||||
set_allocated (self, mfid);
|
||||
else
|
||||
reset_allocated (self, mfid);
|
||||
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
const GSList*
|
||||
mu_msg_cache_str_list (MuMsgCache *self, MuMsgFieldId mfid)
|
||||
{
|
||||
g_return_val_if_fail (mu_msg_field_is_string_list(mfid), NULL);
|
||||
|
||||
switch (mfid) {
|
||||
case MU_MSG_FIELD_ID_REFS:
|
||||
return self->_refs;
|
||||
default:
|
||||
g_return_val_if_reached(NULL);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gint64
|
||||
mu_msg_cache_set_num (MuMsgCache *self, MuMsgFieldId mfid, gint64 val)
|
||||
{
|
||||
g_return_val_if_fail(self, -1);
|
||||
g_return_val_if_fail(mu_msg_field_is_numeric(mfid), -1);
|
||||
|
||||
switch (mfid) {
|
||||
case MU_MSG_FIELD_ID_DATE:
|
||||
self->_date = (time_t)val;
|
||||
break;
|
||||
case MU_MSG_FIELD_ID_PRIO:
|
||||
self->_prio = (MuMsgPrio)val;
|
||||
break;
|
||||
case MU_MSG_FIELD_ID_FLAGS:
|
||||
self->_flags = (MuFlags)val;
|
||||
break;
|
||||
case MU_MSG_FIELD_ID_SIZE:
|
||||
self->_size = (size_t)val;
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached(-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
set_cached (self, mfid);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
gint64
|
||||
mu_msg_cache_num (MuMsgCache *self, MuMsgFieldId mfid)
|
||||
{
|
||||
g_return_val_if_fail(mu_msg_field_is_numeric(mfid), -1);
|
||||
|
||||
switch (mfid) {
|
||||
case MU_MSG_FIELD_ID_DATE:
|
||||
return (gint64)self->_date;
|
||||
case MU_MSG_FIELD_ID_PRIO:
|
||||
return (gint64)self->_prio;
|
||||
case MU_MSG_FIELD_ID_FLAGS:
|
||||
return (gint64)self->_flags;
|
||||
case MU_MSG_FIELD_ID_SIZE:
|
||||
return (gint64)self->_size;
|
||||
default: g_return_val_if_reached(-1);
|
||||
}
|
||||
|
||||
set_cached (self, mfid);
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_msg_cache_cached (MuMsgCache *self, MuMsgFieldId mfid)
|
||||
{
|
||||
g_return_val_if_fail (mfid < MU_MSG_FIELD_ID_NUM, FALSE);
|
||||
return is_cached(self, mfid) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mu_msg_cache_allocate_all (MuMsgCache *self)
|
||||
{
|
||||
int mfid;
|
||||
|
||||
g_return_if_fail (self);
|
||||
|
||||
for (mfid = 0; mfid != MU_MSG_STRING_FIELD_ID_NUM; ++mfid) {
|
||||
if (self->_str[mfid] && !is_allocated(self, mfid)) {
|
||||
self->_str[mfid] = g_strdup (self->_str[mfid]);
|
||||
set_allocated(self, mfid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
/*
|
||||
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify it
|
||||
** under the terms of the GNU General Public License as published by the
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program 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 General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef __MU_MSG_CACHE_H__
|
||||
#define __MU_MSG_CACHE_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <mu-msg-fields.h>
|
||||
|
||||
/* MuMsgCache caches all values for one specific MuMsg, whether its
|
||||
* backend is a MuMsgFile or MuMsgDb. The idea is to take all the
|
||||
* values from the MuMsg so we can release the backend (file or db).
|
||||
*
|
||||
* It is specifically designed minimize memory allocations; you can
|
||||
* either store dynamically-allocated strings, of which the cache will
|
||||
* take ownership (and g_free in the end), *or* use strings, either
|
||||
* static or owned elsewhere. In the later case, no copy will be made
|
||||
* until mu_msg_cache_allocate_all is called
|
||||
*
|
||||
* Minimizing allocations in esp. necessary when storing
|
||||
* search-results as a list of MuMsg instances
|
||||
*/
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct _MuMsgCache;
|
||||
typedef struct _MuMsgCache MuMsgCache;
|
||||
|
||||
/**
|
||||
* initialize the cache
|
||||
*
|
||||
* @param self ptr to a cache struct
|
||||
*/
|
||||
MuMsgCache *mu_msg_cache_new (void);
|
||||
|
||||
/**
|
||||
* free the strings in the cache
|
||||
*
|
||||
* @param self ptr to a cache struct
|
||||
*/
|
||||
void mu_msg_cache_destroy (MuMsgCache *self);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* add a string value to the cache; it will *not* be freed
|
||||
*
|
||||
* @param self a cache struc
|
||||
* @param mfid the MessageFieldId
|
||||
* @param str the string value to set
|
||||
* @param do_free whether the cache should free this value (i.e, take ownership)
|
||||
*
|
||||
* @return the cached value (this is for nesting function calls)
|
||||
*/
|
||||
const char* mu_msg_cache_set_str (MuMsgCache *self, MuMsgFieldId mfid,
|
||||
char *str, gboolean do_free);
|
||||
|
||||
|
||||
/**
|
||||
* get a string value from the cache
|
||||
*
|
||||
* @param self ptr to a MuMsgCache
|
||||
* @param mfid the MessageFieldId for a string value
|
||||
*
|
||||
* @return the string, or NULL. Don't modify.
|
||||
*/
|
||||
const char* mu_msg_cache_str (MuMsgCache *cache, MuMsgFieldId mfid);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* cache a string-list value
|
||||
*
|
||||
* @param self a mumsgcache
|
||||
* @param mfid the MessageFieldId for a string-list value
|
||||
* @param lst the list
|
||||
* @param do_free whether the cache should free this value (i.e, take ownership)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
const GSList* mu_msg_cache_set_str_list (MuMsgCache *self, MuMsgFieldId mfid,
|
||||
GSList *lst, gboolean do_free);
|
||||
|
||||
/**
|
||||
* get a string-list value from the cache
|
||||
*
|
||||
* @param cache a MuMsgCache
|
||||
* @param mfid the MessageFieldId for string-list value
|
||||
*
|
||||
* @return a list, which should not be modified
|
||||
*/
|
||||
const GSList* mu_msg_cache_str_list (MuMsgCache *cache, MuMsgFieldId mfid);
|
||||
|
||||
|
||||
/**
|
||||
* add a numeric value to the cache
|
||||
*
|
||||
* @param self a MuMsgCache ptr
|
||||
* @param mfid the MessageFieldId for a numeric value
|
||||
* @param val the value
|
||||
*
|
||||
* @return the cached value (this is for nesting function calls)
|
||||
*
|
||||
*/
|
||||
gint64 mu_msg_cache_set_num (MuMsgCache *self, MuMsgFieldId mfid, gint64 val);
|
||||
|
||||
|
||||
/**
|
||||
* get a numeric value from the cache
|
||||
*
|
||||
* @param self a MuMsgCache ptr
|
||||
* @param mfid the MessageFieldId for a numeric value
|
||||
*
|
||||
* @return a numeric value
|
||||
*/
|
||||
gint64 mu_msg_cache_num (MuMsgCache *self, MuMsgFieldId mfid);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* is the value cached already?
|
||||
*
|
||||
* @param self a MuMsgCache ptr
|
||||
* @param mfid the MessageFieldId for a numeric value
|
||||
*
|
||||
* @return TRUE if the value is cached, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_msg_cache_cached (MuMsgCache *self, MuMsgFieldId mfid);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* copy all data that was not already owned by the cache
|
||||
*
|
||||
* @param self a MuMsgCache ptr
|
||||
*/
|
||||
void mu_msg_cache_allocate_all (MuMsgCache *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /*__MU_MSG_CACHE_H__*/
|
||||
|
||||
@ -63,15 +63,12 @@ mu_msg_doc_destroy (MuMsgDoc *self)
|
||||
|
||||
|
||||
gchar*
|
||||
mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid,
|
||||
gboolean *do_free)
|
||||
mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL);
|
||||
g_return_val_if_fail (mu_msg_field_is_string(mfid), NULL);
|
||||
|
||||
*do_free = TRUE;
|
||||
|
||||
try {
|
||||
const std::string s (self->doc().get_value(mfid));
|
||||
return s.empty() ? NULL : g_strdup (s.c_str());
|
||||
@ -81,15 +78,12 @@ mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid,
|
||||
|
||||
|
||||
GSList*
|
||||
mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid,
|
||||
gboolean *do_free)
|
||||
mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), NULL);
|
||||
g_return_val_if_fail (mu_msg_field_is_string_list(mfid), NULL);
|
||||
|
||||
*do_free = TRUE;
|
||||
|
||||
try {
|
||||
/* return a comma-separated string as a GSList */
|
||||
const std::string s (self->doc().get_value(mfid));
|
||||
@ -122,5 +116,3 @@ mu_msg_doc_get_num_field (MuMsgDoc *self, MuMsgFieldId mfid)
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK_RETURN(-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -58,16 +58,11 @@ void mu_msg_doc_destroy (MuMsgDoc *self);
|
||||
*
|
||||
* @param self a MuMsgDoc instance
|
||||
* @param mfid a MuMsgFieldId for a string field
|
||||
* @param do_free receives either TRUE or FALSE, where TRUE means that
|
||||
* the caller owns the string, and has to free it (g_free) when done
|
||||
* with it; FALSE means that the MuMsgDoc owns the string, and it is
|
||||
* only valid as long as the MuMsgDoc is valid (ie., before
|
||||
* mu_msg_doc_destroy).
|
||||
*
|
||||
* @return a string for the given field (see do_free), or NULL in case of error
|
||||
* @return a string for the given field (see do_free), or NULL in case of error.
|
||||
* free with g_free
|
||||
*/
|
||||
gchar* mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid,
|
||||
gboolean *do_free)
|
||||
gchar* mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid)
|
||||
G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
@ -75,16 +70,12 @@ gchar* mu_msg_doc_get_str_field (MuMsgDoc *self, MuMsgFieldId mfid,
|
||||
*
|
||||
* @param self a MuMsgDoc instance
|
||||
* @param mfid a MuMsgFieldId for a string-list field
|
||||
* @param do_free receives either TRUE or FALSE, where TRUE means that
|
||||
* the caller owns the string, and has to free it (mu_str_free_list) when done
|
||||
* with it; FALSE means that the MuMsgDoc owns the list, and it is
|
||||
* only valid as long as the MuMsgDoc is valid (ie., before
|
||||
* mu_msg_doc_destroy).
|
||||
*
|
||||
* @return a list for the given field (see do_free), or NULL in case of error
|
||||
* @return a list for the given field (see do_free), or NULL in case
|
||||
* of error. free with mu_str_free_list
|
||||
*/
|
||||
GSList* mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid,
|
||||
gboolean *do_free) G_GNUC_WARN_UNUSED_RESULT;
|
||||
GSList* mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid)
|
||||
G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@ -74,18 +74,9 @@ mu_msg_file_destroy (MuMsgFile *self)
|
||||
if (self->_mime_msg)
|
||||
g_object_unref (self->_mime_msg);
|
||||
|
||||
mu_str_free_list (self->_free_later);
|
||||
|
||||
g_slice_free (MuMsgFile, self);
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
free_string_later (MuMsgFile *self, gchar *str)
|
||||
{
|
||||
self->_free_later = g_slist_prepend (self->_free_later, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
init_file_metadata (MuMsgFile *self, const char* path, const gchar* mdir,
|
||||
@ -316,46 +307,10 @@ static size_t
|
||||
get_size (MuMsgFile *self)
|
||||
{
|
||||
g_return_val_if_fail (self, 0);
|
||||
|
||||
return self->_size;
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
to_lower (char *s)
|
||||
{
|
||||
char *t = s;
|
||||
while (t&&*t) {
|
||||
t[0] = g_ascii_tolower(t[0]);
|
||||
++t;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
get_prio_header_field (MuMsgFile *self)
|
||||
{
|
||||
const char *str;
|
||||
GMimeObject *obj;
|
||||
|
||||
obj = GMIME_OBJECT(self->_mime_msg);
|
||||
|
||||
str = g_mime_object_get_header (obj, "Precedence");
|
||||
if (!str)
|
||||
str = g_mime_object_get_header (obj, "X-Priority");
|
||||
if (!str)
|
||||
str = g_mime_object_get_header (obj, "Importance");
|
||||
/* NOTE: "X-MSMail-Priority" is never seen without "X-Priority" */
|
||||
/* if (!str) */
|
||||
/* str = g_mime_object_get_header (obj, "X-MSMail-Priority"); */
|
||||
if (str)
|
||||
return (to_lower(g_strdup(str)));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static MuMsgPrio
|
||||
parse_prio_str (const char* priostr)
|
||||
{
|
||||
@ -379,7 +334,7 @@ parse_prio_str (const char* priostr)
|
||||
};
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS(str_prio); ++i)
|
||||
if (g_strstr_len (priostr, -1, str_prio[i]._str) != NULL)
|
||||
if (g_ascii_strcasecmp (priostr, str_prio[i]._str) == 0)
|
||||
return str_prio[i]._prio;
|
||||
|
||||
/* e.g., last-fm uses 'fm-user'... as precedence */
|
||||
@ -389,38 +344,33 @@ parse_prio_str (const char* priostr)
|
||||
static MuMsgPrio
|
||||
get_prio (MuMsgFile *self)
|
||||
{
|
||||
MuMsgPrio prio;
|
||||
char* priostr;
|
||||
GMimeObject *obj;
|
||||
const char* priostr;
|
||||
|
||||
g_return_val_if_fail (self, MU_MSG_PRIO_NONE);
|
||||
|
||||
priostr = get_prio_header_field (self);
|
||||
obj = GMIME_OBJECT(self->_mime_msg);
|
||||
|
||||
priostr = g_mime_object_get_header (obj, "Precedence");
|
||||
if (!priostr)
|
||||
return MU_MSG_PRIO_NORMAL;
|
||||
priostr = g_mime_object_get_header (obj, "X-Priority");
|
||||
if (!priostr)
|
||||
priostr = g_mime_object_get_header (obj, "Importance");
|
||||
|
||||
prio = parse_prio_str (priostr);
|
||||
g_free (priostr);
|
||||
|
||||
return prio;
|
||||
return priostr ? parse_prio_str (priostr) : MU_MSG_PRIO_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
struct _GetBodyData {
|
||||
GMimeObject *_txt_part, *_html_part;
|
||||
gboolean _want_html;
|
||||
};
|
||||
struct _GetBodyData { GMimeObject *_txt_part, *_html_part;};
|
||||
typedef struct _GetBodyData GetBodyData;
|
||||
|
||||
|
||||
|
||||
static void
|
||||
get_body_cb (GMimeObject *parent, GMimeObject *part, GetBodyData *data)
|
||||
{
|
||||
GMimeContentType *ct;
|
||||
|
||||
/* already found what we're looking for? */
|
||||
if ((data->_want_html && data->_html_part != NULL) ||
|
||||
(!data->_want_html && data->_txt_part != NULL))
|
||||
if (data->_html_part && data->_txt_part)
|
||||
return;
|
||||
|
||||
ct = g_mime_object_get_content_type (part);
|
||||
@ -433,12 +383,12 @@ get_body_cb (GMimeObject *parent, GMimeObject *part, GetBodyData *data)
|
||||
return; /* not the body */
|
||||
|
||||
/* is it right content type? */
|
||||
if (g_mime_content_type_is_type (ct, "text", "plain"))
|
||||
if (!data->_txt_part &&
|
||||
g_mime_content_type_is_type (ct, "text", "plain"))
|
||||
data->_txt_part = part;
|
||||
else if (g_mime_content_type_is_type (ct, "text", "html"))
|
||||
else if (!data->_html_part &&
|
||||
g_mime_content_type_is_type (ct, "text", "html"))
|
||||
data->_html_part = part;
|
||||
else
|
||||
return; /* wrong type */
|
||||
}
|
||||
|
||||
|
||||
@ -566,10 +516,7 @@ mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean decrypt,
|
||||
g_return_val_if_fail (GMIME_IS_MESSAGE(msg), NULL);
|
||||
|
||||
memset (&data, 0, sizeof(GetBodyData));
|
||||
data._want_html = want_html;
|
||||
|
||||
mu_mime_message_foreach (msg,
|
||||
decrypt,
|
||||
mu_mime_message_foreach (msg, decrypt,
|
||||
(GMimeObjectForeachFunc)get_body_cb,
|
||||
&data);
|
||||
if (want_html)
|
||||
@ -667,8 +614,6 @@ get_concatenated_text (MuMsgFile *self, gboolean decrypt)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
contains (GSList *lst, const char *str)
|
||||
{
|
||||
@ -683,12 +628,12 @@ static GSList*
|
||||
get_references (MuMsgFile *self)
|
||||
{
|
||||
GSList *msgids;
|
||||
const char *str;
|
||||
unsigned u;
|
||||
const char *headers[] = { "References", "In-reply-to", NULL };
|
||||
|
||||
for (msgids = NULL, u = 0; headers[u]; ++u) {
|
||||
|
||||
char *str;
|
||||
const GMimeReferences *cur;
|
||||
GMimeReferences *mime_refs;
|
||||
|
||||
@ -697,6 +642,8 @@ get_references (MuMsgFile *self)
|
||||
continue;
|
||||
|
||||
mime_refs = g_mime_references_decode (str);
|
||||
g_free (str);
|
||||
|
||||
for (cur = mime_refs; cur; cur = g_mime_references_get_next(cur)) {
|
||||
const char* msgid;
|
||||
msgid = g_mime_references_get_message_id (cur);
|
||||
@ -716,13 +663,17 @@ get_references (MuMsgFile *self)
|
||||
static GSList*
|
||||
get_tags (MuMsgFile *self)
|
||||
{
|
||||
const char *hdr;
|
||||
GSList *lst;
|
||||
char *hdr;
|
||||
|
||||
hdr = mu_msg_file_get_header (self, "X-Label");
|
||||
if (!hdr)
|
||||
return NULL;
|
||||
|
||||
return mu_str_to_list (hdr, ',', TRUE);
|
||||
lst = mu_str_to_list (hdr, ',', TRUE);
|
||||
g_free (hdr);
|
||||
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
@ -761,7 +712,8 @@ recipient_type (MuMsgFieldId mfid)
|
||||
|
||||
|
||||
char*
|
||||
mu_msg_file_get_str_field (MuMsgFile *self, MuMsgFieldId mfid, gboolean *do_free)
|
||||
mu_msg_file_get_str_field (MuMsgFile *self, MuMsgFieldId mfid,
|
||||
gboolean *do_free)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (mu_msg_field_is_string(mfid), NULL);
|
||||
@ -806,22 +758,15 @@ mu_msg_file_get_str_field (MuMsgFile *self, MuMsgFieldId mfid, gboolean *do_free
|
||||
|
||||
|
||||
GSList*
|
||||
mu_msg_file_get_str_list_field (MuMsgFile *self, MuMsgFieldId mfid,
|
||||
gboolean *do_free)
|
||||
mu_msg_file_get_str_list_field (MuMsgFile *self, MuMsgFieldId mfid)
|
||||
{
|
||||
g_return_val_if_fail (self, NULL);
|
||||
g_return_val_if_fail (mu_msg_field_is_string_list(mfid), NULL);
|
||||
|
||||
switch (mfid) {
|
||||
|
||||
case MU_MSG_FIELD_ID_REFS:
|
||||
*do_free = TRUE;
|
||||
return get_references (self);
|
||||
case MU_MSG_FIELD_ID_TAGS:
|
||||
*do_free = TRUE;
|
||||
return get_tags (self);
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
case MU_MSG_FIELD_ID_REFS: return get_references (self);
|
||||
case MU_MSG_FIELD_ID_TAGS: return get_tags (self);
|
||||
default: g_return_val_if_reached (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -854,7 +799,7 @@ mu_msg_file_get_num_field (MuMsgFile *self, const MuMsgFieldId mfid)
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
char*
|
||||
mu_msg_file_get_header (MuMsgFile *self, const char *header)
|
||||
{
|
||||
const gchar *hdr;
|
||||
@ -868,7 +813,7 @@ mu_msg_file_get_header (MuMsgFile *self, const char *header)
|
||||
hdr = g_mime_object_get_header (GMIME_OBJECT(self->_mime_msg),
|
||||
header);
|
||||
|
||||
return hdr ? free_string_later (self, mu_str_utf8ify(hdr)) : NULL;
|
||||
return hdr ? mu_str_utf8ify(hdr) : NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -53,11 +53,9 @@ void mu_msg_file_destroy (MuMsgFile *self);
|
||||
* @param self a MuMsgFile instance
|
||||
* @param header a header (e.g. 'X-Mailer' or 'List-Id')
|
||||
*
|
||||
* @return the value of the header or NULL if not found. Note, only
|
||||
* valid as long as this MuMsgFile is valid -- before
|
||||
* mu_msg_file_destroy
|
||||
* @return the value of the header or NULL if not found; free with g_free
|
||||
*/
|
||||
const char* mu_msg_file_get_header (MuMsgFile *self, const char *header);
|
||||
char* mu_msg_file_get_header (MuMsgFile *self, const char *header);
|
||||
|
||||
|
||||
/**
|
||||
@ -84,17 +82,10 @@ char* mu_msg_file_get_str_field (MuMsgFile *self,
|
||||
*
|
||||
* @param self a valid MuMsgFile
|
||||
* @param msfid the message field id to get (must be of type string-list)
|
||||
* @param do_free receives TRUE or FALSE, conveying if this string
|
||||
* should be owned & freed (TRUE) or not by caller. In case 'FALSE',
|
||||
* this function should be treated as if it were returning a const
|
||||
* GSList*, and note that in that case the string is only valid as long
|
||||
* as the MuMsgFile is alive, ie. before mu_msg_file_destroy
|
||||
*
|
||||
* @return a GSList*, or NULL
|
||||
* @return a GSList*, or NULL; free with mu_str_free_list
|
||||
*/
|
||||
GSList* mu_msg_file_get_str_list_field (MuMsgFile *self,
|
||||
MuMsgFieldId msfid,
|
||||
gboolean *do_free)
|
||||
GSList* mu_msg_file_get_str_list_field (MuMsgFile *self, MuMsgFieldId msfid)
|
||||
G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
|
||||
@ -386,25 +386,27 @@ handle_encrypted_part (MuMsg *msg,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
looks_like_body (MuMsg *msg, GMimeObject *parent, MuMsgPart *msgpart)
|
||||
/* check if the current part might be a body part, and, if so, update
|
||||
* the msgpart struct */
|
||||
static void
|
||||
check_if_body_part (MuMsg *msg, GMimeObject *mobj, MuMsgPart *msgpart)
|
||||
{
|
||||
/* if we've already seen a body for this one, don't consider
|
||||
* more */
|
||||
if (msg->_file->_body_seen)
|
||||
return FALSE;
|
||||
/* is it an attachment? if so, this one is not the body */
|
||||
if (msgpart->part_type & MU_MSG_PART_TYPE_ATTACHMENT)
|
||||
return;
|
||||
|
||||
if (parent &&
|
||||
!GMIME_IS_MESSAGE_PART(parent) &&
|
||||
!GMIME_IS_MULTIPART(parent))
|
||||
return FALSE; /* probably not a body */
|
||||
/* is it a text part? */
|
||||
if (g_ascii_strcasecmp (msgpart->type, "text") != 0)
|
||||
return;
|
||||
|
||||
if (g_strcmp0 (msgpart->type, "text") != 0)
|
||||
return FALSE; /* probably not a body */
|
||||
|
||||
return TRUE; /* maybe a body part */
|
||||
/* we consider it a body part if it's an inline text/plain or
|
||||
* text/html */
|
||||
if ((g_ascii_strcasecmp (msgpart->subtype, "plain") == 0) ||
|
||||
(g_ascii_strcasecmp (msgpart->subtype, "html") == 0))
|
||||
msgpart->part_type |= MU_MSG_PART_TYPE_BODY;
|
||||
}
|
||||
|
||||
|
||||
/* call 'func' with information about this MIME-part */
|
||||
static gboolean
|
||||
handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent,
|
||||
@ -423,16 +425,10 @@ handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent,
|
||||
}
|
||||
|
||||
msgpart.size = get_part_size (part);
|
||||
|
||||
msgpart.part_type = MU_MSG_PART_TYPE_LEAF;
|
||||
msgpart.part_type |= get_disposition ((GMimeObject*)part);
|
||||
|
||||
/* a top-level non-attachment text part is probably a body */
|
||||
if (looks_like_body (msg, parent, &msgpart)) {
|
||||
msgpart.part_type |= MU_MSG_PART_TYPE_BODY;
|
||||
/* remember that we (probably) saw the body */
|
||||
msg->_file->_body_seen = TRUE;
|
||||
}
|
||||
check_if_body_part (msg, (GMimeObject*)part, &msgpart);
|
||||
|
||||
msgpart.data = (gpointer)part;
|
||||
msgpart.index = index;
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
#include <mu-msg.h>
|
||||
#include <mu-msg-file.h>
|
||||
#include <mu-msg-doc.h>
|
||||
#include <mu-msg-cache.h>
|
||||
#include "mu-msg-part.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@ -43,15 +42,6 @@ struct _MuMsgFile {
|
||||
size_t _size;
|
||||
char _path [PATH_MAX + 1];
|
||||
char _maildir [PATH_MAX + 1];
|
||||
|
||||
/* when we iterate over the parts, remember whether
|
||||
* the body has been seen already */
|
||||
gboolean _body_seen;
|
||||
|
||||
/* list where we push allocated strings so we can
|
||||
* free them when the struct gets destroyed
|
||||
*/
|
||||
GSList *_free_later;
|
||||
};
|
||||
|
||||
|
||||
@ -65,7 +55,12 @@ struct _MuMsg {
|
||||
MuMsgFile *_file; /* based on GMime, ie. a file on disc */
|
||||
MuMsgDoc *_doc; /* based on Xapian::Document */
|
||||
|
||||
MuMsgCache *_cache;
|
||||
/* lists where we push allocated strings / GSLists of string
|
||||
* so we can free them when the struct gets destroyed (and we
|
||||
* can return them as 'const to callers)
|
||||
*/
|
||||
GSList *_free_later_str;
|
||||
GSList *_free_later_lst;
|
||||
};
|
||||
|
||||
|
||||
|
||||
145
lib/mu-msg.c
145
lib/mu-msg.c
@ -73,12 +73,7 @@ msg_new (void)
|
||||
MuMsg *self;
|
||||
|
||||
self = g_slice_new0 (MuMsg);
|
||||
|
||||
self->_refcount = 1;
|
||||
self->_cache = mu_msg_cache_new ();
|
||||
|
||||
self->_file = NULL;
|
||||
self->_doc = NULL;
|
||||
|
||||
return self;
|
||||
}
|
||||
@ -108,8 +103,6 @@ mu_msg_new_from_file (const char *path, const char *mdir,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
MuMsg*
|
||||
mu_msg_new_from_doc (XapianDocument *doc, GError **err)
|
||||
{
|
||||
@ -143,7 +136,13 @@ mu_msg_destroy (MuMsg *self)
|
||||
mu_msg_file_destroy (self->_file);
|
||||
mu_msg_doc_destroy (self->_doc);
|
||||
|
||||
mu_msg_cache_destroy (self->_cache);
|
||||
|
||||
{ /* cleanup the strings / lists we stored */
|
||||
mu_str_free_list (self->_free_later_str);
|
||||
g_slist_foreach (self->_free_later_lst,
|
||||
(GFunc)mu_str_free_list, NULL);
|
||||
g_slist_free (self->_free_later_lst);
|
||||
}
|
||||
|
||||
g_slice_free (MuMsg, self);
|
||||
}
|
||||
@ -169,46 +168,51 @@ mu_msg_unref (MuMsg *self)
|
||||
mu_msg_destroy (self);
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
free_later_str (MuMsg *self, gchar *str)
|
||||
{
|
||||
if (str)
|
||||
self->_free_later_str =
|
||||
g_slist_prepend (self->_free_later_str, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
static const GSList*
|
||||
free_later_lst (MuMsg *self, GSList *lst)
|
||||
{
|
||||
if (lst)
|
||||
self->_free_later_lst =
|
||||
g_slist_prepend (self->_free_later_lst, lst);
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
/* use this instead of mu_msg_get_path so we don't get into infinite
|
||||
* regress...*/
|
||||
static const char*
|
||||
get_path (MuMsg *self)
|
||||
{
|
||||
const char *path;
|
||||
char *val;
|
||||
gboolean do_free;
|
||||
|
||||
/* try to get the path from the cache */
|
||||
path = mu_msg_cache_str (self->_cache, MU_MSG_FIELD_ID_PATH);
|
||||
if (path)
|
||||
return path;
|
||||
|
||||
/* nothing found yet? try the doc in case we are using that
|
||||
* backend */
|
||||
do_free = TRUE;
|
||||
val = NULL;
|
||||
|
||||
if (self->_doc)
|
||||
val = mu_msg_doc_get_str_field (self->_doc,
|
||||
MU_MSG_FIELD_ID_PATH,
|
||||
&do_free);
|
||||
val = mu_msg_doc_get_str_field
|
||||
(self->_doc, MU_MSG_FIELD_ID_PATH);
|
||||
|
||||
/* not in the cache yet? try to get it from the file backend,
|
||||
* in case we are using that */
|
||||
if (!val && self->_file)
|
||||
val = mu_msg_file_get_str_field (self->_file,
|
||||
MU_MSG_FIELD_ID_PATH,
|
||||
&do_free);
|
||||
val = mu_msg_file_get_str_field
|
||||
(self->_file, MU_MSG_FIELD_ID_PATH, &do_free);
|
||||
|
||||
/* this cannot happen unless there are bugs in mu */
|
||||
if (!val) {
|
||||
/* shouldn't happen */
|
||||
if (!val)
|
||||
g_warning ("%s: cannot find path", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we found something */
|
||||
return mu_msg_cache_set_str (self->_cache,
|
||||
MU_MSG_FIELD_ID_PATH, val,
|
||||
do_free);
|
||||
return free_later_str (self, val);
|
||||
}
|
||||
|
||||
|
||||
@ -245,57 +249,39 @@ mu_msg_unload_msg_file (MuMsg *msg)
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const GSList*
|
||||
get_str_list_field (MuMsg *self, MuMsgFieldId mfid)
|
||||
{
|
||||
gboolean do_free;
|
||||
GSList *val;
|
||||
|
||||
/* first we try the cache */
|
||||
if (mu_msg_cache_cached (self->_cache, mfid))
|
||||
return mu_msg_cache_str_list (self->_cache, mfid);
|
||||
|
||||
/* if it's not in the cache but it is a value retrievable from
|
||||
* the doc backend, use that */
|
||||
val = NULL;
|
||||
|
||||
if (self->_doc && mu_msg_field_xapian_value (mfid))
|
||||
val = mu_msg_doc_get_str_list_field (self->_doc,
|
||||
mfid, &do_free);
|
||||
else {
|
||||
val = mu_msg_doc_get_str_list_field (self->_doc, mfid);
|
||||
if (!val) {
|
||||
/* if we don't have a file object yet, we need to
|
||||
* create it from the file on disk */
|
||||
if (!mu_msg_load_msg_file (self, NULL))
|
||||
return NULL;
|
||||
val = mu_msg_file_get_str_list_field (self->_file, mfid,
|
||||
&do_free);
|
||||
val = mu_msg_file_get_str_list_field (self->_file, mfid);
|
||||
}
|
||||
|
||||
/* if we get a string that needs freeing, we tell the cache to
|
||||
* mark the string as such, so it will be freed when the cache
|
||||
* is freed (or when the value is overwritten) */
|
||||
return mu_msg_cache_set_str_list (self->_cache, mfid, val,
|
||||
do_free);
|
||||
return free_later_lst (self, val);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static const char*
|
||||
get_str_field (MuMsg *self, MuMsgFieldId mfid)
|
||||
{
|
||||
gboolean do_free;
|
||||
char *val;
|
||||
gboolean do_free;
|
||||
|
||||
/* first we try the cache */
|
||||
if (mu_msg_cache_cached (self->_cache, mfid))
|
||||
return mu_msg_cache_str (self->_cache, mfid);
|
||||
|
||||
/* if it's not in the cache but it is a value retrievable from
|
||||
* the doc backend, use that */
|
||||
do_free = FALSE;
|
||||
val = NULL;
|
||||
|
||||
if (self->_doc && mu_msg_field_xapian_value (mfid))
|
||||
val = mu_msg_doc_get_str_field (self->_doc, mfid, &do_free);
|
||||
val = mu_msg_doc_get_str_field (self->_doc, mfid);
|
||||
|
||||
else if (mu_msg_field_gmime (mfid)) {
|
||||
/* if we don't have a file object yet, we need to
|
||||
* create it from the file on disk */
|
||||
@ -304,13 +290,10 @@ get_str_field (MuMsg *self, MuMsgFieldId mfid)
|
||||
val = mu_msg_file_get_str_field (self->_file, mfid, &do_free);
|
||||
} else {
|
||||
g_warning ("%s: cannot retrieve field", __FUNCTION__);
|
||||
return NULL;
|
||||
val = NULL;
|
||||
}
|
||||
|
||||
/* if we get a string that needs freeing, we tell the cache to
|
||||
* mark the string as such, so it will be freed when the cache
|
||||
* is freed (or when the value is overwritten) */
|
||||
return mu_msg_cache_set_str (self->_cache, mfid, val, do_free);
|
||||
return do_free ? free_later_str (self, val) : val;
|
||||
}
|
||||
|
||||
|
||||
@ -319,12 +302,6 @@ get_num_field (MuMsg *self, MuMsgFieldId mfid)
|
||||
{
|
||||
guint64 val;
|
||||
|
||||
/* first try the cache */
|
||||
if (mu_msg_cache_cached (self->_cache, mfid))
|
||||
return mu_msg_cache_num (self->_cache, mfid);
|
||||
|
||||
/* if it's not in the cache but it is a value retrievable from
|
||||
* the doc backend, use that */
|
||||
val = -1;
|
||||
if (self->_doc && mu_msg_field_xapian_value (mfid))
|
||||
val = mu_msg_doc_get_num_field (self->_doc, mfid);
|
||||
@ -336,7 +313,7 @@ get_num_field (MuMsg *self, MuMsgFieldId mfid)
|
||||
val = mu_msg_file_get_num_field (self->_file, mfid);
|
||||
}
|
||||
|
||||
return mu_msg_cache_set_num (self->_cache, mfid, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
@ -833,8 +810,6 @@ get_target_mdir (MuMsg *msg, const char *target_maildir, GError **err)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* move a msg to another maildir, trying to maintain 'integrity',
|
||||
* ie. msg in 'new/' will go to new/, one in cur/ goes to cur/. be
|
||||
@ -861,24 +836,14 @@ mu_msg_move_to_maildir (MuMsg *self, const char *maildir,
|
||||
|
||||
/* update the message path and the flags; they may have
|
||||
* changed */
|
||||
if (newfullpath) {
|
||||
mu_msg_cache_set_str (self->_cache, MU_MSG_FIELD_ID_PATH, newfullpath,
|
||||
TRUE); /* the cache will free the string */
|
||||
mu_msg_cache_set_str (self->_cache, MU_MSG_FIELD_ID_MAILDIR,
|
||||
g_strdup(maildir), TRUE);
|
||||
/* the cache will free the string */
|
||||
if (!newfullpath)
|
||||
return FALSE;
|
||||
|
||||
/* the contentflags haven't changed, so make sure they persist */
|
||||
flags |= mu_msg_get_flags (self) &
|
||||
(MU_FLAG_HAS_ATTACH|MU_FLAG_ENCRYPTED|MU_FLAG_SIGNED);
|
||||
/* update the pseudo-flag as well */
|
||||
if (!(flags & MU_FLAG_NEW) && (flags & MU_FLAG_SEEN))
|
||||
flags &= ~MU_FLAG_UNREAD;
|
||||
else
|
||||
flags |= MU_FLAG_UNREAD;
|
||||
/* clear the old backends */
|
||||
mu_msg_doc_destroy (self->_doc);
|
||||
mu_msg_file_destroy (self->_file);
|
||||
|
||||
mu_msg_cache_set_num (self->_cache, MU_MSG_FIELD_ID_FLAGS, flags);
|
||||
}
|
||||
|
||||
return newfullpath ? TRUE : FALSE;
|
||||
/* and create a new one */
|
||||
self->_file = mu_msg_file_new (newfullpath, targetmdir, err);
|
||||
return self->_file ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user