aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camel/ChangeLog2
-rw-r--r--camel/camel-mime-utils.c389
-rw-r--r--camel/camel-smime-context.c53
-rw-r--r--camel/camel-smime-context.h1
4 files changed, 383 insertions, 62 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog
index 9e8f8c9fe6..e3edbcd426 100644
--- a/camel/ChangeLog
+++ b/camel/ChangeLog
@@ -1,5 +1,7 @@
2001-05-31 Jeffrey Stedfast <fejj@ximian.com>
+ * camel-smime-context.c: Numerous fixes to get it to compile.
+
* camel.h: Added camel-cipher-context.h, camel-cms-context.h,
camel-smime-context.h, and camel-smime-utils.h
diff --git a/camel/camel-mime-utils.c b/camel/camel-mime-utils.c
index 2686b9921b..34a1f35d11 100644
--- a/camel/camel-mime-utils.c
+++ b/camel/camel-mime-utils.c
@@ -1736,27 +1736,211 @@ header_decode_int(const char **in)
return v;
}
-static int
-header_decode_param(const char **in, char **paramp, char **valuep)
+#define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
+
+static char *
+hex_decode (const char *in, int len)
+{
+ const guchar *inend = (const guchar *) in + len;
+ guchar *inptr, *outptr;
+ char *outbuf;
+
+ outptr = outbuf = g_malloc (len);
+
+ inptr = (guchar *) in;
+ while (inptr < inend) {
+ if (*inptr == '%') {
+ if (isxdigit (inptr[1]) && isxdigit (inptr[2])) {
+ *outptr++ = HEXVAL (inptr[1]) * 16 + HEXVAL (inptr[2]);
+ inptr += 3;
+ } else
+ *outptr++ = *inptr++;
+ } else
+ *outptr++ = *inptr++;
+ }
+
+ *outptr = '\0';
+
+ return outbuf;
+}
+
+/* an rfc2184 encoded string looks something like:
+ * us-ascii'en'This%20is%20even%20more%20
+ */
+
+static char *
+rfc2184_decode (const char *in, int len)
+{
+ const char *inptr = in;
+ const char *inend = in + len;
+ char *decoded = NULL;
+ char *encoding;
+
+ inptr = memchr (inptr, '\'', len);
+ if (!inptr)
+ return NULL;
+
+ encoding = g_strndup (in, inptr - in);
+ inptr = memchr (inptr + 1, '\'', inend - inptr - 1);
+ if (!inptr) {
+ g_free (encoding);
+ return NULL;
+ }
+
+ inptr++;
+ if (inptr < inend) {
+ char *decword, *inbuf, *outbase, *outbuf;
+ int inlen, outlen;
+ iconv_t ic;
+
+ inbuf = decword = hex_decode (inptr, inend - inptr);
+ inlen = strlen (inbuf);
+
+ outlen = inlen * 6 + 16;
+ outbuf = outbase = g_malloc (outlen);
+
+ ic = iconv_open ("UTF-8", encoding);
+ if (ic != (iconv_t) -1) {
+ int ret;
+
+ ret = iconv (ic, &inbuf, &inlen, &outbuf, &outlen);
+ if (ret >= 0) {
+ iconv (ic, NULL, 0, &outbuf, &outlen);
+ *outbuf = '\0';
+ g_free (decoded);
+ decoded = outbase;
+ }
+
+ iconv_close (ic);
+ } else {
+ decoded = decword;
+ }
+ }
+
+ return decoded;
+}
+
+/* This function is basically the same as decode_token()
+ * except that it will not accept *'s which have a special
+ * meaning for rfc2184 params */
+static char *
+decode_param_token (const char **in)
{
const char *inptr = *in;
- char *param, *value=NULL;
+ const char *start;
+
+ header_decode_lwsp (&inptr);
+ start = inptr;
+ while (is_ttoken (*inptr) && *inptr != '*')
+ inptr++;
+ if (inptr > start) {
+ *in = inptr;
+ return g_strndup (start, inptr-start);
+ } else {
+ return NULL;
+ }
+}
- param = decode_token(&inptr);
- header_decode_lwsp(&inptr);
- if (*inptr == '=') {
+static gboolean
+header_decode_rfc2184_param (const char **in, char **paramp, int *part, gboolean *value_is_encoded)
+{
+ gboolean is_rfc2184 = FALSE;
+ const char *inptr = *in;
+ char *param;
+
+ param = decode_param_token (&inptr);
+ header_decode_lwsp (&inptr);
+
+ if (*inptr == '*') {
+ is_rfc2184 = TRUE;
inptr++;
- value = header_decode_value(&inptr);
+ header_decode_lwsp (&inptr);
+ if (*inptr == '=') {
+ /* form := param*=value */
+ if (value_is_encoded)
+ *value_is_encoded = TRUE;
+ } else {
+ /* form := param*#=value or param*#*=value */
+ *part = header_decode_int (&inptr);
+ header_decode_lwsp (&inptr);
+ if (*inptr == '*') {
+ /* form := param*#*=value */
+ if (value_is_encoded)
+ *value_is_encoded = TRUE;
+ inptr++;
+ header_decode_lwsp (&inptr);
+ }
+ }
}
+
+ if (paramp)
+ *paramp = param;
+
+ if (param)
+ *in = inptr;
+
+ return is_rfc2184;
+}
+static int
+header_decode_param(const char **in, char **paramp, char **valuep, int *is_rfc2184_param)
+{
+ gboolean is_rfc2184_encoded = FALSE;
+ gboolean is_rfc2184 = FALSE;
+ const char *inptr = *in;
+ char *param, *value = NULL;
+ int rfc2184_part = -1;
+
+ *is_rfc2184_param = FALSE;
+
+ is_rfc2184 = header_decode_rfc2184_param (&inptr, &param, &rfc2184_part,
+ &is_rfc2184_encoded);
+
+ if (*inptr == '=') {
+ inptr++;
+ value = header_decode_value (&inptr);
+ if (is_rfc2184) {
+ /* We have ourselves an rfc2184 parameter */
+
+ if (rfc2184_part == -1) {
+ /* rfc2184 allows the value to be broken into
+ * multiple parts - this isn't one of them so
+ * it is safe to decode it.
+ */
+ char *val;
+
+ val = rfc2184_decode (value, strlen (value));
+ g_free (value);
+ value = val;
+ } else {
+ /* Since we are expecting to find the rest of
+ * this paramter value later, let our caller know.
+ */
+ *is_rfc2184_param = TRUE;
+ }
+ } else if (value && !strcmp (value, "=?")) {
+ /* We have a broken param value that is rfc2047 encoded.
+ * Since both Outlook and Netscape/Mozilla do this, we
+ * should handle this case.
+ */
+ char *val;
+
+ val = rfc2047_decode_word (value, strlen (value));
+ if (val) {
+ g_free (value);
+ value = val;
+ }
+ }
+ }
+
if (param && value) {
*paramp = param;
*valuep = value;
*in = inptr;
return 0;
} else {
- g_free(param);
- g_free(value);
+ g_free (param);
+ g_free (value);
return 1;
}
}
@@ -2425,28 +2609,73 @@ header_decode_param_list(const char **in)
{
const char *inptr = *in;
struct _header_param *head = NULL, *tail = NULL;
-
- header_decode_lwsp(&inptr);
+ gboolean last_was_rfc2184 = FALSE;
+ gboolean is_rfc2184 = FALSE;
+
+ header_decode_lwsp (&inptr);
+
while (*inptr == ';') {
char *param, *value;
struct _header_param *p;
-
+
inptr++;
/* invalid format? */
- if (header_decode_param(&inptr, &param, &value) != 0)
+ if (header_decode_param (&inptr, &param, &value, &is_rfc2184) != 0)
break;
-
- p = g_malloc(sizeof(*p));
- p->name = param;
- p->value = value;
- p->next = NULL;
- if (head == NULL)
- head = p;
- if (tail)
- tail->next = p;
- tail = p;
- header_decode_lwsp(&inptr);
+
+ if (is_rfc2184 && tail && !g_strcasecmp (param, tail->name)) {
+ /* rfc2184 allows a parameter to be broken into multiple parts
+ * and it looks like we've found one. Append this value to the
+ * last value.
+ */
+ GString *gvalue;
+
+ gvalue = g_string_new (tail->value);
+ g_string_append (gvalue, value);
+ g_free (tail->value);
+ g_free (value);
+
+ tail->value = gvalue->str;
+ g_string_free (gvalue, FALSE);
+ } else {
+ if (last_was_rfc2184) {
+ /* We've finished gathering the values for the last param
+ * so it is now safe to decode it.
+ */
+ char *val;
+
+ val = rfc2184_decode (tail->value, strlen (tail->value));
+ g_free (tail->value);
+ tail->value = val;
+ }
+
+ p = g_malloc (sizeof (struct _header_param));
+ p->name = param;
+ p->value = value;
+ p->next = NULL;
+ if (head == NULL)
+ head = p;
+ if (tail)
+ tail->next = p;
+ tail = p;
+ }
+
+ last_was_rfc2184 = is_rfc2184;
+
+ header_decode_lwsp (&inptr);
+ }
+
+ if (last_was_rfc2184) {
+ /* We've finished gathering the values for the last param
+ * so it is now safe to decode it.
+ */
+ char *val;
+
+ val = rfc2184_decode (tail->value, strlen (tail->value));
+ g_free (tail->value);
+ tail->value = val;
}
+
*in = inptr;
return head;
}
@@ -2460,31 +2689,115 @@ header_param_list_decode(const char *in)
return header_decode_param_list(&in);
}
+/* FIXME: I wrote this in a quick & dirty fasion - it may not be 100% correct */
+static char *
+header_encode_param (const unsigned char *in, gboolean *encoded)
+{
+ const unsigned char *inptr = in;
+ char *outstr, *charset;
+ int encoding;
+ GString *out;
+
+ *encoded = FALSE;
+
+ g_return_val_if_fail (g_utf8_validate (in, -1, NULL), NULL);
+
+ if (in == NULL)
+ return NULL;
+
+ /* do a quick us-ascii check (the common case?) */
+ while (*inptr) {
+ if (*inptr > 127)
+ break;
+ inptr++;
+ }
+
+ if (*inptr == '\0')
+ return g_strdup (in);
+
+ out = g_string_new ("");
+ inptr = in;
+ encoding = 0;
+ while (inptr && *inptr) {
+ gunichar c;
+ const char *newinptr;
+
+ newinptr = g_utf8_next_char (inptr);
+ c = g_utf8_get_char (inptr);
+ if (newinptr == NULL || !g_unichar_validate (c)) {
+ w(g_warning ("Invalid UTF-8 sequence encountered (pos %d, char '%c'): %s",
+ (inptr-in), inptr[0], in));
+ inptr++;
+ continue;
+ }
+
+ if (c > 127 && c < 256) {
+ encoding = MAX (encoding, 1);
+ g_string_sprintfa (out, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]);
+ } else if (c >= 256) {
+ encoding = MAX (encoding, 2);
+ g_string_sprintfa (out, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]);
+ } else if (is_lwsp (c) || camel_mime_special_table[c] & IS_ESAFE) {
+ g_string_sprintfa (out, "%%%c%c", tohex[(c >> 4) & 0xf], tohex[c & 0xf]);
+ } else {
+ g_string_append_c (out, c);
+ }
+
+ inptr = newinptr;
+ }
+
+ switch (encoding) {
+ default:
+ g_string_prepend (out, "iso-8859-1''");
+ break;
+ case 2:
+ charset = g_strdup_printf ("%s''", camel_charset_best (in, inptr - in));
+ g_string_prepend (out, charset);
+ g_free (charset);
+ break;
+ }
+
+ outstr = out->str;
+ g_string_free (out, FALSE);
+ *encoded = TRUE;
+
+ return outstr;
+}
+
void
header_param_list_format_append(GString *out, struct _header_param *p)
{
int len = out->len;
- char *ch;
-
+
while (p) {
+ gboolean encoded = FALSE;
+ char *value, *ch = NULL;
int here = out->len;
- if (len+strlen(p->name)+strlen(p->value)>60) {
- out = g_string_append(out, ";\n\t");
+
+ value = header_encode_param (p->value, &encoded);
+
+ if (!encoded) {
+ for (ch = value; *ch; ch++) {
+ if (is_tspecial (*ch) || is_lwsp (*ch))
+ break;
+ }
+ }
+
+ if (len + strlen (p->name) + strlen (value) > 60) {
+ out = g_string_append (out, ";\n\t");
len = 0;
} else
out = g_string_append (out, "; ");
- g_string_sprintfa(out, "%s=", p->name);
-
- for (ch = p->value; *ch; ch++) {
- if (is_tspecial (*ch) || is_lwsp (*ch))
- break;
- }
- if (!*ch)
- g_string_append(out, p->value);
+ g_string_sprintfa (out, "%s%s=", p->name, encoded ? "*" : "");
+
+ if (!ch || !*ch)
+ g_string_append (out, value);
else
- quote_word(out, TRUE, p->value, strlen(p->value));
-
+ quote_word (out, TRUE, value, strlen (value));
+
+ g_free (value);
+
len += (out->len - here);
p = p->next;
}
diff --git a/camel/camel-smime-context.c b/camel/camel-smime-context.c
index 7d2814fb82..8f944841fe 100644
--- a/camel/camel-smime-context.c
+++ b/camel/camel-smime-context.c
@@ -32,9 +32,15 @@
#include "camel-stream-filter.h"
#include "camel-stream-fs.h"
#include "camel-stream-mem.h"
+#include "camel-mime-part.h"
+#include "camel-multipart.h"
#include "nss.h"
#include <cms.h>
+#include <cert.h>
+#include <certdb.h>
+#include <pkcs11.h>
+#include <smime.h>
#include <gtk/gtk.h> /* for _() macro */
@@ -64,7 +70,7 @@ static CamelMimeMessage *smime_envelope (CamelCMSContext *ctx, CamelMimeMessage
static CamelMimeMessage *smime_decode (CamelCMSContext *ctx, CamelMimeMessage *message,
CamelCMSValidityInfo **info, CamelException *ex);
-static CamelCipherContextClass *parent_class;
+static CamelCMSContextClass *parent_class;
static void
camel_smime_context_init (CamelSMimeContext *context)
@@ -89,11 +95,11 @@ camel_smime_context_class_init (CamelSMimeContextClass *camel_smime_context_clas
parent_class = CAMEL_CMS_CONTEXT_CLASS (camel_type_get_global_classfuncs (camel_cms_context_get_type ()));
- camel_cms_context_class->sign = cms_sign;
- camel_cms_context_class->certsonly = cms_certsonly;
- camel_cms_context_class->encrypt = cms_encrypt;
- camel_cms_context_class->envelope = cms_envelope;
- camel_cms_context_class->decode = cms_decode;
+ camel_cms_context_class->sign = smime_sign;
+ camel_cms_context_class->certsonly = smime_certsonly;
+ camel_cms_context_class->encrypt = smime_encrypt;
+ camel_cms_context_class->envelope = smime_envelope;
+ camel_cms_context_class->decode = smime_decode;
}
CamelType
@@ -198,7 +204,7 @@ signed_data (CamelSMimeContext *ctx, const char *userid, gboolean signing_time,
return NULL;
}
- if ((cert = CERT_FindCertByNickname (ctx->priv->certdb, userid)) == NULL) {
+ if ((cert = CERT_FindCertByNickname (ctx->priv->certdb, (char *) userid)) == NULL) {
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
_("The signature certificate for \"%s\" does not exist."),
userid);
@@ -386,17 +392,17 @@ smime_sign (CamelCMSContext *ctx, CamelMimeMessage *message,
static NSSCMSMessage *
-certsonly_data (CamelSMimeContext *ctx, const char *userid, GByteArray *recipients, CamelException *ex)
+certsonly_data (CamelSMimeContext *ctx, const char *userid, GPtrArray *recipients, CamelException *ex)
{
NSSCMSMessage *cmsg = NULL;
NSSCMSContentInfo *cinfo;
NSSCMSSignedData *sigd;
CERTCertificate **rcerts;
- int i;
+ int i = 0;
/* find the signer's and the recipients' certs */
rcerts = g_new (CERTCertificate *, recipients->len + 2);
- rcerts[0] = CERT_FindCertByNicknameOrEmailAddr (ctx->priv->certdb, userid);
+ rcerts[0] = CERT_FindCertByNicknameOrEmailAddr (ctx->priv->certdb, (char *) userid);
if (!rcerts[0]) {
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
_("Failed to find certificate for \"%s\"."),
@@ -424,7 +430,7 @@ certsonly_data (CamelSMimeContext *ctx, const char *userid, GByteArray *recipien
/* add the recipient cert chain */
for (i = 0; i < recipients->len; i++) {
- NSS_CMSSignedData_AddCertChain (sigd, certs[i]);
+ NSS_CMSSignedData_AddCertChain (sigd, rcerts[i]);
}
cinfo = NSS_CMSMessage_GetContentInfo (cmsg);
@@ -452,6 +458,7 @@ smime_certsonly (CamelCMSContext *ctx, CamelMimeMessage *message,
CamelException *ex)
{
CamelMimeMessage *mesg = NULL;
+ struct _GetPasswdData *data;
NSSCMSMessage *cmsg = NULL;
PLArenaPool *arena;
NSSCMSEncoderContext *ecx;
@@ -512,7 +519,7 @@ enveloped_data (CamelSMimeContext *ctx, const char *userid, GPtrArray *recipient
/* find the recipient certs by email address or nickname */
rcerts = g_new (CERTCertificate *, recipients->len + 2);
- rcerts[0] = CERT_FindCertByNicknameOrEmailAddr (ctx->priv->certdb, userid);
+ rcerts[0] = CERT_FindCertByNicknameOrEmailAddr (ctx->priv->certdb, (char *) userid);
if (!rcerts[0]) {
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
_("Failed to find certificate for \"%s\"."),
@@ -574,6 +581,7 @@ smime_envelope (CamelCMSContext *ctx, CamelMimeMessage *message,
CamelException *ex)
{
CamelMimeMessage *mesg = NULL;
+ struct _GetPasswdData *data;
NSSCMSMessage *cmsg = NULL;
PLArenaPool *arena;
NSSCMSEncoderContext *ecx;
@@ -622,8 +630,8 @@ smime_envelope (CamelCMSContext *ctx, CamelMimeMessage *message,
struct _BulkKey {
- PK11KeySym *bulkkey;
- SECOidTag bulkkeytag;
+ PK11SymKey *bulkkey;
+ SECOidTag bulkalgtag;
int keysize;
};
@@ -754,8 +762,8 @@ smime_encrypt (CamelCMSContext *ctx, CamelMimeMessage *message,
static NSSCMSMessage *
-decode (CamelSMimeContext *ctx, GByteArray *input, CamelStream *ostream,
- CamelCMSValidityInfo **info, CamelExcepton *ex)
+decode_data (CamelSMimeContext *ctx, GByteArray *input, CamelStream *ostream,
+ CamelCMSValidityInfo **info, CamelException *ex)
{
NSSCMSDecoderContext *dcx;
struct _GetPasswdData *data;
@@ -765,17 +773,14 @@ decode (CamelSMimeContext *ctx, GByteArray *input, CamelStream *ostream,
NSSCMSSignedData *sigd = NULL;
NSSCMSEnvelopedData *envd;
NSSCMSEncryptedData *encd;
- SECAlgorithmID **digestalgs;
int nlevels, i, nsigners, j;
char *signercn;
NSSCMSSignerInfo *si;
SECOidTag typetag;
- SECItem **digests;
- PLArenaPool *arena;
- SECItem *item, sitem = { 0, 0, 0 };
+ SECItem *item;
data = g_new (struct _GetPasswdData, 1);
- data->session = ctx->session;
+ data->session = CAMEL_CMS_CONTEXT (ctx)->session;
data->userid = NULL;
data->ex = ex;
@@ -783,7 +788,7 @@ decode (CamelSMimeContext *ctx, GByteArray *input, CamelStream *ostream,
NULL, NULL,
smime_get_password, data,
decode_key_cb,
- decodeOptions->bulkkey);
+ NULL);
NSS_CMSDecoder_Update (dcx, input->data, input->len);
@@ -861,7 +866,7 @@ decode (CamelSMimeContext *ctx, GByteArray *input, CamelStream *ostream,
certUsageEmailSigner);
if (signers) {
- signers->signeercn = g_strdup (signercn);
+ signers->signercn = g_strdup (signercn);
signers->status = g_strdup (
NSS_CMSUtil_VerificationStatusToString (
NSS_CMSSignerInfo_GetVerificationStatus (si)));
@@ -918,7 +923,7 @@ smime_decode (CamelCMSContext *ctx, CamelMimeMessage *message,
buf = CAMEL_STREAM_MEM (stream)->buffer;
ostream = camel_stream_mem_new ();
- cmsg = decode (CAMEL_SMIME_CONTEXT (ctx), buf, ostream, info, ex);
+ cmsg = decode_data (CAMEL_SMIME_CONTEXT (ctx), buf, ostream, info, ex);
camel_object_unref (CAMEL_OBJECT (stream));
if (!cmsg) {
camel_object_unref (CAMEL_OBJECT (ostream));
diff --git a/camel/camel-smime-context.h b/camel/camel-smime-context.h
index 3a2214fc3a..d1c54d1bfc 100644
--- a/camel/camel-smime-context.h
+++ b/camel/camel-smime-context.h
@@ -42,6 +42,7 @@ typedef struct _CamelSMimeContext {
struct _CamelSMimeContextPrivate *priv;
+ char *encryption_key;
} CamelSMimeContext;
typedef struct _CamelSMimeContextClass {