mirror of https://github.com/djcb/mu.git
* improve checks for database version
This commit is contained in:
parent
87f9dc6cb6
commit
d1bf8b3c73
|
@ -78,7 +78,7 @@ get_output_format (const char *formatstr)
|
|||
|
||||
|
||||
static void
|
||||
update_warning (void)
|
||||
upgrade_warning (void)
|
||||
{
|
||||
g_warning ("the database needs to be updated to version %s\n",
|
||||
MU_XAPIAN_DB_VERSION);
|
||||
|
@ -300,14 +300,14 @@ get_query (MuConfig *opts)
|
|||
static gboolean
|
||||
db_is_ready (const char *xpath)
|
||||
{
|
||||
if (mu_util_db_is_empty (xpath)) {
|
||||
if (mu_util_xapian_is_empty (xpath)) {
|
||||
g_warning ("database is empty; use 'mu index' to "
|
||||
"add messages");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!mu_util_db_version_up_to_date (xpath)) {
|
||||
update_warning ();
|
||||
if (mu_util_xapian_needs_upgrade (xpath)) {
|
||||
upgrade_warning ();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
@ -313,14 +313,14 @@ mu_query_new (const char* xpath, GError **err)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!mu_util_db_version_up_to_date (xpath)) {
|
||||
if (mu_util_xapian_needs_upgrade (xpath)) {
|
||||
g_set_error (err, 0, MU_ERROR_XAPIAN_NOT_UPTODATE,
|
||||
"%s is not up-to-date, needs a full update",
|
||||
xpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mu_util_db_is_empty (xpath))
|
||||
if (mu_util_xapian_is_empty (xpath))
|
||||
g_warning ("database %s is empty; nothing to do", xpath);
|
||||
|
||||
mqx = g_new (MuQuery, 1);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "mu-str.h"
|
||||
#include "mu-msg-flags.h"
|
||||
|
||||
|
||||
/* by default, use transactions of 30000 messages */
|
||||
#define MU_STORE_DEFAULT_TRX_SIZE 30000
|
||||
|
||||
|
@ -93,12 +94,13 @@ check_version (MuStore *store)
|
|||
{
|
||||
/* FIXME clear up versioning semantics */
|
||||
const gchar *version;
|
||||
|
||||
|
||||
version = mu_store_version (store);
|
||||
|
||||
/* no version yet? it must be a new db then; we'll set the version */
|
||||
if (!version) {
|
||||
if (!mu_store_set_version (store, MU_XAPIAN_DB_VERSION)) {
|
||||
if (!mu_store_set_metadata (store, MU_STORE_VERSION_KEY,
|
||||
MU_XAPIAN_DB_VERSION)) {
|
||||
g_warning ("failed to set database version");
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -108,7 +110,8 @@ check_version (MuStore *store)
|
|||
/* we have a version, but is it the right one? */
|
||||
if (std::strcmp (version, MU_XAPIAN_DB_VERSION) != 0) {
|
||||
g_warning ("expected db version %s, but got %s",
|
||||
MU_XAPIAN_DB_VERSION, version);
|
||||
MU_XAPIAN_DB_VERSION,
|
||||
version ? version : "<none>" );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -124,8 +127,10 @@ mu_store_new (const char* xpath, guint batchsize, GError **err)
|
|||
|
||||
try {
|
||||
store = g_new0(MuStore,1);
|
||||
|
||||
store->_db = new Xapian::WritableDatabase
|
||||
(xpath,Xapian::DB_CREATE_OR_OPEN);
|
||||
|
||||
if (!check_version (store)) {
|
||||
mu_store_destroy (store);
|
||||
return NULL;
|
||||
|
@ -191,34 +196,44 @@ const char*
|
|||
mu_store_version (MuStore *store)
|
||||
{
|
||||
g_return_val_if_fail (store, NULL);
|
||||
|
||||
try {
|
||||
std::string v;
|
||||
v = store->_db->get_metadata (MU_XAPIAN_VERSION_KEY);
|
||||
|
||||
g_free (store->_version);
|
||||
return store->_version =
|
||||
v.empty() ? NULL : g_strdup (v.c_str());
|
||||
|
||||
g_free (store->_version);
|
||||
return store->_version =
|
||||
mu_store_get_metadata (store, MU_STORE_VERSION_KEY);
|
||||
}
|
||||
|
||||
gboolean
|
||||
mu_store_set_metadata (MuStore *store, const char *key, const char *val)
|
||||
{
|
||||
g_return_val_if_fail (store, FALSE);
|
||||
g_return_val_if_fail (key, FALSE);
|
||||
g_return_val_if_fail (val, FALSE);
|
||||
|
||||
try {
|
||||
store->_db->set_metadata (key, val);
|
||||
return TRUE;
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
mu_store_get_metadata (MuStore *store, const char *key)
|
||||
{
|
||||
g_return_val_if_fail (store, NULL);
|
||||
g_return_val_if_fail (key, NULL);
|
||||
|
||||
try {
|
||||
const std::string val (store->_db->get_metadata (key));
|
||||
return val.empty() ? NULL : g_strdup (val.c_str());
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
mu_store_set_version (MuStore *store, const char* version)
|
||||
{
|
||||
g_return_val_if_fail (store, FALSE);
|
||||
g_return_val_if_fail (version, FALSE);
|
||||
|
||||
try {
|
||||
store->_db->set_metadata (MU_XAPIAN_VERSION_KEY, version);
|
||||
return TRUE;
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
|
@ -550,35 +565,30 @@ mu_store_contains_message (MuStore *store, const char* path)
|
|||
time_t
|
||||
mu_store_get_timestamp (MuStore *store, const char* msgpath)
|
||||
{
|
||||
char *stampstr;
|
||||
|
||||
g_return_val_if_fail (store, 0);
|
||||
g_return_val_if_fail (msgpath, 0);
|
||||
|
||||
try {
|
||||
const std::string stamp (store->_db->get_metadata (msgpath));
|
||||
if (stamp.empty())
|
||||
return 0;
|
||||
|
||||
return (time_t) g_ascii_strtoull (stamp.c_str(), NULL, 10);
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK_RETURN (0);
|
||||
|
||||
return 0;
|
||||
stampstr = mu_store_get_metadata (store, msgpath);
|
||||
if (!stampstr)
|
||||
return (time_t)0;
|
||||
else
|
||||
return (time_t) g_ascii_strtoull (stampstr, NULL, 10);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gboolean
|
||||
mu_store_set_timestamp (MuStore *store, const char* msgpath,
|
||||
time_t stamp)
|
||||
{
|
||||
g_return_if_fail (store);
|
||||
g_return_if_fail (msgpath);
|
||||
char buf[21];
|
||||
|
||||
g_return_val_if_fail (store, FALSE);
|
||||
g_return_val_if_fail (msgpath, FALSE);
|
||||
|
||||
try {
|
||||
char buf[21];
|
||||
sprintf (buf, "%" G_GUINT64_FORMAT, (guint64)stamp);
|
||||
store->_db->set_metadata (msgpath, buf);
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
sprintf (buf, "%" G_GUINT64_FORMAT, (guint64)stamp);
|
||||
return mu_store_set_metadata (store, msgpath, buf);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ G_BEGIN_DECLS
|
|||
struct _MuStore;
|
||||
typedef struct _MuStore MuStore;
|
||||
|
||||
|
||||
/**
|
||||
* create a new Xapian store, a place to store documents
|
||||
*
|
||||
|
@ -73,17 +74,6 @@ unsigned mu_store_count (MuStore *store);
|
|||
*/
|
||||
const char* mu_store_version (MuStore *store);
|
||||
|
||||
/**
|
||||
* set the version string for the database
|
||||
*
|
||||
* @param store a valid MuStore
|
||||
* @param version the version string (non-NULL)
|
||||
*
|
||||
* @return TRUE if setting the version succeeded, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_store_set_version (MuStore *store,
|
||||
const char* version);
|
||||
|
||||
|
||||
/**
|
||||
* try to flush/commit all outstanding work
|
||||
|
@ -100,7 +90,7 @@ void mu_store_flush (MuStore *store);
|
|||
*
|
||||
* @return TRUE if it succeeded, FALSE otherwise
|
||||
*/
|
||||
MuResult mu_store_store (MuStore *store, MuMsg *msg);
|
||||
MuResult mu_store_store (MuStore *store, MuMsg *msg);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -114,8 +104,8 @@ MuResult mu_store_store (MuStore *store, MuMsg *msg);
|
|||
*
|
||||
* @return TRUE if it succeeded, FALSE otherwise
|
||||
*/
|
||||
MuResult mu_store_remove (MuStore *store,
|
||||
const char* msgpath);
|
||||
MuResult mu_store_remove (MuStore *store,
|
||||
const char* msgpath);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -126,8 +116,8 @@ MuResult mu_store_remove (MuStore *store,
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
gboolean mu_store_contains_message (MuStore *store,
|
||||
const char* path);
|
||||
gboolean mu_store_contains_message (MuStore *store,
|
||||
const char* path);
|
||||
|
||||
/**
|
||||
* store a timestamp for a directory
|
||||
|
@ -135,10 +125,11 @@ gboolean mu_store_contains_message (MuStore *store,
|
|||
* @param store a valid store
|
||||
* @param msgpath path to a maildir
|
||||
* @param stamp a timestamp
|
||||
*
|
||||
* @return TRUE if setting the timestamp succeeded, FALSE otherwise
|
||||
*/
|
||||
void mu_store_set_timestamp (MuStore *store,
|
||||
const char* msgpath,
|
||||
time_t stamp);
|
||||
gboolean mu_store_set_timestamp (MuStore *store, const char* msgpath,
|
||||
time_t stamp);
|
||||
|
||||
/**
|
||||
* get the timestamp for a directory
|
||||
|
@ -148,8 +139,12 @@ void mu_store_set_timestamp (MuStore *store,
|
|||
*
|
||||
* @return the timestamp, or 0 in case of error
|
||||
*/
|
||||
time_t mu_store_get_timestamp (MuStore *store,
|
||||
const char* msgpath);
|
||||
time_t mu_store_get_timestamp (MuStore *store,
|
||||
const char* msgpath);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* call a function for each document in the database
|
||||
|
@ -167,6 +162,30 @@ MuResult mu_store_foreach (MuStore *self,
|
|||
MuStoreForeachFunc func,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* set metadata for this MuStore
|
||||
*
|
||||
* @param store a store
|
||||
* @param key metadata key
|
||||
* @param val metadata value
|
||||
*
|
||||
* @return TRUE if succeeded, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_store_set_metadata (MuStore *store, const char *key, const char *val);
|
||||
|
||||
/**
|
||||
* get metadata for this MuStore
|
||||
*
|
||||
* @param store a store
|
||||
* @param key the metadata key
|
||||
*
|
||||
* @return the value of the metadata (gfree when done with it), or
|
||||
* NULL in case of error
|
||||
*/
|
||||
char* mu_store_get_metadata (MuStore *store, const char *key)
|
||||
G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /*__MU_STORE_H__*/
|
||||
|
|
|
@ -23,16 +23,17 @@
|
|||
|
||||
#include <cstring>
|
||||
#include <errno.h>
|
||||
|
||||
#include <xapian.h>
|
||||
|
||||
#include "mu-util.h"
|
||||
|
||||
|
||||
char*
|
||||
mu_util_db_version (const gchar *xpath)
|
||||
mu_util_xapian_get_metadata (const gchar *xpath, const gchar *key)
|
||||
{
|
||||
g_return_val_if_fail (xpath, NULL);
|
||||
|
||||
g_return_val_if_fail (key, NULL);
|
||||
|
||||
if (!access(xpath, F_OK) == 0) {
|
||||
g_warning ("cannot access %s: %s", xpath, strerror(errno));
|
||||
return NULL;
|
||||
|
@ -40,38 +41,70 @@ mu_util_db_version (const gchar *xpath)
|
|||
|
||||
try {
|
||||
Xapian::Database db (xpath);
|
||||
const std::string version
|
||||
(db.get_metadata (MU_XAPIAN_VERSION_KEY));
|
||||
|
||||
return version.empty() ? NULL : g_strdup (version.c_str());
|
||||
const std::string val(db.get_metadata (key));
|
||||
return val.empty() ? NULL : g_strdup (val.c_str());
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_util_db_version_up_to_date (const gchar *xpath)
|
||||
mu_util_xapian_set_metadata (const gchar *xpath,
|
||||
const gchar *key, const gchar *val)
|
||||
{
|
||||
gchar *version;
|
||||
gboolean uptodate;
|
||||
|
||||
g_return_val_if_fail (xpath, FALSE);
|
||||
|
||||
version = mu_util_db_version (xpath);
|
||||
if (!version)
|
||||
return FALSE;
|
||||
g_return_val_if_fail (xpath, NULL);
|
||||
g_return_val_if_fail (key, NULL);
|
||||
g_return_val_if_fail (val, NULL);
|
||||
|
||||
uptodate = (std::strcmp (version, MU_XAPIAN_DB_VERSION) == 0);
|
||||
if (!access(xpath, F_OK) == 0) {
|
||||
g_warning ("cannot access %s: %s", xpath, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
try {
|
||||
Xapian::WritableDatabase db (xpath, Xapian::DB_OPEN);
|
||||
db.set_metadata (key, val);
|
||||
return TRUE;
|
||||
|
||||
} MU_XAPIAN_CATCH_BLOCK;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
mu_util_xapian_dbversion (const gchar *xpath)
|
||||
{
|
||||
g_return_val_if_fail (xpath, NULL);
|
||||
|
||||
return mu_util_xapian_get_metadata (xpath, MU_STORE_VERSION_KEY);
|
||||
}
|
||||
|
||||
gboolean
|
||||
mu_util_xapian_needs_upgrade (const gchar *xpath)
|
||||
{
|
||||
char *version;
|
||||
gboolean rv;
|
||||
|
||||
g_return_val_if_fail (xpath, TRUE);
|
||||
|
||||
version = mu_util_xapian_dbversion (xpath);
|
||||
|
||||
if (g_strcmp0 (version, MU_XAPIAN_DB_VERSION) == 0)
|
||||
rv = FALSE;
|
||||
else
|
||||
rv = TRUE;
|
||||
|
||||
g_free (version);
|
||||
|
||||
return uptodate;
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_util_db_is_empty (const gchar* xpath)
|
||||
mu_util_xapian_is_empty (const gchar* xpath)
|
||||
{
|
||||
g_return_val_if_fail (xpath, TRUE);
|
||||
|
||||
|
@ -89,7 +122,7 @@ mu_util_db_is_empty (const gchar* xpath)
|
|||
}
|
||||
|
||||
gboolean
|
||||
mu_util_clear_database (const gchar *xpath)
|
||||
mu_util_xapian_clear (const gchar *xpath)
|
||||
{
|
||||
g_return_val_if_fail (xpath, FALSE);
|
||||
|
||||
|
|
|
@ -120,7 +120,6 @@ int mu_util_create_writeable_fd (const char* path, mode_t mode,
|
|||
G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* check if file is local, ie. on the local file system. this means
|
||||
* that it's either having a file URI, *or* that it's an existing file
|
||||
|
@ -146,7 +145,7 @@ gboolean mu_util_play (const char *path,
|
|||
|
||||
|
||||
/**
|
||||
* get the version of the xapian database (ie., the version of the
|
||||
" * get the version of the xapian database (ie., the version of the
|
||||
* 'schema' we are using). If this version != MU_XAPIAN_DB_VERSION,
|
||||
* it's means we need to a full reindex.
|
||||
*
|
||||
|
@ -155,7 +154,11 @@ gboolean mu_util_play (const char *path,
|
|||
* @return the version of the database as a newly allocated string
|
||||
* (free with g_free); if there is no version yet, it will return NULL
|
||||
*/
|
||||
gchar* mu_util_db_version (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT;
|
||||
gchar* mu_util_xapian_dbversion (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
|
||||
gboolean mu_util_xapian_needs_upgrade (const gchar *xpath);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -166,16 +169,8 @@ gchar* mu_util_db_version (const gchar *xpath) G_GNUC_WARN_UNUSED_RESULT;
|
|||
*
|
||||
* @return TRUE if the database is empty, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_util_db_is_empty (const gchar *xpath);
|
||||
gboolean mu_util_xapian_is_empty (const gchar *xpath);
|
||||
|
||||
/**
|
||||
* check if the 'schema' of the current database is up-to-date
|
||||
*
|
||||
* @param xpath path to the xapian database
|
||||
*
|
||||
* @return TRUE if it's up-to-date, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_util_db_version_up_to_date (const gchar *xpath);
|
||||
|
||||
/**
|
||||
* clear the database, ie., remove all of the contents. This is a
|
||||
|
@ -186,7 +181,7 @@ gboolean mu_util_db_version_up_to_date (const gchar *xpath);
|
|||
*
|
||||
* @return TRUE if the clearing succeeded, FALSE otherwise.
|
||||
*/
|
||||
gboolean mu_util_clear_database (const gchar *xpath);
|
||||
gboolean mu_util_xapian_clear (const gchar *xpath);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -252,14 +247,21 @@ unsigned char mu_util_get_dtype_with_lstat (const char *path);
|
|||
g_critical ("%s: caught exception", __FUNCTION__); \
|
||||
}
|
||||
|
||||
#define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \
|
||||
catch (const Xapian::Error &xerr) { \
|
||||
g_set_error ((GE),0,(E), \
|
||||
"%s: xapian error '%s'", \
|
||||
__FUNCTION__, xerr.get_msg().c_str()); \
|
||||
} catch (...) { \
|
||||
g_set_error ((GE),0,(MU_ERROR_INTERNAL), \
|
||||
"%s: caught exception", __FUNCTION__); \
|
||||
#define MU_XAPIAN_CATCH_BLOCK_G_ERROR(GE,E) \
|
||||
catch (const Xapian::DatabaseLockError &xerr) { \
|
||||
g_set_error ((GE),0,MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, \
|
||||
"%s: xapian error '%s'", \
|
||||
__FUNCTION__, xerr.get_msg().c_str()); \
|
||||
} catch (const Xapian::DatabaseCorruptError &xerr ) { \
|
||||
g_set_error ((GE),0,MU_ERROR_XAPIAN_CORRUPTION, \
|
||||
"%s: xapian error '%s'", \
|
||||
__FUNCTION__, xerr.get_msg().c_str()); \
|
||||
} catch (const Xapian::Error &xerr) { \
|
||||
g_set_error ((GE),0,(E), "%s: xapian error '%s'", \
|
||||
__FUNCTION__, xerr.get_msg().c_str()); \
|
||||
} catch (...) { \
|
||||
g_set_error ((GE),0,(MU_ERROR_INTERNAL), \
|
||||
"%s: caught exception", __FUNCTION__); \
|
||||
}
|
||||
|
||||
|
||||
|
@ -286,14 +288,16 @@ unsigned char mu_util_get_dtype_with_lstat (const char *path);
|
|||
return (R); \
|
||||
}
|
||||
|
||||
|
||||
/* the name of the (leaf) dir which has the xapian database */
|
||||
#define MU_XAPIAN_DIR_NAME "xapian"
|
||||
#define MU_XAPIAN_VERSION_KEY "db_version"
|
||||
|
||||
/* name of the bookmark file */
|
||||
#define MU_BOOKMARK_FILENAME "bookmarks"
|
||||
|
||||
/* metdata key for the xapian 'schema' version */
|
||||
#define MU_STORE_VERSION_KEY "db_version"
|
||||
|
||||
|
||||
/**
|
||||
* log something in the log file; note, we use G_LOG_LEVEL_INFO
|
||||
* for such messages
|
||||
|
@ -314,26 +318,24 @@ enum _MuResult {
|
|||
typedef enum _MuResult MuResult;
|
||||
|
||||
enum _MuExitCode {
|
||||
MU_EXITCODE_OK = 0,
|
||||
MU_EXITCODE_ERROR = 1,
|
||||
MU_EXITCODE_NO_MATCHES = 2
|
||||
MU_EXITCODE_OK = 0,
|
||||
MU_EXITCODE_ERROR = 1,
|
||||
MU_EXITCODE_NO_MATCHES = 2,
|
||||
MU_EXITCODE_DB_LOCKED = 3,
|
||||
MU_EXITCODE_DB_CORRUPTED = 4
|
||||
};
|
||||
typedef enum _MuExitCode MuExitCode;
|
||||
|
||||
enum _MuError {
|
||||
/* general xapian related error */
|
||||
MU_ERROR_XAPIAN,
|
||||
/* xapian dir is not accessible */
|
||||
MU_ERROR_XAPIAN_DIR,
|
||||
/* database version is not uptodate (ie. not compatible with
|
||||
* the version that mu expects) */
|
||||
MU_ERROR_XAPIAN_NOT_UPTODATE,
|
||||
/* missing data for a document */
|
||||
MU_ERROR_XAPIAN_MISSING_DATA,
|
||||
/* (parsnng) error in the query */
|
||||
MU_ERROR_QUERY,
|
||||
/* gmime parsing related error */
|
||||
MU_ERROR_GMIME,
|
||||
MU_ERROR_XAPIAN, /* general xapian related error */
|
||||
MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK, /* can't get write lock */
|
||||
MU_ERROR_XAPIAN_CORRUPTION, /* database corruption */
|
||||
MU_ERROR_XAPIAN_DIR, /* xapian dir is not accessible */
|
||||
MU_ERROR_XAPIAN_NOT_UPTODATE, /* database version is not uptodate */
|
||||
MU_ERROR_XAPIAN_MISSING_DATA, /* missing data for a document */
|
||||
MU_ERROR_QUERY, /* (parsing) error in the query */
|
||||
|
||||
MU_ERROR_GMIME, /* gmime parsing related error */
|
||||
|
||||
/* File errors */
|
||||
MU_ERROR_FILE_INVALID_SOURCE,
|
||||
|
|
Loading…
Reference in New Issue