* crypto: decryption support (WIP)
This commit is contained in:
@ -21,6 +21,8 @@
|
||||
#include "config.h"
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mu-msg.h"
|
||||
#include "mu-msg-priv.h"
|
||||
#include "mu-msg-part.h"
|
||||
@ -31,74 +33,138 @@
|
||||
#include <gmime/gmime.h>
|
||||
#include <gmime/gmime-multipart-signed.h>
|
||||
|
||||
static gboolean
|
||||
dummy_password_requester (GMimeCryptoContext *ctx, const char *user_id,
|
||||
const char* prompt_ctx, gboolean reprompt,
|
||||
GMimeStream *response, GError **err)
|
||||
{
|
||||
g_print ("password requested\n");
|
||||
return TRUE;
|
||||
#define CALLBACK_DATA "callback-data"
|
||||
|
||||
struct _CallbackData {
|
||||
MuMsgPartPasswordFunc pw_func;
|
||||
gpointer user_data;
|
||||
};
|
||||
typedef struct _CallbackData CallbackData;
|
||||
|
||||
|
||||
static gboolean
|
||||
password_requester (GMimeCryptoContext *ctx, const char *user_id,
|
||||
const char* prompt_ctx, gboolean reprompt,
|
||||
GMimeStream *response, GError **err)
|
||||
{
|
||||
CallbackData *cbdata;
|
||||
gchar *password;
|
||||
ssize_t written;
|
||||
|
||||
cbdata = g_object_get_data (G_OBJECT(ctx), CALLBACK_DATA);
|
||||
g_return_val_if_fail (cbdata, FALSE);
|
||||
|
||||
password = cbdata->pw_func (user_id, prompt_ctx, reprompt,
|
||||
cbdata->user_data);
|
||||
if (!password)
|
||||
return FALSE;
|
||||
|
||||
written = g_mime_stream_write_string (response, password);
|
||||
if (written != -1)
|
||||
written = g_mime_stream_write_string (response, "\n");
|
||||
if (written == -1)
|
||||
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
|
||||
"writing password to mime stream failed");
|
||||
|
||||
if (g_mime_stream_flush (response) != 0)
|
||||
g_printerr ("error flushing stream!\n");
|
||||
|
||||
memset (password, 0, strlen(password));
|
||||
g_free (password);
|
||||
|
||||
return written != -1 ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
|
||||
GMimeCryptoContext*
|
||||
static char*
|
||||
dummy_password_func (const char *user_id, const char *prompt_ctx,
|
||||
gboolean reprompt, gpointer user_data)
|
||||
{
|
||||
g_print ("password requested for %s (%s) %s\n",
|
||||
user_id, prompt_ctx, reprompt ? "again" : "");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static GMimeCryptoContext*
|
||||
get_gpg_crypto_context (MuMsgOptions opts, GError **err)
|
||||
{
|
||||
GMimeCryptoContext *ctx;
|
||||
GMimeCryptoContext *cctx;
|
||||
const char *prog;
|
||||
|
||||
ctx = NULL;
|
||||
cctx = NULL;
|
||||
|
||||
prog = g_getenv ("MU_GPG_PATH");
|
||||
if (prog)
|
||||
ctx = g_mime_gpg_context_new (
|
||||
(GMimePasswordRequestFunc)dummy_password_requester,
|
||||
prog);
|
||||
cctx = g_mime_gpg_context_new (
|
||||
(GMimePasswordRequestFunc)password_requester, prog);
|
||||
else {
|
||||
char *path;
|
||||
path = g_find_program_in_path ("gpg");
|
||||
if (path)
|
||||
ctx = g_mime_gpg_context_new (
|
||||
(GMimePasswordRequestFunc)dummy_password_requester,
|
||||
path);
|
||||
cctx = g_mime_gpg_context_new (
|
||||
password_requester, path);
|
||||
g_free (path);
|
||||
}
|
||||
if (!ctx) {
|
||||
if (!cctx) {
|
||||
mu_util_g_set_error (err, MU_ERROR,
|
||||
"failed to get GPG crypto context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_mime_gpg_context_set_use_agent
|
||||
(GMIME_GPG_CONTEXT(ctx),
|
||||
opts & MU_MSG_OPTION_USE_AGENT);
|
||||
g_mime_gpg_context_set_always_trust
|
||||
(GMIME_GPG_CONTEXT(ctx), FALSE);
|
||||
(GMIME_GPG_CONTEXT(cctx),
|
||||
opts & MU_MSG_OPTION_USE_AGENT ? TRUE:FALSE);
|
||||
g_mime_gpg_context_set_auto_key_retrieve
|
||||
(GMIME_GPG_CONTEXT(ctx),
|
||||
opts & MU_MSG_OPTION_AUTO_RETRIEVE_KEY);
|
||||
(GMIME_GPG_CONTEXT(cctx),
|
||||
opts & MU_MSG_OPTION_AUTO_RETRIEVE_KEY ? TRUE:FALSE);
|
||||
g_mime_gpg_context_set_always_trust
|
||||
(GMIME_GPG_CONTEXT(cctx), FALSE);
|
||||
|
||||
return ctx;
|
||||
return cctx;
|
||||
}
|
||||
|
||||
GMimeCryptoContext*
|
||||
static GMimeCryptoContext*
|
||||
get_pkcs7_crypto_context (MuMsgOptions opts, GError **err)
|
||||
{
|
||||
GMimeCryptoContext *ctx;
|
||||
GMimeCryptoContext *cctx;
|
||||
|
||||
ctx = g_mime_pkcs7_context_new
|
||||
((GMimePasswordRequestFunc)dummy_password_requester);
|
||||
if (!ctx) {
|
||||
cctx = g_mime_pkcs7_context_new (password_requester);
|
||||
if (!cctx) {
|
||||
mu_util_g_set_error (err, MU_ERROR,
|
||||
"failed to get PKCS7 crypto context");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_mime_pkcs7_context_set_always_trust
|
||||
(GMIME_PKCS7_CONTEXT(ctx), FALSE);
|
||||
(GMIME_PKCS7_CONTEXT(cctx), FALSE);
|
||||
|
||||
return ctx;
|
||||
return cctx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static GMimeCryptoContext*
|
||||
get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func,
|
||||
gpointer user_data, GError **err)
|
||||
{
|
||||
CallbackData *cbdata;
|
||||
GMimeCryptoContext *cctx;
|
||||
|
||||
if (opts & MU_MSG_OPTION_USE_PKCS7)
|
||||
cctx = get_pkcs7_crypto_context (opts, err);
|
||||
else
|
||||
cctx = get_gpg_crypto_context (opts, err);
|
||||
|
||||
/* use gobject to pass data to the callback func */
|
||||
cbdata = g_new0 (CallbackData, 1);
|
||||
cbdata->pw_func = password_func;
|
||||
cbdata->user_data = user_data;
|
||||
|
||||
g_object_set_data_full (G_OBJECT(cctx), CALLBACK_DATA,
|
||||
cbdata, (GDestroyNotify)g_free);
|
||||
return cctx;
|
||||
}
|
||||
|
||||
|
||||
@ -266,13 +332,11 @@ mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart, MuMsgOptions opts,
|
||||
return NULL; /* error */
|
||||
}
|
||||
|
||||
if (opts & MU_MSG_OPTION_USE_PKCS7)
|
||||
cctx = get_pkcs7_crypto_context (opts, err);
|
||||
else
|
||||
cctx = get_gpg_crypto_context (opts, err);
|
||||
/* dummy is good, since we don't need a password when checking
|
||||
* signatures */
|
||||
cctx = get_crypto_context (opts, dummy_password_func, NULL, err);
|
||||
|
||||
/* return a fake siginfos with the error */
|
||||
if (!cctx)
|
||||
if (!cctx) /* return a fake siginfos with the error */
|
||||
return error_sig_infos (); /* error */
|
||||
|
||||
sigs = g_mime_multipart_signed_verify (sigmpart, cctx, err);
|
||||
@ -313,7 +377,7 @@ mu_msg_part_free_sig_infos (GSList *siginfos)
|
||||
* - if not, the verdic is MU_MSG_PART_SIG_STATUS_UNKNOWN
|
||||
*/
|
||||
MuMsgPartSigStatus
|
||||
mu_msg_mime_sig_infos_verdict (GSList *sig_infos)
|
||||
mu_msg_part_sig_infos_verdict (GSList *sig_infos)
|
||||
{
|
||||
GSList *cur;
|
||||
MuMsgPartSigStatus status;
|
||||
@ -457,3 +521,120 @@ mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info)
|
||||
|
||||
return g_string_free (gstr, FALSE);
|
||||
}
|
||||
|
||||
|
||||
struct _MuMsgDecryptedPart {
|
||||
GMimeObject *decrypted;
|
||||
};
|
||||
|
||||
|
||||
struct _PartData {
|
||||
MuMsgPartDecryptForeachFunc func;
|
||||
gpointer user_data;
|
||||
GMimeCryptoContext *cctx;
|
||||
GError *err;
|
||||
};
|
||||
typedef struct _PartData PartData;
|
||||
|
||||
|
||||
static void
|
||||
each_encpart (GMimeObject *parent, GMimeObject *part, PartData *pdata)
|
||||
{
|
||||
MuMsgDecryptedPart dpart;
|
||||
|
||||
if (pdata->err)
|
||||
return;
|
||||
|
||||
if (!GMIME_IS_MULTIPART_ENCRYPTED (part))
|
||||
return; /* nothing to do for this part */
|
||||
|
||||
dpart.decrypted =
|
||||
g_mime_multipart_encrypted_decrypt (
|
||||
GMIME_MULTIPART_ENCRYPTED(part),
|
||||
pdata->cctx, NULL, &pdata->err);
|
||||
if (!dpart.decrypted || pdata->err) {
|
||||
if (!pdata->err)
|
||||
mu_util_g_set_error
|
||||
(&pdata->err,
|
||||
MU_ERROR_CRYPTO,
|
||||
"decryption failed");
|
||||
return;
|
||||
}
|
||||
|
||||
pdata->func (&dpart, pdata->user_data);
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_msg_part_decrypt_foreach (MuMsg *msg, MuMsgPartDecryptForeachFunc func,
|
||||
MuMsgPartPasswordFunc password_func,
|
||||
gpointer user_data, MuMsgOptions opts, GError **err)
|
||||
{
|
||||
PartData pdata;
|
||||
|
||||
g_return_val_if_fail (msg, FALSE);
|
||||
g_return_val_if_fail (func, FALSE);
|
||||
g_return_val_if_fail (password_func, FALSE);
|
||||
|
||||
if (!mu_msg_load_msg_file (msg, err))
|
||||
return FALSE;
|
||||
|
||||
pdata.func = func;
|
||||
pdata.user_data = user_data;
|
||||
pdata.err = NULL;
|
||||
pdata.cctx = get_crypto_context (opts, password_func,
|
||||
user_data, err);
|
||||
if (!pdata.cctx)
|
||||
return FALSE;
|
||||
|
||||
g_mime_message_foreach (msg->_file->_mime_msg,
|
||||
(GMimeObjectForeachFunc)each_encpart,
|
||||
&pdata);
|
||||
if (pdata.err) {
|
||||
*err = pdata.err;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_object_unref (pdata.cctx);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char*
|
||||
mu_msg_decrypted_part_to_string (MuMsgDecryptedPart *dpart, GError **err)
|
||||
{
|
||||
GMimePart *part;
|
||||
gboolean not_ok;
|
||||
gchar *str;
|
||||
|
||||
g_return_val_if_fail (dpart, NULL);
|
||||
|
||||
if (!GMIME_IS_PART(dpart->decrypted)) {
|
||||
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
|
||||
"wrong mime-type");
|
||||
return NULL; /* can only convert parts to string*/
|
||||
}
|
||||
part = GMIME_PART(dpart->decrypted);
|
||||
str = mu_msg_mime_part_to_string (part, ¬_ok);
|
||||
|
||||
if (not_ok) {
|
||||
g_free (str);
|
||||
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
|
||||
"failed to convert part to string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
mu_msg_decrypted_part_to_file (MuMsgDecryptedPart *dpart, const char *path,
|
||||
GError **err)
|
||||
{
|
||||
g_return_val_if_fail (dpart, FALSE);
|
||||
g_return_val_if_fail (path, FALSE);
|
||||
|
||||
return mu_msg_part_mime_save_object (dpart->decrypted, path, FALSE, FALSE,
|
||||
err);
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ const char* mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status);
|
||||
|
||||
|
||||
/**
|
||||
* summarize the signatures to one status:
|
||||
* summarize the signature checks to one status:
|
||||
*
|
||||
* - if there's any signature with MU_MSG_PART_SIG_STATUS_(ERROR|FAIL),
|
||||
* the verdict is MU_MSG_PART_SIG_STATUS_ERROR
|
||||
@ -94,7 +94,7 @@ const char* mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status);
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
MuMsgPartSigStatus mu_msg_mime_sig_infos_verdict (GSList *sig_infos);
|
||||
MuMsgPartSigStatus mu_msg_part_sig_infos_verdict (GSList *sig_infos);
|
||||
|
||||
|
||||
/**
|
||||
@ -125,4 +125,81 @@ char* mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info)
|
||||
*/
|
||||
void mu_msg_part_free_sig_infos (GSList *siginfos);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* below function only do anything useful if mu was built with crypto
|
||||
* support
|
||||
*/
|
||||
|
||||
struct _MuMsgDecryptedPart;
|
||||
typedef struct _MuMsgDecryptedPart MuMsgDecryptedPart;
|
||||
|
||||
|
||||
/**
|
||||
* callback function to provide decrypted message parts
|
||||
*
|
||||
* @param dpart a decrypted par
|
||||
* @param user_data user pointer (as passed to mu_msg_part_decrypt_foreach)
|
||||
*/
|
||||
typedef void (*MuMsgPartDecryptForeachFunc) (MuMsgDecryptedPart *dpart,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
/**
|
||||
* callback function to retrieve a password from the user
|
||||
*
|
||||
* @param user_id the user name / id to get the password for
|
||||
* @param prompt_ctx a string containing some helpful context for the prompt
|
||||
* @param reprompt whether this is a reprompt after an earlier, incorrect password
|
||||
* @param user_data the user_data pointer passed to mu_msg_part_decrypt_foreach
|
||||
*
|
||||
* @return a newly allocated (g_free'able) string
|
||||
*/
|
||||
typedef char* (*MuMsgPartPasswordFunc) (const char *user_id, const char *prompt_ctx,
|
||||
gboolean reprompt, gpointer user_data);
|
||||
|
||||
/**
|
||||
* go through all MIME-parts for this message, and decrypted all parts
|
||||
* that are encrypted. After decryption,
|
||||
*
|
||||
* If mu was built without crypto support, function does nothing.
|
||||
*
|
||||
* @param msg a valid MuMsg instance
|
||||
* @param fun a callback function called for each decrypted part
|
||||
* @param password_func a callback func called to retrieve a password from user
|
||||
* @param user_data user data which passed to the callback function
|
||||
* @param opts options
|
||||
* @param err receives error information
|
||||
*
|
||||
* @return TRUE if function succeeded, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_msg_part_decrypt_foreach (MuMsg *msg, MuMsgPartDecryptForeachFunc func,
|
||||
MuMsgPartPasswordFunc password_func,
|
||||
gpointer user_data, MuMsgOptions opts,
|
||||
GError **err);
|
||||
/**
|
||||
* convert the decrypted part to a string.
|
||||
*
|
||||
* @param dpart decrypted part
|
||||
* @param err receives error information
|
||||
*
|
||||
* @return decrypted part as a string (g_free after use), or NULL
|
||||
*/
|
||||
char* mu_msg_decrypted_part_to_string (MuMsgDecryptedPart *dpart, GError **err);
|
||||
|
||||
/**
|
||||
* write the decrypted part to a file.
|
||||
*
|
||||
* @param dpart decrypted part
|
||||
* @param path path to write it to
|
||||
* @param err receives error information
|
||||
*
|
||||
* @return TRUE if it succeeded, FALSE otherwise
|
||||
*/
|
||||
gboolean mu_msg_decrypted_part_to_file (MuMsgDecryptedPart *dpart, const char *path,
|
||||
GError **err);
|
||||
|
||||
#endif /*__MU_MSG_CRYPTO_H__*/
|
||||
|
||||
@ -242,13 +242,11 @@ msg_part_free (MuMsgPart *pi)
|
||||
#endif /*BUILD_CRYPTO*/
|
||||
}
|
||||
|
||||
|
||||
#ifdef BUILD_CRYPTO
|
||||
static void
|
||||
check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi,
|
||||
MuMsgOptions opts)
|
||||
{
|
||||
#ifdef BUILD_CRYPTO
|
||||
|
||||
GMimeContentType *ctype;
|
||||
GError *err;
|
||||
gboolean pkcs7;
|
||||
@ -275,10 +273,10 @@ check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi,
|
||||
g_warning ("error verifying signature: %s", err->message);
|
||||
g_clear_error (&err);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /*BUILD_CRYPTO*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
@ -307,7 +305,9 @@ part_foreach_cb (GMimeObject *parent, GMimeObject *mobj, PartData *pdata)
|
||||
return;
|
||||
rv = init_msg_part_from_mime_message_part (mmsg, &pi);
|
||||
if (rv && (pdata->_opts && MU_MSG_OPTION_RECURSE_RFC822))
|
||||
/* NOTE: this screws up the counting (pdata->_idx) */
|
||||
/* NOTE: this screws up the counting
|
||||
* (pdata->_idx); happily, we only use it
|
||||
* where we don't care about that */
|
||||
g_mime_message_foreach /* recurse */
|
||||
(mmsg, (GMimeObjectForeachFunc)part_foreach_cb,
|
||||
pdata);
|
||||
@ -315,8 +315,10 @@ part_foreach_cb (GMimeObject *parent, GMimeObject *mobj, PartData *pdata)
|
||||
rv = FALSE; /* ignore */
|
||||
|
||||
/* if we have crypto support, check the signature if there is one */
|
||||
#ifdef BUILD_CRYPTO
|
||||
if (pdata->_opts & MU_MSG_OPTION_CHECK_SIGNATURES)
|
||||
check_signature_maybe (parent, mobj, &pi, pdata->_opts);
|
||||
#endif /*BUILD_CRYPTO*/
|
||||
|
||||
if (rv)
|
||||
pdata->_func(pdata->_msg, &pi, pdata->_user_data);
|
||||
@ -352,7 +354,7 @@ mu_msg_part_foreach (MuMsg *msg, MuMsgPartForeachFunc func, gpointer user_data,
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
gboolean
|
||||
write_part_to_fd (GMimePart *part, int fd, GError **err)
|
||||
{
|
||||
GMimeStream *stream;
|
||||
@ -416,9 +418,9 @@ write_object_to_fd (GMimeObject *obj, int fd, GError **err)
|
||||
|
||||
|
||||
|
||||
static gboolean
|
||||
save_mime_object (GMimeObject *obj, const char *fullpath,
|
||||
gboolean overwrite, gboolean use_existing, GError **err)
|
||||
gboolean
|
||||
mu_msg_part_mime_save_object (GMimeObject *obj, const char *fullpath,
|
||||
gboolean overwrite, gboolean use_existing, GError **err)
|
||||
{
|
||||
int fd;
|
||||
gboolean rv;
|
||||
@ -498,7 +500,6 @@ mu_msg_part_filepath (MuMsg *msg, const char* targetdir, guint partidx,
|
||||
|
||||
|
||||
|
||||
|
||||
gchar*
|
||||
mu_msg_part_filepath_cache (MuMsg *msg, guint partid)
|
||||
{
|
||||
@ -556,8 +557,8 @@ mu_msg_part_save (MuMsg *msg, const char *fullpath, guint partidx,
|
||||
partidx);
|
||||
return FALSE;
|
||||
} else
|
||||
return save_mime_object (part, fullpath, overwrite,
|
||||
use_cached, err);
|
||||
return mu_msg_part_mime_save_object (part, fullpath, overwrite,
|
||||
use_cached, err);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -76,6 +76,23 @@ struct _MuMsg {
|
||||
gchar* mu_msg_mime_part_to_string (GMimePart *part, gboolean *err);
|
||||
|
||||
|
||||
/**
|
||||
* write a GMimeObject to a file
|
||||
*
|
||||
* @param obj a GMimeObject
|
||||
* @param fullpath full file path
|
||||
* @param overwrite allow overwriting existing file
|
||||
* @param if file already exist, don't bother to write
|
||||
* @param err receives error information
|
||||
*
|
||||
* @return TRUE if writing succeeded, FALSE otherwise.
|
||||
*/
|
||||
gboolean mu_msg_part_mime_save_object (GMimeObject *obj, const char *fullpath,
|
||||
gboolean overwrite, gboolean use_existing,
|
||||
GError **err);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* get the MIME part that's probably the body of the message (heuristic)
|
||||
*
|
||||
@ -87,6 +104,9 @@ gchar* mu_msg_mime_part_to_string (GMimePart *part, gboolean *err);
|
||||
GMimePart* mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean want_html);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef BUILD_CRYPTO
|
||||
/**
|
||||
* get signature information for the mime part
|
||||
@ -100,6 +120,22 @@ GMimePart* mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean want_html);
|
||||
*/
|
||||
GSList* mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart,
|
||||
MuMsgOptions opts, GError **err);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* decrypt the given mime part
|
||||
*
|
||||
* @param encpart
|
||||
* @param opts
|
||||
* @param err
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
char* mu_msg_mime_decrypt (GMimeMultipartEncrypted *encpart,
|
||||
MuMsgOptions opts, GError **err);
|
||||
|
||||
|
||||
#endif /*BUILD_CRYPTO*/
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@ -242,7 +242,7 @@ elvis (const char *s1, const char *s2)
|
||||
static const char*
|
||||
sig_verdict (GSList *sig_infos)
|
||||
{
|
||||
switch (mu_msg_mime_sig_infos_verdict (sig_infos)) {
|
||||
switch (mu_msg_part_sig_infos_verdict (sig_infos)) {
|
||||
case MU_MSG_PART_SIG_STATUS_GOOD:
|
||||
return ":signature good";
|
||||
case MU_MSG_PART_SIG_STATUS_BAD:
|
||||
|
||||
@ -414,6 +414,9 @@ enum _MuError {
|
||||
MU_ERROR_CONTACTS = 50,
|
||||
MU_ERROR_CONTACTS_CANNOT_RETRIEVE = 51,
|
||||
|
||||
/* crypto related errors */
|
||||
MU_ERROR_CRYPTO = 60,
|
||||
|
||||
|
||||
/* File errors */
|
||||
/* generic file-related error */
|
||||
|
||||
Reference in New Issue
Block a user