[pan2/testing] a little broken ...
- From: Heinrich MÃller <henmull src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pan2/testing] a little broken ...
- Date: Mon, 12 Dec 2011 13:29:39 +0000 (UTC)
commit 0220ec7dc64ddc74447ce477c024976840bcb7af
Author: Heinrich MÃller <henmull src gnome org>
Date: Mon Dec 12 14:29:15 2011 +0100
a little broken ...
configure.in | 16 +-
pan/gui/body-pane.cc | 35 +-
pan/gui/body-pane.h | 4 +-
pan/usenet-utils/mime-utils.cc | 2045 ++++++++++++++++++++--------------------
pan/usenet-utils/mime-utils.h | 2 +-
5 files changed, 1063 insertions(+), 1039 deletions(-)
---
diff --git a/configure.in b/configure.in
index 5ce2b3c..a89b89a 100644
--- a/configure.in
+++ b/configure.in
@@ -122,7 +122,7 @@ fi
dnl Check for libnotify if user-enabled for popup notifications
AC_ARG_ENABLE([libnotify],
-AC_HELP_STRING([--enable-libnotify],[enable libnotify support]),,[enable_libnotify=yes])
+AC_HELP_STRING([--enable-libnotify],[enable libnotify support]),[enable_libnotify=$enableval],[enable_libnotify=yes])
if test "x$enable_libnotify" = "xyes" ; then
PKG_CHECK_MODULES([LIBNOTIFY],[libnotify >= $LIBNOTIFY_REQUIRED],[HAVE_LIBNOTIFY="yes"],[HAVE_LIBNOTIFY="no"])
AC_SUBST([LIBNOTIFY_CFLAGS])
@@ -134,7 +134,7 @@ fi
dnl check for libgsasl for secure authentication
AC_ARG_ENABLE([libgsasl],
-AC_HELP_STRING([--enable-libgsasl],[enable libgsasl support]),,[enable_libgsasl=yes])
+AC_HELP_STRING([--enable-libgsasl],[enable libgsasl support]),[enable_libgsasl=$enableval],[enable_libgsasl=yes])
if test "x$enable_libgsasl" = "xyes" ; then
PKG_CHECK_MODULES([LIBGSASL],[libgsasl >= $LIBGSASL_REQUIRED],[HAVE_SASL="yes"],[HAVE_SASL="no"])
AC_SUBST([LIBGSASL_CFLAGS])
@@ -145,9 +145,9 @@ if test "x$enable_libgsasl" = "xyes" ; then
fi
dnl Check for gnome-keyring if user-enabled for popup notifications
-AC_ARG_ENABLE([gnome-keyring],
-AC_HELP_STRING([--enable-gnome-keyring],[enable gnome-keyring support]),,[enable_gnome-keyring=yes])
-if test "x$enable-gnome-keyring" = "xyes" ; then
+AC_ARG_ENABLE([gkr],
+AC_HELP_STRING([--enable-gkr],[enable gnome-keyring support]),[enable_gkr=$enableval],[enable_gkr=yes])
+if test "x$enable_gkr" = "xyes" ; then
PKG_CHECK_MODULES([LIBGNOME_KEYRING_1],[gnome-keyring-1 >= $LIBGKR_REQUIRED],[HAVE_GKR="yes"],[HAVE_GKR="no"])
AC_SUBST([LIBGNOME_KEYRING_1_CFLAGS])
AC_SUBST([LIBGNOME_KEYRING_1_LIBS])
@@ -158,10 +158,10 @@ fi
dnl Check for gpgme for message encryption / signing
AC_ARG_ENABLE([gpgme],
-AC_HELP_STRING([--enable-gpgme],[enable gpgme support]),,[enable_gpgme=no])
-if test "x$enable-gpgme" = "xyes" ; then
+AC_HELP_STRING([--enable-gpgme],[enable gpgme support]),[enable_gpgme=$enableval],[enable_gpgme=yes])
+if test "x$enable_gpgme" = "xyes" ; then
AM_PATH_GPGME([$LIBGPGME_REQUIRED],[HAVE_GPGME="yes"],[HAVE_GPGME="no"])
- if test "x$HAVE_GKR" = "xyes"; then
+ if test "x$HAVE_GPGME" = "xyes"; then
AC_DEFINE([HAVE_GPGME],[1],[gpgme for message encryption / signing])
fi
fi
diff --git a/pan/gui/body-pane.cc b/pan/gui/body-pane.cc
index 56ef715..bd0a79e 100644
--- a/pan/gui/body-pane.cc
+++ b/pan/gui/body-pane.cc
@@ -918,7 +918,7 @@ namespace
#ifdef HAVE_GPGME
bool
-BodyPane ::get_gpgsig_from_gmime_part (GMimePart * part)
+BodyPane ::get_gpgsig_from_gmime_part (GMimeObject* parent, GMimeObject* base, GMimePart * part)
{
GMimeDataWrapper * wrapper (g_mime_part_get_content_object (part));
GMimeStream * mem_stream (g_mime_stream_mem_new ());
@@ -926,7 +926,9 @@ BodyPane ::get_gpgsig_from_gmime_part (GMimePart * part)
{
g_mime_data_wrapper_write_to_stream (wrapper, mem_stream);
g_mime_stream_reset(mem_stream);
- gpg_decrypt_and_verify(_signer_info, _gpgerr, mem_stream);
+ gpg_decrypt_and_verify(_signer_info, _gpgerr, mem_stream,
+ g_mime_multipart_index_of(GMIME_MULTIPART(parent),base),
+ parent);
return true;
}
return false;
@@ -934,7 +936,7 @@ BodyPane ::get_gpgsig_from_gmime_part (GMimePart * part)
#endif
void
-BodyPane :: append_part (GMimeObject * obj, GtkAllocation * widget_size)
+BodyPane :: append_part (GMimeObject * parent, GMimeObject * obj, GtkAllocation * widget_size)
{
bool is_done (false);
@@ -1017,9 +1019,10 @@ BodyPane :: append_part (GMimeObject * obj, GtkAllocation * widget_size)
is_done = true;
#ifdef HAVE_GPGME
/* verify signature */
+
if (g_mime_content_type_is_type (type, "*", "pgp-signature"))
{
- bool res = get_gpgsig_from_gmime_part(part);
+ bool res = get_gpgsig_from_gmime_part(parent, obj, part);
std::cerr<<"1023\n";
if (res) update_sig_valid(_gpgerr.verify_ok);
}
@@ -1040,13 +1043,13 @@ BodyPane :: append_part (GMimeObject * obj, GtkAllocation * widget_size)
}
void
-BodyPane :: foreach_part_cb (GMimeObject* /*parent*/, GMimeObject* o, gpointer self)
+BodyPane :: foreach_part_cb (GMimeObject* parent, GMimeObject* o, gpointer self)
{
BodyPane * pane = static_cast<BodyPane*>(self);
GtkWidget * w (pane->_text);
GtkAllocation aloc;
gtk_widget_get_allocation(w, &aloc);
- pane->append_part (o, &aloc);
+ pane->append_part (parent, o, &aloc);
}
@@ -1141,15 +1144,6 @@ BodyPane :: set_text_from_message (GMimeMessage * message)
w = std::max (w, l);
}
}
-
- // obsolete in favor of the certificate icon tooltip (bodypane)
-// const StringView gpg (g_mime_object_get_header ((GMimeObject *) message, "X-GPG-Signed"));
-// if (!gpg.empty())
-// {
-// char buf[256];
-// l = add_header_line (s, message, _("GPG-Signed message signature "), "X-GPG-Signed", fallback_charset);
-// w = std::max (w, l);
-// }
}
s.resize (s.size()-1); // remove trailing linefeed
@@ -1237,7 +1231,7 @@ BodyPane :: set_text_from_message (GMimeMessage * message)
gtk_text_buffer_get_start_iter (_buffer, &iter);
gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW(_text), &iter, 0.0, true, 0.0, 0.0);
}
- std::cerr<<"1240\n";
+ std::cerr<<"1234\n";
}
@@ -1273,7 +1267,7 @@ BodyPane:: on_tooltip_query(GtkWidget *widget,
GPGDecErr& err = pane->_gpgerr;
GPGSignersInfo& info = pane->_signer_info;
- g_return_val_if_fail(err.dec_ok, false);
+// g_return_val_if_fail(err.dec_ok, false);
g_return_val_if_fail(err.err == GPG_ERR_NO_ERROR || err.err == GPG_ERR_NO_DATA, false);
if (err.no_sigs) return false;
@@ -1281,9 +1275,6 @@ BodyPane:: on_tooltip_query(GtkWidget *widget,
if (!err.v_res->signatures) return false;
if (!err.v_res->signatures->fpr) return false;
-// get uid from fingerprint
-// GPGSignersInfo info = get_uids_from_fingerprint(err->v_res->signatures->fpr);
-
EvolutionDateMaker ed;
char buf[2048];
@@ -1312,6 +1303,8 @@ BodyPane :: update_sig_valid(int i)
gtk_image_clear(GTK_IMAGE(_sig_icon));
+ i = 1;
+
switch (i)
{
case 0:
@@ -1350,7 +1343,7 @@ BodyPane :: set_article (const Article& a)
val = 0;
}
#ifdef HAVE_GPGME
- std::cerr<<"1354\n";
+ std::cerr<<"1344\n";
update_sig_valid(val);
#endif
refresh ();
diff --git a/pan/gui/body-pane.h b/pan/gui/body-pane.h
index 17c111b..2120f50 100644
--- a/pan/gui/body-pane.h
+++ b/pan/gui/body-pane.h
@@ -92,7 +92,7 @@ namespace pan
bool read_more_or_less (bool more);
char* body_to_utf8 (GMimePart*);
void set_text_from_message (GMimeMessage*);
- void append_part (GMimeObject*, GtkAllocation*);
+ void append_part (GMimeObject*, GMimeObject*, GtkAllocation*);
static gboolean expander_activated_idle (gpointer self);
static void expander_activated_cb (GtkExpander*, gpointer self);
static void foreach_part_cb (GMimeObject*, GMimeObject*, gpointer self);
@@ -114,7 +114,7 @@ namespace pan
#ifdef HAVE_GPGME
GPGDecErr _gpgerr;
GPGSignersInfo _signer_info;
- bool get_gpgsig_from_gmime_part (GMimePart * part);
+ bool get_gpgsig_from_gmime_part (GMimeObject* parent, GMimeObject* base, GMimePart * part);
#endif
private:
diff --git a/pan/usenet-utils/mime-utils.cc b/pan/usenet-utils/mime-utils.cc
index aeb9853..d888773 100644
--- a/pan/usenet-utils/mime-utils.cc
+++ b/pan/usenet-utils/mime-utils.cc
@@ -478,1209 +478,1240 @@ namespace pan
return true;
}
-#ifdef HAVE_GPGME
- GMimeStream* gpg_decrypt_and_verify (GPGSignersInfo& signer_info, GPGDecErr& info, GMimeStream* s, const char* body)
+ void apply_source_and_maybe_filter (TempPart * part, GMimeStream * s)
{
- GMimeStream* decrypted = g_mime_stream_mem_new ();
-
- ssize_t stream_len = g_mime_stream_length(s);
-
- char* streambuf = new char[stream_len];
-
- g_mime_stream_read(s, streambuf, stream_len);
- g_object_unref(s);
-
- info.err = GPG_ERR_NO_ERROR;
-
- gpgme_data_t gpg_buf, gpg_out_buf, body_data;
- gpgme_key_t key;
-
- StringView v(streambuf);
- gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
-
- if (body) gpgme_data_new_from_mem (&body_data, body, strlen(body), 0);
-
- gpgme_strerror(gpgme_data_new (&gpg_out_buf));
-
- gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_NONE);
-
- info.err = gpgme_op_decrypt_verify (gpg_ctx, gpg_buf, gpg_out_buf);
+ if (!part->stream) {
+ part->stream = g_mime_stream_mem_new ();
+ if (part->type != ENC_PLAIN && part->type != ENC_GPG) {
+ part->filter_stream = g_mime_stream_filter_new (part->stream);
+ switch (part->type)
+ {
+ case ENC_UU:
+ part->filter = g_mime_filter_basic_new (GMIME_CONTENT_ENCODING_UUENCODE, FALSE);
+ break;
- // no data to decrypt, check for signature validity anyway....
- if (gpgme_err_code(info.err) == GPG_ERR_NO_DATA || gpgme_err_code(info.err) == GPG_ERR_NO_ERROR)
- info.dec_ok = true;
- else
- return decrypted;
+ case ENC_YENC:
+ part->filter = g_mime_filter_yenc_new (FALSE);
+ break;
+ }
- if (gpgme_err_code(info.err) == GPG_ERR_NO_DATA && body) // verify attached sigs, too
- {
- info.err = gpgme_op_verify (gpg_ctx, gpg_buf, body_data, 0);
+ g_mime_stream_filter_add (GMIME_STREAM_FILTER(part->filter_stream),
+ part->filter);
+ }
}
- info.dec_res = gpgme_op_decrypt_result (gpg_ctx);
- info.v_res = gpgme_op_verify_result (gpg_ctx);
+ g_mime_stream_write_to_stream (s, (part->type == ENC_PLAIN || part->type == ENC_GPG) ?
+ part->stream : part->filter_stream);
+ g_object_unref (s);
+ }
- if (info.v_res->signatures)
- {
- info.no_sigs = false;
- if (gpgme_err_code(info.v_res->signatures->status) == GPG_ERR_NO_ERROR)
- info.verify_ok = true;
- }
- else
- info.no_sigs = true;
+ struct sep_state
+ {
+ temp_parts_t master_list;
+ temp_parts_t current_list;
+ TempPart *uu_temp;
+ std::string gpg_verified;
+#ifdef HAVE_GPGME
+ GPGDecErr gpgerr;
+ GPGSignersInfo signer_info;
+#endif
+ sep_state():uu_temp(NULL), gpg_verified("") {};
+ };
- delete streambuf;
+ bool
+ separate_encoded_parts (GMimeStream * istream, sep_state &state)
+ {
+ temp_parts_t& master(state.master_list);
+ temp_parts_t& appendme(state.current_list);
+ TempPart * cur = NULL;
+ EncType type = ENC_PLAIN;
+ GByteArray * line;
+ gboolean yenc_looking_for_part_line = FALSE;
+ gboolean gpg_looking_for_line = FALSE;
+ gboolean gpg_is_signed = FALSE;
+ gboolean gpg_signed_end_found = FALSE;
+ gint64 linestart_pos = 0;
+ gint64 sub_begin = 0;
+ gint64 signed_msg_start = 0;
+ gint64 signed_msg_end = 0;
+ guint line_len;
+ bool found = false;
- gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
+ /* sanity clause */
+ pan_return_val_if_fail (istream!=NULL,false);
- ssize_t ret(0);
- char buffer[4096]={0};
- gint64 len(0);
+ sub_begin = 0;
+ line = g_byte_array_sized_new (4096);
- while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
+ while ((line_len = stream_readln (istream, line, &linestart_pos)))
{
- len += g_mime_stream_write(decrypted, buffer, ret);
- }
+ char * line_str = (char*) line->data;
+ char * pch = strchr (line_str, '\n');
+ if (pch != NULL) {
+ pch[1] = '\0';
+ line_len = pch - line_str;
+ }
- g_mime_stream_flush (decrypted);
- g_mime_stream_reset(decrypted);
+ if (gpg_is_signed_begin_line(line_str))
+ {
+ gpg_is_signed = true;
+ signed_msg_start = linestart_pos;
+ }
+ if (gpg_is_signed_end_line(line_str))
+ {
+ gboolean gpg_signed_end_found = true;
+ signed_msg_end = linestart_pos+line_len;
+ }
- gpgme_data_release(gpg_buf);
- gpgme_data_release(gpg_out_buf);
+ switch (type)
+ {
+ case ENC_PLAIN:
+ {
+ const StringView line_pstr (line_str, line_len);
- GPGSignersInfo si;
+ if (uu_is_beginning_line (line_pstr))
+ {
+ gulong mode = 0ul;
+ char * filename = NULL;
- if (!info.no_sigs)
- {
- if (info.v_res->signatures->fpr)
- gpgme_get_key (gpg_ctx, info.v_res->signatures->fpr, &key, 0);
- if (key)
- fill_signer_info(si, key);
- }
+ found=true;
+ // flush the current entry
+ if (cur != NULL) {
+ GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+ apply_source_and_maybe_filter (cur, s);
- signer_info = si;
- info.err = GPG_ERR_NO_ERROR;
+ if ( append_if_not_present (master, cur) )
+ append_if_not_present (appendme, cur);
+ cur = NULL;
+ }
- return decrypted;
- }
+ // start a new section (or, if filename matches, continue an existing one)
+ sub_begin = linestart_pos;
+ uu_parse_begin_line (line_pstr, &filename, &mode);
+ cur = find_filename_part (master, filename);
+ if (cur)
+ g_free (filename);
+ else
+ cur = new TempPart (type=ENC_UU, filename);
+ state.uu_temp = cur;
+ }
+ else if (yenc_is_beginning_line (line_str))
+ {
+ found = true;
+ // flush the current entry
+ if (cur != NULL) {
+ GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+ apply_source_and_maybe_filter (cur, s);
- void
- mime_part_set_content (GMimePart *part, const char *str)
- {
- GMimeDataWrapper *content;
- GMimeStream *stream;
+ if ( append_if_not_present (master, cur) )
+ append_if_not_present (appendme, cur);
+ cur = NULL;
+ }
+ sub_begin = linestart_pos;
- stream = g_mime_stream_mem_new_with_buffer (str, strlen (str));
- content = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT);
- g_object_unref (stream);
+ // start a new section (or, if filename matches, continue an existing one)
+ char * fname;
+ int line_len, attach_size, part;
+ yenc_parse_begin_line (line_str, &fname, &line_len, &attach_size, &part);
+ cur = find_filename_part (master, fname);
+ if (cur) {
+ g_free (fname);
+ g_mime_filter_yenc_set_state (GMIME_FILTER_YENC (cur->filter),
+ GMIME_YDECODE_STATE_INIT);
+ }
+ else
+ {
+ cur = new TempPart (type=ENC_YENC, fname);
+ cur->y_line_len = line_len;
+ cur->y_attach_size = attach_size;
+ cur->y_part = part;
+ yenc_looking_for_part_line = cur->y_part!=0;
+ }
+ }
+ else if (gpg_is_beginning_line (line_str))
+ {
- g_mime_part_set_content_object (part, content);
- g_object_unref (content);
- }
+ found = true;
+ cur = new TempPart (type = ENC_GPG);
+ gpg_looking_for_line = true;
+ sub_begin = linestart_pos;
+ }
+ else if (state.uu_temp != NULL && is_uu_line(line_str, line_len) )
+ {
+ // continue an incomplete uu decode
+ found = true;
+ // flush the current entry
+ if (cur != NULL) {
+ GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+ apply_source_and_maybe_filter (cur, s);
- GMimeMessage*
- message_add_signed_part (const std::string& uid, const std::string& body_str, GMimeMessage* body, GPGEncErr& fail)
- {
- if (uid.empty()) return 0;
+ if ( append_if_not_present (master, cur) )
+ append_if_not_present (appendme, cur);
+ cur = NULL;
+ }
+ sub_begin = linestart_pos;
+ cur = state.uu_temp;
+ ++cur->valid_lines;
+ type = ENC_UU;
+ }
+ else if (cur == NULL)
+ {
+ sub_begin = linestart_pos;
- GMimeMultipart *mp = g_mime_multipart_new_with_subtype ("mixed");
- g_mime_multipart_set_boundary(mp, NULL);
- g_mime_multipart_add(mp,g_mime_message_get_mime_part(body));
+ cur = new TempPart (type = ENC_PLAIN);
+ }
+ break;
+ }
+ case ENC_UU:
+ {
+ if (uu_is_ending_line(line_str))
+ {
+ GMimeStream * stream;
+ if (sub_begin < 0)
+ sub_begin = linestart_pos;
+ stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
+ apply_source_and_maybe_filter (cur, stream);
+ if( append_if_not_present (master, cur) )
+ append_if_not_present (appendme, cur);
- gpgme_data_t gpg_buf, gpg_out_buf;
- gpgme_key_t mykey(0), key;
+ cur = NULL;
+ type = ENC_PLAIN;
+ state.uu_temp = NULL;
+ }
+ else if (!is_uu_line(line_str, line_len))
+ {
+ /* hm, this isn't a uenc line, so ending the cat and setting sub_begin to -1 */
+ if (sub_begin >= 0)
+ {
+ GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+ apply_source_and_maybe_filter (cur, stream);
+ }
- StringView v(body_str);
- gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
- gpgme_data_new (&gpg_out_buf);
- // gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_BASE64);
- // gpgme_data_set_file_name(gpg_out_buf, "signature.asc");
+ sub_begin = -1;
+ }
+ else if (sub_begin == -1)
+ {
+ /* looks like they decided to start using uu lines again. */
+ ++cur->valid_lines;
+ sub_begin = linestart_pos;
+ }
+ else
+ {
+ ++cur->valid_lines;
+ }
+ break;
+ }
+ case ENC_YENC:
+ {
+ if (yenc_is_ending_line (line_str))
+ {
+ GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
+ apply_source_and_maybe_filter (cur, stream);
+ yenc_parse_end_line (line_str, &cur->y_size, NULL, &cur->y_pcrc, &cur->y_crc);
+ if( append_if_not_present (master, cur) )
+ append_if_not_present (appendme, cur);
- fail.err = gpgme_op_keylist_start (gpg_ctx, 0, 0);
- while (!fail.err)
- {
- fail.err = gpgme_op_keylist_next (gpg_ctx, &key);
- if (fail.err) break;
- if (strcmp(key->subkeys->keyid, uid.c_str()) == 0) { mykey = key; break; }
- }
- if (!mykey) { fail.err = GPG_ERR_NO_PUBKEY; return 0; }
+ cur = NULL;
+ type = ENC_PLAIN;
+ }
+ else if (yenc_looking_for_part_line && yenc_is_part_line(line_str))
+ {
+ yenc_parse_part_line (line_str, &cur->y_offset_begin, &cur->y_offset_end);
+ yenc_looking_for_part_line = FALSE;
+ ++cur->valid_lines;
+ }
+ else
+ {
+ ++cur->valid_lines;
+ }
+ break;
+ }
+#ifdef HAVE_GPGME
+ case ENC_GPG:
+ {
+ if (gpg_is_ending_line(line_str))
+ {
+ GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
+ GMimeStream * dec = gpg_decrypt_and_verify(state.signer_info, state.gpgerr, stream);
+ if (state.gpgerr.verify_ok)
+ state.gpg_verified = "valid";
+ else if (!state.gpgerr.verify_ok && !state.gpgerr.no_sigs)
+ state.gpg_verified = "invalid";
- gpgme_signers_clear(gpg_ctx);
- gpgme_signers_add(gpg_ctx, mykey);
+ gpg_looking_for_line = false;
+ if (state.gpgerr.dec_ok)
+ {
+ apply_source_and_maybe_filter (cur, dec);
+ if( append_if_not_present (master, cur) )
+ append_if_not_present (appendme, cur);
+ }
+ cur = NULL;
+ type = ENC_PLAIN;
+ sub_begin = -1;
+ }
+ else if (gpg_looking_for_line)
+ {
+ ++cur->valid_lines;
+ }
+ break;
+ }
+#endif
+ }
+ }
- fail.err = gpgme_op_sign (gpg_ctx, gpg_buf, gpg_out_buf, GPGME_SIG_MODE_DETACH);
- fail.sign_res = gpgme_op_sign_result (gpg_ctx);
- if (fail.err) return 0;
+ /* close last entry */
+ if (cur != NULL)
+ {
+ if (sub_begin >= 0)
+ {
+ GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
+ apply_source_and_maybe_filter (cur, stream);
+ }
- gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
- ssize_t ret;
- std::stringstream ret_str;
- char buffer[4096]={0};
+ /* just in case someone started with "yenc" or "begin 644 asf" in a text message to fuck with unwary newsreaders */
+ if (cur->valid_lines < 10u)
+ cur->type = ENC_PLAIN;
- while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
- {
- ret_str << buffer;
+ if( append_if_not_present (master, cur) )
+ append_if_not_present (appendme, cur);
+ cur = NULL;
+ type = ENC_PLAIN;
}
- GMimePart *sig = g_mime_part_new_with_type("multipart", "signed");
- g_mime_object_set_content_type_parameter(GMIME_OBJECT(sig),"protocol","pgp-signature");
- mime_part_set_content (sig, ret_str.str().c_str());
+ g_byte_array_free (line, TRUE);
+ return found;
+ }
- gpgme_data_release(gpg_buf);
- gpgme_data_release(gpg_out_buf);
+}
- g_mime_multipart_add (GMIME_MULTIPART (mp), GMIME_OBJECT (sig));
+void
+mime :: guess_part_type_from_filename (const char * filename,
+ const char ** setme_type,
+ const char ** setme_subtype)
+{
+ static const struct {
+ const char * suffix;
+ const char * type;
+ const char * subtype;
+ } suffixes[] = {
+ { ".avi", "video", "vnd.msvideo" },
+ { ".dtd", "text", "xml-dtd" },
+ { ".flac", "audio", "ogg" },
+ { ".gif", "image", "gif" },
+ { ".htm", "text", "html" },
+ { ".html", "text", "html" },
+ { ".jpg", "image", "jpeg" },
+ { ".jpeg", "image", "jpeg" },
+ { ".md5", "image", "tiff" },
+ { ".mp3", "audio", "mpeg" },
+ { ".mpeg", "video", "mpeg" },
+ { ".mpg", "video", "mpeg" },
+ { ".mov", "video", "quicktime" },
+ { ".nfo", "text", "plain" },
+ { ".oga", "audio", "x-vorbis" },
+ { ".ogg", "audio", "ogg" },
+ { ".ogv", "video", "ogg" },
+ { ".ogx", "application", "ogg" },
+ { ".png", "image", "png" },
+ { ".qt", "video", "quicktime" },
+ { ".rar", "application", "x-rar" },
+ { ".rv", "video", "vnd.rn-realvideo" },
+ { ".scr", "application", "octet-stream" },
+ { ".spx", "audio", "ogg" },
+ { ".svg", "image", "svg+xml" },
+ { ".tar", "application", "x-tar" },
+ { ".tbz2", "application", "x-tar" },
+ { ".tgz", "application", "x-tar" },
+ { ".tiff", "image", "tiff" },
+ { ".tif", "image", "tiff" },
+ { ".txt", "text", "plain" },
+ { ".uu", "text", "x-uuencode" },
+ { ".uue", "text", "x-uuencode" },
+ { ".xml", "text", "xml" },
+ { ".xsl", "text", "xml" },
+ { ".zip", "application", "zip" }
+ };
+ static const int suffix_qty = G_N_ELEMENTS (suffixes);
+ const char * suffix;
- g_mime_message_set_mime_part(body,GMIME_OBJECT(mp));
- g_object_unref(mp);
+ /* zero out the return values */
+ pan_return_if_fail (setme_type!=NULL);
+ pan_return_if_fail (setme_subtype!=NULL);
+ *setme_type = *setme_subtype = NULL;
- fail.err = GPG_ERR_NO_ERROR;
+ /* sanity clause */
+ pan_return_if_fail (is_nonempty_string(filename));
- return body;
- }
+ suffix = strrchr (filename, '.');
+ if (suffix != NULL) {
+ int i;
+ for (i=0; i<suffix_qty; ++i) {
+ if (!g_ascii_strcasecmp (suffix, suffixes[i].suffix)) {
+ *setme_type = suffixes[i].type;
+ *setme_subtype = suffixes[i].subtype;
+ break;
+ }
+ }
+ }
- std::string
- gpg_encrypt(const std::string& uid, const std::string& body, GPGEncErr& fail)
- {
- fail.err = GPG_ERR_GENERAL;
+ if (*setme_type == NULL) {
+ *setme_type = "application";
+ *setme_subtype = "octet-stream";
+ }
+}
- if (uid.empty()) return "";
+namespace
+{
+ void
+ ptr_array_insert (GPtrArray *array, guint index, gpointer object)
+ {
+ unsigned char *dest, *src;
+ guint n;
- gpgme_data_t gpg_buf, gpg_out_buf;
- gpgme_key_t mykey(0), key;
+ g_ptr_array_set_size (array, array->len + 1);
- StringView v(body);
- gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
- gpgme_data_new (&gpg_out_buf);
- gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_BASE64);
+ if (index != array->len) {
+ /* need to move items down */
+ dest = ((unsigned char *) array->pdata) + (sizeof (void *) * (index + 1));
+ src = ((unsigned char *) array->pdata) + (sizeof (void *) * index);
+ n = array->len - index - 1;
- /* find key to uid */
- fail.err = gpgme_op_keylist_start (gpg_ctx, 0, 0);
- while (!fail.err)
- {
- fail.err = gpgme_op_keylist_next (gpg_ctx, &key);
- if (fail.err) break;
- if (strcmp(key->subkeys->keyid, uid.c_str()) == 0) { mykey = key; break; }
+ g_memmove (dest, src, (sizeof (void *) * n));
}
- if (!mykey) { fail.err = GPG_ERR_NO_PUBKEY; return std::string(""); }
- gpgme_key_t enc_keys[] = { mykey, NULL};
+ array->pdata[index] = object;
+ }
- fail.err = gpgme_op_encrypt (gpg_ctx, enc_keys, GPGME_ENCRYPT_PREPARE, gpg_buf, gpg_out_buf);
- fail.enc_res = gpgme_op_encrypt_result (gpg_ctx);
- if (fail.err) return std::string("");
+ void handle_uu_and_yenc_in_text_plain_cb (GMimeObject *parent, GMimeObject *part, gpointer data)
+ {
- gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
- ssize_t ret;
- std::stringstream ret_str;
- char buffer[4096]={0};
+ if (!part)
+ return;
- while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
- {
- ret_str << buffer;
- }
-
- gpgme_data_release(gpg_buf);
- gpgme_data_release(gpg_out_buf);
-
- fail.err = GPG_ERR_NO_ERROR;
- return ret_str.str();
- }
-
- std::string
- gpg_encrypt_and_sign(const std::string& uid, const std::string& body, GPGEncErr& fail)
- {
- fail.err = GPG_ERR_GENERAL;
+ // we assume that inlined yenc and uu are only in text/plain blocks
+ GMimeContentType * content_type = g_mime_object_get_content_type (part);
+ if (!g_mime_content_type_is_type (content_type, "text", "plain"))
+ return;
- if (uid.empty()) return "";
+ // get this part's content
+ GMimeDataWrapper * content = g_mime_part_get_content_object (GMIME_PART (part));
+ if (!content)
+ return;
- gpgme_data_t gpg_buf, gpg_out_buf;
- gpgme_key_t mykey(0), key;
+ // wrap a buffer stream around it for faster reading -- it could be a file stream
+ GMimeStream * stream = g_mime_data_wrapper_get_stream (content);
+ g_mime_stream_reset (stream);
+ GMimeStream * istream = g_mime_stream_buffer_new (stream, GMIME_STREAM_BUFFER_BLOCK_READ);
- StringView v(body);
- gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
- gpgme_data_new (&gpg_out_buf);
- gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_BASE64);
+ // break it into separate parts for text, uu, and yenc pieces.
+ sep_state &state(*(sep_state*)data);
+ temp_parts_t &parts(state.current_list);
+ bool split = separate_encoded_parts (istream, state);
+ g_mime_stream_reset (istream);
- /* find key to uid */
- fail.err = gpgme_op_keylist_start (gpg_ctx, 0, 0);
- while (!fail.err)
+ // split?
+ if(split)
{
- fail.err = gpgme_op_keylist_next (gpg_ctx, &key);
- if (fail.err) break;
- if (strcmp(key->subkeys->keyid, uid.c_str()) == 0) { mykey = key; break; }
- }
- if (!mykey) { fail.err = GPG_ERR_NO_PUBKEY; return std::string(""); }
-
- gpgme_signers_clear(gpg_ctx);
- gpgme_signers_add(gpg_ctx, mykey);
-
- gpgme_key_t enc_keys[] = { mykey, NULL};
+ //this part was completely folded into a previous part
+ //so delete it
+ if(parts.size()==0) {
+ GMimeMultipart *mp = GMIME_MULTIPART (parent);
+ int index = g_mime_multipart_index_of (mp,part);
+ if(index>0)
+ g_mime_multipart_remove_at (mp,index);
+ g_object_unref(part);
+ }
+ else
+ {
+ GMimeMultipart * multipart = g_mime_multipart_new_with_subtype ("mixed");
- fail.err = gpgme_op_encrypt_sign (gpg_ctx, enc_keys, GPGME_ENCRYPT_PREPARE, gpg_buf, gpg_out_buf);
- fail.enc_res = gpgme_op_encrypt_result (gpg_ctx);
- fail.sign_res= gpgme_op_sign_result (gpg_ctx);
- if (fail.err) return std::string("");
+ const TempPart *tmp_part;
+ const char *filename;
+ GMimePart *subpart;
+ GMimeStream *subpart_stream;
+ foreach (temp_parts_t, parts, it)
+ {
+ // reset these for each part
+ const char * type = "text";
+ const char * subtype = "plain";
- gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
- ssize_t ret;
- std::stringstream ret_str;
- char buffer[4096]={0};
+ tmp_part = *it;
+ filename = tmp_part->filename;
- while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
- {
- ret_str << buffer;
- }
+ if (filename && *filename)
+ mime::guess_part_type_from_filename (filename, &type, &subtype);
- gpgme_data_release(gpg_buf);
- gpgme_data_release(gpg_out_buf);
+ subpart = g_mime_part_new_with_type (type, subtype);
+ if (filename && *filename)
+ g_mime_part_set_filename (subpart, filename);
- fail.err = GPG_ERR_NO_ERROR;
- return ret_str.str();
- }
-#endif
+ subpart_stream = tmp_part->stream;
+ content = g_mime_data_wrapper_new_with_stream (subpart_stream, GMIME_CONTENT_ENCODING_DEFAULT);
+ g_mime_part_set_content_object (subpart, content);
+ g_mime_multipart_add (GMIME_MULTIPART (multipart), GMIME_OBJECT (subpart));
- void apply_source_and_maybe_filter (TempPart * part, GMimeStream * s)
- {
+ g_object_unref (content);
+ g_object_unref (subpart);
+ }
- if (!part->stream) {
- part->stream = g_mime_stream_mem_new ();
- if (part->type != ENC_PLAIN && part->type != ENC_GPG) {
- part->filter_stream = g_mime_stream_filter_new (part->stream);
- switch (part->type)
+ // replace the old part with the new multipart
+ GMimeObject *newpart = GMIME_OBJECT(multipart);
+ if(parts.size()==1)
{
- case ENC_UU:
- part->filter = g_mime_filter_basic_new (GMIME_CONTENT_ENCODING_UUENCODE, FALSE);
- break;
-
- case ENC_YENC:
- part->filter = g_mime_filter_yenc_new (FALSE);
- break;
+ //only one part so no need for multipart
+ newpart = g_mime_multipart_remove_at(multipart,0);
+ g_object_unref(multipart);
}
+ if(GMIME_IS_MULTIPART(parent))
+ {
+ GMimeMultipart *mp = GMIME_MULTIPART (parent);
+ int index = g_mime_multipart_index_of (mp, part);
+ g_mime_multipart_remove_at (mp, index);
+ g_object_unref (part);
- g_mime_stream_filter_add (GMIME_STREAM_FILTER(part->filter_stream),
- part->filter);
+ //workaround gmime insert bug
+ //g_mime_multipart_insert (mp,index,newpart);
+ {
+ ptr_array_insert(mp->children, index, newpart);
+ g_object_ref(newpart);
+ }
+ }
+ else if(GMIME_IS_MESSAGE(parent))
+ {
+ g_mime_message_set_mime_part((GMimeMessage*)parent, newpart);
+ }
+ g_object_unref(newpart);
}
}
- g_mime_stream_write_to_stream (s, (part->type == ENC_PLAIN || part->type == ENC_GPG) ?
- part->stream : part->filter_stream);
- g_object_unref (s);
+ parts.clear();
+ g_object_unref (istream);
}
+}
- struct sep_state
- {
- temp_parts_t master_list;
- temp_parts_t current_list;
- TempPart *uu_temp;
- std::string gpg_verified;
-#ifdef HAVE_GPGME
- GPGDecErr gpgerr;
- GPGSignersInfo signer_info;
-#endif
- sep_state():uu_temp(NULL), gpg_verified("") {};
+namespace{
+ struct temp_p {
+ GMimeObject *parent,*part;
+
+ temp_p(GMimeObject *p, GMimeObject *par):parent(p),part(par) {};
};
+ typedef std::vector<temp_p> temp_p_t;
- bool
- separate_encoded_parts (GMimeStream * istream, sep_state &state)
+ void find_text_cb(GMimeObject *parent, GMimeObject *part, gpointer data)
{
- temp_parts_t& master(state.master_list);
- temp_parts_t& appendme(state.current_list);
- TempPart * cur = NULL;
- EncType type = ENC_PLAIN;
- GByteArray * line;
- gboolean yenc_looking_for_part_line = FALSE;
- gboolean gpg_looking_for_line = FALSE;
- gboolean gpg_is_signed = FALSE;
- gboolean gpg_signed_end_found = FALSE;
- gint64 linestart_pos = 0;
- gint64 sub_begin = 0;
- gint64 signed_msg_start = 0;
- gint64 signed_msg_end = 0;
- guint line_len;
- bool found = false;
-
- /* sanity clause */
- pan_return_val_if_fail (istream!=NULL,false);
-
- sub_begin = 0;
- line = g_byte_array_sized_new (4096);
-
- while ((line_len = stream_readln (istream, line, &linestart_pos)))
- {
- char * line_str = (char*) line->data;
- char * pch = strchr (line_str, '\n');
- if (pch != NULL) {
- pch[1] = '\0';
- line_len = pch - line_str;
- }
-
- if (gpg_is_signed_begin_line(line_str))
- {
- gpg_is_signed = true;
- signed_msg_start = linestart_pos;
- }
- if (gpg_is_signed_end_line(line_str))
- {
- gboolean gpg_signed_end_found = true;
- signed_msg_end = linestart_pos+line_len;
- }
-
- switch (type)
- {
- case ENC_PLAIN:
- {
- const StringView line_pstr (line_str, line_len);
-
- if (uu_is_beginning_line (line_pstr))
- {
- gulong mode = 0ul;
- char * filename = NULL;
-
- found=true;
- // flush the current entry
- if (cur != NULL) {
- GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
- apply_source_and_maybe_filter (cur, s);
-
- if ( append_if_not_present (master, cur) )
- append_if_not_present (appendme, cur);
- cur = NULL;
- }
+ if(!GMIME_IS_PART(part))
+ return;
- // start a new section (or, if filename matches, continue an existing one)
- sub_begin = linestart_pos;
- uu_parse_begin_line (line_pstr, &filename, &mode);
- cur = find_filename_part (master, filename);
- if (cur)
- g_free (filename);
- else
- cur = new TempPart (type=ENC_UU, filename);
- state.uu_temp = cur;
- }
- else if (yenc_is_beginning_line (line_str))
- {
- found = true;
- // flush the current entry
- if (cur != NULL) {
- GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
- apply_source_and_maybe_filter (cur, s);
+ temp_p_t *v( (temp_p_t *) data);
+ // we assume that inlined yenc and uu are only in text/plain blocks
+ GMimeContentType * content_type = g_mime_object_get_content_type (part);
+ if (!g_mime_content_type_is_type (content_type, "text", "plain"))
+ return;
+ v->push_back(temp_p(parent,part) );
+ }
- if ( append_if_not_present (master, cur) )
- append_if_not_present (appendme, cur);
- cur = NULL;
- }
- sub_begin = linestart_pos;
+ void find_gpg_parts_cb(GMimeObject *parent, GMimeObject *part, gpointer data)
+ {
+ if(!GMIME_IS_PART(part))
+ return;
- // start a new section (or, if filename matches, continue an existing one)
- char * fname;
- int line_len, attach_size, part;
- yenc_parse_begin_line (line_str, &fname, &line_len, &attach_size, &part);
- cur = find_filename_part (master, fname);
- if (cur) {
- g_free (fname);
- g_mime_filter_yenc_set_state (GMIME_FILTER_YENC (cur->filter),
- GMIME_YDECODE_STATE_INIT);
- }
- else
- {
- cur = new TempPart (type=ENC_YENC, fname);
- cur->y_line_len = line_len;
- cur->y_attach_size = attach_size;
- cur->y_part = part;
- yenc_looking_for_part_line = cur->y_part!=0;
- }
- }
- else if (gpg_is_beginning_line (line_str))
- {
+ temp_p_t *v( (temp_p_t *) data);
- found = true;
- cur = new TempPart (type = ENC_GPG);
- gpg_looking_for_line = true;
- sub_begin = linestart_pos;
- }
- else if (state.uu_temp != NULL && is_uu_line(line_str, line_len) )
- {
- // continue an incomplete uu decode
- found = true;
- // flush the current entry
- if (cur != NULL) {
- GMimeStream * s = g_mime_stream_substream (istream, sub_begin, linestart_pos);
- apply_source_and_maybe_filter (cur, s);
+ GMimeContentType * content_type = g_mime_object_get_content_type (part);
+ if (!(g_mime_content_type_is_type (content_type, "multipart", "encrypted") ||
+ g_mime_content_type_is_type (content_type, "text", "plain")))
+ return;
+ v->push_back(temp_p(parent,part) );
+ }
- if ( append_if_not_present (master, cur) )
- append_if_not_present (appendme, cur);
- cur = NULL;
- }
- sub_begin = linestart_pos;
- cur = state.uu_temp;
- ++cur->valid_lines;
- type = ENC_UU;
- }
- else if (cur == NULL)
- {
- sub_begin = linestart_pos;
+}
- cur = new TempPart (type = ENC_PLAIN);
- }
- break;
- }
- case ENC_UU:
- {
- if (uu_is_ending_line(line_str))
- {
- GMimeStream * stream;
- if (sub_begin < 0)
- sub_begin = linestart_pos;
- stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
- apply_source_and_maybe_filter (cur, stream);
- if( append_if_not_present (master, cur) )
- append_if_not_present (appendme, cur);
+/***
+****
+***/
- cur = NULL;
- type = ENC_PLAIN;
- state.uu_temp = NULL;
- }
- else if (!is_uu_line(line_str, line_len))
- {
- /* hm, this isn't a uenc line, so ending the cat and setting sub_begin to -1 */
- if (sub_begin >= 0)
- {
- GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
- apply_source_and_maybe_filter (cur, stream);
- }
+#ifdef HAVE_GPGME
+GMimeMessage*
+mime :: construct_message (GMimeStream ** istreams,
+ int qty,
+ GPGSignersInfo & signer_info,
+ GPGDecErr & gpgerr)
+#else
+GMimeMessage*
+mime :: construct_message (GMimeStream ** istreams,
+ int qty)
+#endif
+{
+ const char * message_id = "Foo <bar mum>";
+ GMimeMessage * retval = 0;
- sub_begin = -1;
- }
- else if (sub_begin == -1)
- {
- /* looks like they decided to start using uu lines again. */
- ++cur->valid_lines;
- sub_begin = linestart_pos;
- }
- else
- {
- ++cur->valid_lines;
- }
- break;
- }
- case ENC_YENC:
- {
- if (yenc_is_ending_line (line_str))
- {
- GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
- apply_source_and_maybe_filter (cur, stream);
- yenc_parse_end_line (line_str, &cur->y_size, NULL, &cur->y_pcrc, &cur->y_crc);
- if( append_if_not_present (master, cur) )
- append_if_not_present (appendme, cur);
+ // sanity clause
+ pan_return_val_if_fail (is_nonempty_string(message_id), NULL);
+ pan_return_val_if_fail (istreams!=NULL, NULL);
+ pan_return_val_if_fail (qty>=1, NULL);
+ for (int i=0; i<qty; ++i)
+ pan_return_val_if_fail (GMIME_IS_STREAM(istreams[i]), NULL);
- cur = NULL;
- type = ENC_PLAIN;
- }
- else if (yenc_looking_for_part_line && yenc_is_part_line(line_str))
- {
- yenc_parse_part_line (line_str, &cur->y_offset_begin, &cur->y_offset_end);
- yenc_looking_for_part_line = FALSE;
- ++cur->valid_lines;
- }
- else
- {
- ++cur->valid_lines;
- }
- break;
- }
-#ifdef HAVE_GPGME
- case ENC_GPG:
- {
- if (gpg_is_ending_line(line_str))
- {
- GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos+line_len);
- GMimeStream * dec = gpg_decrypt_and_verify(state.signer_info, state.gpgerr, stream);
- if (state.gpgerr.verify_ok)
- state.gpg_verified = "valid";
- else if (!state.gpgerr.verify_ok && !state.gpgerr.no_sigs)
- state.gpg_verified = "invalid";
+ // build the GMimeMessages
+ GMimeParser * parser = g_mime_parser_new ();
+ GMimeMessage ** messages = g_new (GMimeMessage*, qty);
+ for (int i=0; i<qty; ++i) {
+ g_mime_parser_init_with_stream (parser, istreams[i]);
+ messages[i] = g_mime_parser_construct_message (parser);
+ }
+ g_object_unref (parser);
- gpg_looking_for_line = false;
- if (state.gpgerr.dec_ok)
- {
- apply_source_and_maybe_filter (cur, dec);
- if( append_if_not_present (master, cur) )
- append_if_not_present (appendme, cur);
- }
- cur = NULL;
- type = ENC_PLAIN;
- sub_begin = -1;
- }
- else if (gpg_looking_for_line)
- {
- ++cur->valid_lines;
- }
- break;
- }
-#endif
- }
- }
+ if (qty > 1) // fold multiparts together
+ {
+ GMimeMultipart * mp = g_mime_multipart_new ();
- /* close last entry */
- if (cur != NULL)
+ for (int i=0; i<qty; ++i)
{
- if (sub_begin >= 0)
- {
- GMimeStream * stream = g_mime_stream_substream (istream, sub_begin, linestart_pos);
- apply_source_and_maybe_filter (cur, stream);
- }
+ g_mime_multipart_add(mp,g_mime_message_get_mime_part(messages[i]) );
+ }
- /* just in case someone started with "yenc" or "begin 644 asf" in a text message to fuck with unwary newsreaders */
- if (cur->valid_lines < 10u)
- cur->type = ENC_PLAIN;
+ g_mime_message_set_mime_part(messages[0],GMIME_OBJECT(mp));
+ g_object_unref(mp);
+ }
- if( append_if_not_present (master, cur) )
- append_if_not_present (appendme, cur);
- cur = NULL;
- type = ENC_PLAIN;
- }
+ retval = messages[0];
+ for (int i=1; i<qty; ++i)
+ g_object_unref (messages[i]);
- g_byte_array_free (line, TRUE);
- return found;
+ // pick out yenc and uuenc data in text/plain messages
+ temp_p_t partslist;
+ sep_state state;
+ if (retval != NULL)
+ g_mime_message_foreach(retval, find_text_cb, &partslist);
+ foreach(temp_p_t, partslist, it)
+ {
+ temp_p &data(*it);
+ handle_uu_and_yenc_in_text_plain_cb(data.parent, data.part, &state);
+ /* set gpg signature verify success/fail flag */
+#ifdef HAVE_GPGME
+ g_mime_object_set_header(GMIME_OBJECT(data.parent), "X-GPG-Signed", state.gpg_verified.c_str());
+ gpgerr = state.gpgerr;
+ signer_info = state.signer_info;
+#endif
+ }
+
+ // cleanup
+ foreach (temp_parts_t, state.master_list, it)
+ {
+ delete *it;
}
+ g_free (messages);
+ return retval;
}
-void
-mime :: guess_part_type_from_filename (const char * filename,
- const char ** setme_type,
- const char ** setme_subtype)
-{
- static const struct {
- const char * suffix;
- const char * type;
- const char * subtype;
- } suffixes[] = {
- { ".avi", "video", "vnd.msvideo" },
- { ".dtd", "text", "xml-dtd" },
- { ".flac", "audio", "ogg" },
- { ".gif", "image", "gif" },
- { ".htm", "text", "html" },
- { ".html", "text", "html" },
- { ".jpg", "image", "jpeg" },
- { ".jpeg", "image", "jpeg" },
- { ".md5", "image", "tiff" },
- { ".mp3", "audio", "mpeg" },
- { ".mpeg", "video", "mpeg" },
- { ".mpg", "video", "mpeg" },
- { ".mov", "video", "quicktime" },
- { ".nfo", "text", "plain" },
- { ".oga", "audio", "x-vorbis" },
- { ".ogg", "audio", "ogg" },
- { ".ogv", "video", "ogg" },
- { ".ogx", "application", "ogg" },
- { ".png", "image", "png" },
- { ".qt", "video", "quicktime" },
- { ".rar", "application", "x-rar" },
- { ".rv", "video", "vnd.rn-realvideo" },
- { ".scr", "application", "octet-stream" },
- { ".spx", "audio", "ogg" },
- { ".svg", "image", "svg+xml" },
- { ".tar", "application", "x-tar" },
- { ".tbz2", "application", "x-tar" },
- { ".tgz", "application", "x-tar" },
- { ".tiff", "image", "tiff" },
- { ".tif", "image", "tiff" },
- { ".txt", "text", "plain" },
- { ".uu", "text", "x-uuencode" },
- { ".uue", "text", "x-uuencode" },
- { ".xml", "text", "xml" },
- { ".xsl", "text", "xml" },
- { ".zip", "application", "zip" }
- };
- static const int suffix_qty = G_N_ELEMENTS (suffixes);
- const char * suffix;
+/***
+****
+***/
- /* zero out the return values */
- pan_return_if_fail (setme_type!=NULL);
- pan_return_if_fail (setme_subtype!=NULL);
- *setme_type = *setme_subtype = NULL;
+/**
+ * Retrieve the charset from a mime message
+ */
- /* sanity clause */
- pan_return_if_fail (is_nonempty_string(filename));
+#if 0 // unused?
+static void
+get_charset_partfunc (GMimeObject * obj, gpointer charset_gpointer)
+{
+ GMimePart * part;
+ const GMimeContentType * type;
+ const char ** charset;
- suffix = strrchr (filename, '.');
- if (suffix != NULL) {
- int i;
- for (i=0; i<suffix_qty; ++i) {
- if (!g_ascii_strcasecmp (suffix, suffixes[i].suffix)) {
- *setme_type = suffixes[i].type;
- *setme_subtype = suffixes[i].subtype;
- break;
- }
- }
- }
+ if (!GMIME_IS_PART (obj))
+ return;
- if (*setme_type == NULL) {
- *setme_type = "application";
- *setme_subtype = "octet-stream";
- }
+ part = GMIME_PART (obj);
+ type = g_mime_object_get_content_type (GMIME_OBJECT (part));
+ charset = (const char **) charset_gpointer;
+ if (g_mime_content_type_is_type (type, "text", "*"))
+ *charset = g_mime_object_get_content_type_parameter (GMIME_OBJECT (part), "charset");
}
-namespace
+const char *
+mime :: get_charset (GMimeMessage * message)
{
- void
- ptr_array_insert (GPtrArray *array, guint index, gpointer object)
- {
- unsigned char *dest, *src;
- guint n;
+ const char * retval = NULL;
+ pan_return_val_if_fail (message!=NULL, NULL);
- g_ptr_array_set_size (array, array->len + 1);
+ g_mime_message_foreach_part (message, get_charset_partfunc, &retval);
- if (index != array->len) {
- /* need to move items down */
- dest = ((unsigned char *) array->pdata) + (sizeof (void *) * (index + 1));
- src = ((unsigned char *) array->pdata) + (sizeof (void *) * index);
- n = array->len - index - 1;
+ return retval;
+}
+#endif
- g_memmove (dest, src, (sizeof (void *) * n));
- }
+/**
+***
+**/
- array->pdata[index] = object;
- }
+namespace
+{
+ enum StripFlags
+ {
+ STRIP_CASE = (1<<0),
+ STRIP_MULTIPART_NUMERATOR = (1<<1),
+ STRIP_MULTIPART = (1<<2)
+ };
- void handle_uu_and_yenc_in_text_plain_cb (GMimeObject *parent, GMimeObject *part, gpointer data)
- {
+ /**
+ * Normalizing a subject header involves:
+ *
+ * 1. tearing out the numerator from multipart indicators
+ * (i.e., remove "21" from (21/42))
+ * for threading.
+ * 2. convert it to lowercase so that, when sorting, we can
+ * have case-insensitive sorting without having to use
+ * a slower case-insensitive compare function.
+ * 3. strip out all the leading noise that breaks sorting.
+ *
+ * When we're threading articles, it's much faster to
+ * normalize the * subjects at the outset instead of
+ * normalizing them for each comparison.
+ */
+ void
+ normalize_subject (const StringView & subject,
+ StripFlags strip,
+ std::string & setme)
+ {
+#if 0
+ static bool _keep_chars[UCHAR_MAX+1];
+ static bool _inited (false);
+ if (!_inited) {
+ _inited = true;
+ for (int i=0; i<=UCHAR_MAX; ++i) {
+ const unsigned char uch ((unsigned char)i);
+ _keep_chars[i] = isalnum(uch) || isdigit(uch) || isspace(uch);
+ }
+ }
+#endif
+ setme.reserve (subject.len + 1);
+ const unsigned char * in ((const unsigned char*) subject.begin());
+ const unsigned char * end ((const unsigned char*) subject.end());
+ const bool strip_case (strip & STRIP_CASE);
+ const bool strip_numerator (strip & STRIP_MULTIPART_NUMERATOR);
+ const bool strip_multipart (strip & STRIP_MULTIPART);
- if (!part)
- return;
+ // skip the leading noise
+ while (in!=end && isspace(*in))
+ ++in;
- // we assume that inlined yenc and uu are only in text/plain blocks
- GMimeContentType * content_type = g_mime_object_get_content_type (part);
- if (!g_mime_content_type_is_type (content_type, "text", "plain"))
- return;
+ while (in!=end)
+ {
+ // strip numerator out of multipart information
+ if ((strip_multipart||strip_numerator)
+ && (*in=='('||*in=='[')
+ && isdigit(in[1]))
+ {
+ const unsigned char * start (++in);
- // get this part's content
- GMimeDataWrapper * content = g_mime_part_get_content_object (GMIME_PART (part));
- if (!content)
- return;
+ if (strip_multipart || strip_numerator)
+ {
+ while (in!=end && isdigit(*in))
+ ++in;
- // wrap a buffer stream around it for faster reading -- it could be a file stream
- GMimeStream * stream = g_mime_data_wrapper_get_stream (content);
- g_mime_stream_reset (stream);
- GMimeStream * istream = g_mime_stream_buffer_new (stream, GMIME_STREAM_BUFFER_BLOCK_READ);
+ if (*in!='/' && *in!='|') // oops, not multipart information
+ in = start;
- // break it into separate parts for text, uu, and yenc pieces.
- sep_state &state(*(sep_state*)data);
- temp_parts_t &parts(state.current_list);
- bool split = separate_encoded_parts (istream, state);
- g_mime_stream_reset (istream);
+ else if (strip_multipart)
+ {
+ for (++in; in!=end && *in!=']' && *in!=')'; ++in)
+ {
+ if (isalpha(*in)) { // oops, not multipart information
+ in = ++start;
+ break;
+ }
+ }
- // split?
- if(split)
- {
- //this part was completely folded into a previous part
- //so delete it
- if(parts.size()==0) {
- GMimeMultipart *mp = GMIME_MULTIPART (parent);
- int index = g_mime_multipart_index_of (mp,part);
- if(index>0)
- g_mime_multipart_remove_at (mp,index);
- g_object_unref(part);
+ if (in!=end && (*in==']' || *in==')'))
+ ++in;
+ }
+ }
+ continue;
+ }
+
+#if 0
+ // strip out junk that breaks sorting
+ if (_keep_chars[*in])
+#endif
+ setme += (char) (strip_case ? tolower(*in) : *in);
+
+ ++in;
}
- else
- {
- GMimeMultipart * multipart = g_mime_multipart_new_with_subtype ("mixed");
+ }
+}
- const TempPart *tmp_part;
- const char *filename;
- GMimePart *subpart;
- GMimeStream *subpart_stream;
- foreach (temp_parts_t, parts, it)
- {
- // reset these for each part
- const char * type = "text";
- const char * subtype = "plain";
+void
+mime :: remove_multipart_from_subject (const StringView & subject,
+ std::string & setme)
+{
+ normalize_subject (subject, STRIP_MULTIPART, setme);
+}
- tmp_part = *it;
- filename = tmp_part->filename;
+void
+mime :: remove_multipart_part_from_subject (const StringView & subject,
+ std::string & setme)
+{
+ normalize_subject (subject, STRIP_MULTIPART_NUMERATOR, setme);
+}
- if (filename && *filename)
- mime::guess_part_type_from_filename (filename, &type, &subtype);
+namespace
+{
+ GMimeObject *
+ handle_multipart_mixed (GMimeMultipart *multipart, gboolean *is_html);
- subpart = g_mime_part_new_with_type (type, subtype);
- if (filename && *filename)
- g_mime_part_set_filename (subpart, filename);
+ GMimeObject *
+ handle_multipart_alternative (GMimeMultipart *multipart, gboolean *is_html)
+ {
+ GMimeObject *mime_part, *text_part = NULL;
+ GMimeContentType *type;
+ int count = g_mime_multipart_get_count (multipart);
- subpart_stream = tmp_part->stream;
- content = g_mime_data_wrapper_new_with_stream (subpart_stream, GMIME_CONTENT_ENCODING_DEFAULT);
- g_mime_part_set_content_object (subpart, content);
- g_mime_multipart_add (GMIME_MULTIPART (multipart), GMIME_OBJECT (subpart));
+ for (int i = 0; i < count; ++i) {
+ mime_part = g_mime_multipart_get_part (multipart, i);
- g_object_unref (content);
- g_object_unref (subpart);
+ type = g_mime_object_get_content_type (mime_part);
+ if (g_mime_content_type_is_type (type, "text", "*")) {
+ if (!text_part || !g_ascii_strcasecmp (type->subtype, "plain")) {
+ *is_html = !g_ascii_strcasecmp (type->subtype, "html");
+ text_part = mime_part;
}
+ }
+ }
- // replace the old part with the new multipart
- GMimeObject *newpart = GMIME_OBJECT(multipart);
- if(parts.size()==1)
- {
- //only one part so no need for multipart
- newpart = g_mime_multipart_remove_at(multipart,0);
- g_object_unref(multipart);
- }
- if(GMIME_IS_MULTIPART(parent))
- {
- GMimeMultipart *mp = GMIME_MULTIPART (parent);
- int index = g_mime_multipart_index_of (mp, part);
- g_mime_multipart_remove_at (mp, index);
- g_object_unref (part);
+ return text_part;
+ }
- //workaround gmime insert bug
- //g_mime_multipart_insert (mp,index,newpart);
- {
- ptr_array_insert(mp->children, index, newpart);
- g_object_ref(newpart);
- }
+ GMimeObject *
+ handle_multipart_mixed (GMimeMultipart *multipart, gboolean *is_html)
+ {
+ GMimeObject *mime_part, *text_part = NULL;
+ GMimeContentType *type, *first_type = NULL;
+ int count = g_mime_multipart_get_count (multipart);
+
+ for (int i = 0; i < count; ++i) {
+ mime_part = g_mime_multipart_get_part (multipart, i);
+
+ type = g_mime_object_get_content_type (mime_part);
+ if (GMIME_IS_MULTIPART (mime_part)) {
+ multipart = GMIME_MULTIPART (mime_part);
+ if (g_mime_content_type_is_type (type, "multipart", "alternative")) {
+ mime_part = handle_multipart_alternative (multipart, is_html);
+ if (mime_part)
+ return mime_part;
+ } else {
+ mime_part = handle_multipart_mixed (multipart, is_html);
+ if (mime_part && !text_part)
+ text_part = mime_part;
}
- else if(GMIME_IS_MESSAGE(parent))
- {
- g_mime_message_set_mime_part((GMimeMessage*)parent, newpart);
+ } else if (g_mime_content_type_is_type (type, "text", "*")) {
+ if (!g_ascii_strcasecmp (type->subtype, "plain")) {
+ /* we got what we came for */
+ *is_html = !g_ascii_strcasecmp (type->subtype, "html");
+ return mime_part;
+ }
+
+ /* if we haven't yet found a text part or if it is a type we can
+ * understand and it is the first of that type, save it */
+ if (!text_part || (!g_ascii_strcasecmp (type->subtype, "plain") && (first_type &&
+ g_ascii_strcasecmp (type->subtype, first_type->subtype) != 0))) {
+ *is_html = !g_ascii_strcasecmp (type->subtype, "html");
+ text_part = mime_part;
+ first_type = type;
}
- g_object_unref(newpart);
}
}
- parts.clear();
- g_object_unref (istream);
+ return text_part;
}
+
}
+#define NEEDS_DECODING(encoding) ((encoding == GMIME_CONTENT_ENCODING_BASE64) || \
+ (encoding == GMIME_CONTENT_ENCODING_UUENCODE) || \
+ (encoding == GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE))
-namespace{
- struct temp_p {
- GMimeObject *parent,*part;
+namespace
+{
+ char *
+ pan_g_mime_part_get_content (GMimePart *mime_part, size_t *len)
+ {
+ char *retval = NULL;
- temp_p(GMimeObject *p, GMimeObject *par):parent(p),part(par) {};
- };
- typedef std::vector<temp_p> temp_p_t;
+ g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
- void find_text_cb(GMimeObject *parent, GMimeObject *part, gpointer data)
- {
- if(!GMIME_IS_PART(part))
- return;
+ if (!mime_part->content || !mime_part->content->stream) {
+// g_warning ("no content set on this mime part"); // dbg
+ return NULL;
+ }
- temp_p_t *v( (temp_p_t *) data);
- // we assume that inlined yenc and uu are only in text/plain blocks
- GMimeContentType * content_type = g_mime_object_get_content_type (part);
- if (!g_mime_content_type_is_type (content_type, "text", "plain"))
- return;
- v->push_back(temp_p(parent,part) );
+ GMimeDataWrapper *wrapper = g_mime_part_get_content_object(mime_part);
+ GMimeStream *stream = g_mime_stream_mem_new();
+ g_mime_data_wrapper_write_to_stream (wrapper, stream);
+ GByteArray *bytes = g_mime_stream_mem_get_byte_array((GMimeStreamMem*)stream);
+ *len = bytes->len + 1;
+ if (bytes->len)
+ {
+ retval = (char*)g_malloc0(bytes->len + 1);
+ memcpy(retval, bytes->data, bytes->len);
+ }
+ g_object_unref(stream);
+
+ return retval;
}
+}
- void find_gpg_parts_cb(GMimeObject *parent, GMimeObject *part, gpointer data)
- {
- if(!GMIME_IS_PART(part))
- return;
+char *pan::pan_g_mime_message_get_body (GMimeMessage *message, gboolean *is_html)
+{
+ GMimeObject *mime_part = NULL;
+ GMimeContentType *type;
+ GMimeMultipart *multipart;
+ char *body = NULL;
+ size_t len = 0;
- temp_p_t *v( (temp_p_t *) data);
+ g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
+// g_return_val_if_fail (is_html != NULL, NULL);
- GMimeContentType * content_type = g_mime_object_get_content_type (part);
- if (!(g_mime_content_type_is_type (content_type, "multipart", "encrypted") ||
- g_mime_content_type_is_type (content_type, "text", "plain")))
- return;
- v->push_back(temp_p(parent,part) );
+ type = g_mime_object_get_content_type (message->mime_part);
+ if (GMIME_IS_MULTIPART (message->mime_part)) {
+ /* let's see if we can find a body in the multipart */
+ multipart = GMIME_MULTIPART (message->mime_part);
+ if (g_mime_content_type_is_type (type, "multipart", "alternative"))
+ mime_part = handle_multipart_alternative (multipart, is_html);
+ else
+ mime_part = handle_multipart_mixed (multipart, is_html);
+ } else if (g_mime_content_type_is_type (type, "text", "*")) {
+ /* this *has* to be the message body */
+ if (g_mime_content_type_is_type (type, "text", "html"))
+ *is_html = TRUE;
+ else
+ *is_html = FALSE;
+ mime_part = message->mime_part;
}
+ if (mime_part != NULL) {
+ body = pan_g_mime_part_get_content (GMIME_PART (mime_part), &len);
+ }
+ return body;
}
-/***
-****
-***/
+void pan::pan_g_mime_message_add_recipients_from_string (GMimeMessage *message, GMimeRecipientType type, const char *string)
+{
+ InternetAddressList *addrlist;
+ if ((addrlist = internet_address_list_parse_string (string))) {
+ for (int i = 0; i < internet_address_list_length (addrlist); ++i) {
+ InternetAddress *ia = internet_address_list_get_address (addrlist, i);
+ if (INTERNET_ADDRESS_IS_MAILBOX(ia))
+ g_mime_message_add_recipient (message, type, internet_address_get_name(ia), internet_address_mailbox_get_addr(INTERNET_ADDRESS_MAILBOX(ia)));
+ }
+ }
+}
-#ifdef HAVE_GPGME
-GMimeMessage*
-mime :: construct_message (GMimeStream ** istreams,
- int qty,
- GPGSignersInfo & signer_info,
- GPGDecErr & gpgerr)
-#else
-GMimeMessage*
-mime :: construct_message (GMimeStream ** istreams,
- int qty)
-#endif
+/**
+* Works around a GMime bug that uses `Message-Id' rather than `Message-ID'
+*/
+void pan::pan_g_mime_message_set_message_id (GMimeMessage *msg, const char *mid)
{
- const char * message_id = "Foo <bar mum>";
- GMimeMessage * retval = 0;
+ g_mime_object_append_header ((GMimeObject *) msg, "Message-ID", mid);
+ char * bracketed = g_strdup_printf ("<%s>", mid);
+ g_mime_header_list_set (GMIME_OBJECT(msg)->headers, "Message-ID", bracketed);
+ g_free (bracketed);
+}
+
+namespace pan
+
+{
+ #ifdef HAVE_GPGME
+ GMimeStream* gpg_decrypt_and_verify (GPGSignersInfo& signer_info, GPGDecErr& info,
+ GMimeStream* s, int index, GMimeObject* parent)
+ {
+
+ GMimeStream* decrypted = g_mime_stream_mem_new ();
+
+ ssize_t stream_len = g_mime_stream_length(s);
+
+ char* streambuf = new char[stream_len];
+
+ g_mime_stream_read(s, streambuf, stream_len);
+ g_object_unref(s);
- // sanity clause
- pan_return_val_if_fail (is_nonempty_string(message_id), NULL);
- pan_return_val_if_fail (istreams!=NULL, NULL);
- pan_return_val_if_fail (qty>=1, NULL);
- for (int i=0; i<qty; ++i)
- pan_return_val_if_fail (GMIME_IS_STREAM(istreams[i]), NULL);
+ info.err = GPG_ERR_NO_ERROR;
- // build the GMimeMessages
- GMimeParser * parser = g_mime_parser_new ();
- GMimeMessage ** messages = g_new (GMimeMessage*, qty);
- for (int i=0; i<qty; ++i) {
- g_mime_parser_init_with_stream (parser, istreams[i]);
- messages[i] = g_mime_parser_construct_message (parser);
- }
- g_object_unref (parser);
+ gpgme_data_t gpg_buf, gpg_out_buf;
+ gpgme_key_t key;
- if (qty > 1) // fold multiparts together
- {
- GMimeMultipart * mp = g_mime_multipart_new ();
+ StringView v(streambuf);
+ gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
- for (int i=0; i<qty; ++i)
- {
- g_mime_multipart_add(mp,g_mime_message_get_mime_part(messages[i]) );
- }
+ gpgme_strerror(gpgme_data_new (&gpg_out_buf));
- g_mime_message_set_mime_part(messages[0],GMIME_OBJECT(mp));
- g_object_unref(mp);
- }
+ gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_NONE);
- retval = messages[0];
- for (int i=1; i<qty; ++i)
- g_object_unref (messages[i]);
+ info.err = gpgme_op_decrypt_verify (gpg_ctx, gpg_buf, gpg_out_buf);
- // pick out yenc and uuenc data in text/plain messages
- temp_p_t partslist;
- sep_state state;
- if (retval != NULL)
- g_mime_message_foreach(retval, find_text_cb, &partslist);
- foreach(temp_p_t, partslist, it)
- {
- temp_p &data(*it);
- handle_uu_and_yenc_in_text_plain_cb(data.parent, data.part, &state);
- /* set gpg signature verify success/fail flag */
-#ifdef HAVE_GPGME
- g_mime_object_set_header(GMIME_OBJECT(data.parent), "X-GPG-Signed", state.gpg_verified.c_str());
- gpgerr = state.gpgerr;
- signer_info = state.signer_info;
-#endif
- }
+ // no data to decrypt, check for signature validity anyway....
+ if (gpgme_err_code(info.err) == GPG_ERR_NO_DATA || gpgme_err_code(info.err) == GPG_ERR_NO_ERROR)
+ info.dec_ok = true;
+ else
+ return decrypted;
- // cleanup
- foreach (temp_parts_t, state.master_list, it)
- {
- delete *it;
- }
- g_free (messages);
+ info.dec_res = gpgme_op_decrypt_result (gpg_ctx);
+ info.v_res = gpgme_op_verify_result (gpg_ctx);
- return retval;
-}
+ // verify attached sigs, too. that means, no data to decrypt and no signatures found, yet
+ if (gpgme_err_code(info.err) == GPG_ERR_NO_DATA &&
+ gpgme_err_code(info.v_res->signatures->status) == GPG_ERR_BAD_SIGNATURE && parent)
+ {
+ int count(0); int no(0);
+ size_t len;
+ char* body(0);
+ count = g_mime_multipart_get_count (GMIME_MULTIPART(parent));
+ no = index;
+ gpgme_data_t body_data;
+
+ for (int i=0;i<count;++i)
+ {
-/***
-****
-***/
+ if (i == no) continue;
+ GMimeObject* part_of = g_mime_multipart_get_part (GMIME_MULTIPART(parent), i);
+ if (part_of)
+ {
+ body = pan_g_mime_part_get_content(GMIME_PART(part_of),&len);
+ if (body)
+ {
+ gpgme_data_new_from_mem (&body_data, body, len, 0);
+ info.err = gpgme_op_verify (gpg_ctx, gpg_buf, body_data, 0);
+ info.v_res= gpgme_op_verify_result (gpg_ctx);
+ gpgme_data_release(body_data);
+ g_free(body);
+ if (info.err == GPG_ERR_NO_ERROR &&
+ gpgme_err_code(info.v_res->signatures->status) == GPG_ERR_NO_ERROR) break;
+ }
+ }
+ }
+ }
-/**
- * Retrieve the charset from a mime message
- */
+ if (info.v_res->signatures)
+ {
+ info.no_sigs = false;
+ if (gpgme_err_code(info.v_res->signatures->status) == GPG_ERR_NO_ERROR)
+ info.verify_ok = true;
+ }
+ else
+ info.no_sigs = true;
-#if 0 // unused?
-static void
-get_charset_partfunc (GMimeObject * obj, gpointer charset_gpointer)
-{
- GMimePart * part;
- const GMimeContentType * type;
- const char ** charset;
+ delete streambuf;
- if (!GMIME_IS_PART (obj))
- return;
+ gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
- part = GMIME_PART (obj);
- type = g_mime_object_get_content_type (GMIME_OBJECT (part));
- charset = (const char **) charset_gpointer;
- if (g_mime_content_type_is_type (type, "text", "*"))
- *charset = g_mime_object_get_content_type_parameter (GMIME_OBJECT (part), "charset");
-}
+ ssize_t ret(0);
+ char buffer[4096]={0};
+ gint64 len(0);
-const char *
-mime :: get_charset (GMimeMessage * message)
-{
- const char * retval = NULL;
- pan_return_val_if_fail (message!=NULL, NULL);
+ while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
+ {
+ len += g_mime_stream_write(decrypted, buffer, ret);
+ }
- g_mime_message_foreach_part (message, get_charset_partfunc, &retval);
+ g_mime_stream_flush (decrypted);
+ g_mime_stream_reset(decrypted);
- return retval;
-}
-#endif
+ gpgme_data_release(gpg_buf);
+ gpgme_data_release(gpg_out_buf);
-/**
-***
-**/
+ GPGSignersInfo si;
-namespace
-{
- enum StripFlags
- {
- STRIP_CASE = (1<<0),
- STRIP_MULTIPART_NUMERATOR = (1<<1),
- STRIP_MULTIPART = (1<<2)
- };
+ if (!info.no_sigs)
+ {
+ if (info.v_res->signatures->fpr)
+ gpgme_get_key (gpg_ctx, info.v_res->signatures->fpr, &key, 0);
+ if (key)
+ fill_signer_info(si, key);
+ }
- /**
- * Normalizing a subject header involves:
- *
- * 1. tearing out the numerator from multipart indicators
- * (i.e., remove "21" from (21/42))
- * for threading.
- * 2. convert it to lowercase so that, when sorting, we can
- * have case-insensitive sorting without having to use
- * a slower case-insensitive compare function.
- * 3. strip out all the leading noise that breaks sorting.
- *
- * When we're threading articles, it's much faster to
- * normalize the * subjects at the outset instead of
- * normalizing them for each comparison.
- */
- void
- normalize_subject (const StringView & subject,
- StripFlags strip,
- std::string & setme)
- {
-#if 0
- static bool _keep_chars[UCHAR_MAX+1];
- static bool _inited (false);
- if (!_inited) {
- _inited = true;
- for (int i=0; i<=UCHAR_MAX; ++i) {
- const unsigned char uch ((unsigned char)i);
- _keep_chars[i] = isalnum(uch) || isdigit(uch) || isspace(uch);
- }
- }
-#endif
- setme.reserve (subject.len + 1);
- const unsigned char * in ((const unsigned char*) subject.begin());
- const unsigned char * end ((const unsigned char*) subject.end());
- const bool strip_case (strip & STRIP_CASE);
- const bool strip_numerator (strip & STRIP_MULTIPART_NUMERATOR);
- const bool strip_multipart (strip & STRIP_MULTIPART);
+ signer_info = si;
+ info.err = GPG_ERR_NO_ERROR;
- // skip the leading noise
- while (in!=end && isspace(*in))
- ++in;
+ return decrypted;
+ }
- while (in!=end)
- {
- // strip numerator out of multipart information
- if ((strip_multipart||strip_numerator)
- && (*in=='('||*in=='[')
- && isdigit(in[1]))
- {
- const unsigned char * start (++in);
+ void
+ mime_part_set_content (GMimePart *part, const char *str)
+ {
+ GMimeDataWrapper *content;
+ GMimeStream *stream;
- if (strip_multipart || strip_numerator)
- {
- while (in!=end && isdigit(*in))
- ++in;
+ stream = g_mime_stream_mem_new_with_buffer (str, strlen (str));
+ content = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT);
+ g_object_unref (stream);
- if (*in!='/' && *in!='|') // oops, not multipart information
- in = start;
+ g_mime_part_set_content_object (part, content);
+ g_object_unref (content);
+ }
- else if (strip_multipart)
- {
- for (++in; in!=end && *in!=']' && *in!=')'; ++in)
- {
- if (isalpha(*in)) { // oops, not multipart information
- in = ++start;
- break;
- }
- }
+ GMimeMessage*
+ message_add_signed_part (const std::string& uid, const std::string& body_str, GMimeMessage* body, GPGEncErr& fail)
+ {
+ if (uid.empty()) return 0;
- if (in!=end && (*in==']' || *in==')'))
- ++in;
- }
- }
- continue;
- }
+ GMimeMultipart *mp = g_mime_multipart_new_with_subtype ("mixed");
+ g_mime_multipart_set_boundary(mp, NULL);
+ g_mime_multipart_add(mp,g_mime_message_get_mime_part(body));
+
+ gpgme_data_t gpg_buf, gpg_out_buf;
+ gpgme_key_t mykey(0), key;
+
+ StringView v(body_str);
+ gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
+ gpgme_data_new (&gpg_out_buf);
+ // gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_BASE64);
+ // gpgme_data_set_file_name(gpg_out_buf, "signature.asc");
+
+ fail.err = gpgme_op_keylist_start (gpg_ctx, 0, 0);
+ while (!fail.err)
+ {
+ fail.err = gpgme_op_keylist_next (gpg_ctx, &key);
+ if (fail.err) break;
+ if (strcmp(key->subkeys->keyid, uid.c_str()) == 0) { mykey = key; break; }
+ }
+ if (!mykey) { fail.err = GPG_ERR_NO_PUBKEY; return 0; }
+
+ gpgme_signers_clear(gpg_ctx);
+ gpgme_signers_add(gpg_ctx, mykey);
-#if 0
- // strip out junk that breaks sorting
- if (_keep_chars[*in])
-#endif
- setme += (char) (strip_case ? tolower(*in) : *in);
+ fail.err = gpgme_op_sign (gpg_ctx, gpg_buf, gpg_out_buf, GPGME_SIG_MODE_DETACH);
+ fail.sign_res = gpgme_op_sign_result (gpg_ctx);
+ if (fail.err) return 0;
- ++in;
- }
- }
-}
+ gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
+ ssize_t ret;
+ std::stringstream ret_str;
+ char buffer[4096]={0};
-void
-mime :: remove_multipart_from_subject (const StringView & subject,
- std::string & setme)
-{
- normalize_subject (subject, STRIP_MULTIPART, setme);
-}
+ while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
+ {
+ ret_str << buffer;
+ }
-void
-mime :: remove_multipart_part_from_subject (const StringView & subject,
- std::string & setme)
-{
- normalize_subject (subject, STRIP_MULTIPART_NUMERATOR, setme);
-}
+ GMimePart *sig = g_mime_part_new_with_type("multipart", "signed");
+ g_mime_object_set_content_type_parameter(GMIME_OBJECT(sig),"protocol","pgp-signature");
+ mime_part_set_content (sig, ret_str.str().c_str());
-namespace
-{
- GMimeObject *
- handle_multipart_mixed (GMimeMultipart *multipart, gboolean *is_html);
+ gpgme_data_release(gpg_buf);
+ gpgme_data_release(gpg_out_buf);
- GMimeObject *
- handle_multipart_alternative (GMimeMultipart *multipart, gboolean *is_html)
- {
- GMimeObject *mime_part, *text_part = NULL;
- GMimeContentType *type;
- int count = g_mime_multipart_get_count (multipart);
+ g_mime_multipart_add (GMIME_MULTIPART (mp), GMIME_OBJECT (sig));
- for (int i = 0; i < count; ++i) {
- mime_part = g_mime_multipart_get_part (multipart, i);
+ g_mime_message_set_mime_part(body,GMIME_OBJECT(mp));
+ g_object_unref(mp);
- type = g_mime_object_get_content_type (mime_part);
- if (g_mime_content_type_is_type (type, "text", "*")) {
- if (!text_part || !g_ascii_strcasecmp (type->subtype, "plain")) {
- *is_html = !g_ascii_strcasecmp (type->subtype, "html");
- text_part = mime_part;
- }
- }
- }
+ fail.err = GPG_ERR_NO_ERROR;
- return text_part;
+ return body;
}
- GMimeObject *
- handle_multipart_mixed (GMimeMultipart *multipart, gboolean *is_html)
+ std::string
+ gpg_encrypt(const std::string& uid, const std::string& body, GPGEncErr& fail)
{
- GMimeObject *mime_part, *text_part = NULL;
- GMimeContentType *type, *first_type = NULL;
- int count = g_mime_multipart_get_count (multipart);
+ fail.err = GPG_ERR_GENERAL;
- for (int i = 0; i < count; ++i) {
- mime_part = g_mime_multipart_get_part (multipart, i);
+ if (uid.empty()) return "";
- type = g_mime_object_get_content_type (mime_part);
- if (GMIME_IS_MULTIPART (mime_part)) {
- multipart = GMIME_MULTIPART (mime_part);
- if (g_mime_content_type_is_type (type, "multipart", "alternative")) {
- mime_part = handle_multipart_alternative (multipart, is_html);
- if (mime_part)
- return mime_part;
- } else {
- mime_part = handle_multipart_mixed (multipart, is_html);
- if (mime_part && !text_part)
- text_part = mime_part;
- }
- } else if (g_mime_content_type_is_type (type, "text", "*")) {
- if (!g_ascii_strcasecmp (type->subtype, "plain")) {
- /* we got what we came for */
- *is_html = !g_ascii_strcasecmp (type->subtype, "html");
- return mime_part;
- }
+ gpgme_data_t gpg_buf, gpg_out_buf;
+ gpgme_key_t mykey(0), key;
- /* if we haven't yet found a text part or if it is a type we can
- * understand and it is the first of that type, save it */
- if (!text_part || (!g_ascii_strcasecmp (type->subtype, "plain") && (first_type &&
- g_ascii_strcasecmp (type->subtype, first_type->subtype) != 0))) {
- *is_html = !g_ascii_strcasecmp (type->subtype, "html");
- text_part = mime_part;
- first_type = type;
- }
- }
+ StringView v(body);
+ gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
+ gpgme_data_new (&gpg_out_buf);
+ gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_BASE64);
+
+ /* find key to uid */
+ fail.err = gpgme_op_keylist_start (gpg_ctx, 0, 0);
+ while (!fail.err)
+ {
+ fail.err = gpgme_op_keylist_next (gpg_ctx, &key);
+ if (fail.err) break;
+ if (strcmp(key->subkeys->keyid, uid.c_str()) == 0) { mykey = key; break; }
}
+ if (!mykey) { fail.err = GPG_ERR_NO_PUBKEY; return std::string(""); }
- return text_part;
- }
+ gpgme_key_t enc_keys[] = { mykey, NULL};
-}
-#define NEEDS_DECODING(encoding) ((encoding == GMIME_CONTENT_ENCODING_BASE64) || \
- (encoding == GMIME_CONTENT_ENCODING_UUENCODE) || \
- (encoding == GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE))
+ fail.err = gpgme_op_encrypt (gpg_ctx, enc_keys, GPGME_ENCRYPT_PREPARE, gpg_buf, gpg_out_buf);
+ fail.enc_res = gpgme_op_encrypt_result (gpg_ctx);
+ if (fail.err) return std::string("");
-namespace
-{
- char *
- pan_g_mime_part_get_content (GMimePart *mime_part, size_t *len)
+ gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
+ ssize_t ret;
+ std::stringstream ret_str;
+ char buffer[4096]={0};
+
+ while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
+ {
+ ret_str << buffer;
+ }
+
+ gpgme_data_release(gpg_buf);
+ gpgme_data_release(gpg_out_buf);
+
+ fail.err = GPG_ERR_NO_ERROR;
+ return ret_str.str();
+ }
+
+ std::string
+ gpg_encrypt_and_sign(const std::string& uid, const std::string& body, GPGEncErr& fail)
{
- char *retval = NULL;
+ fail.err = GPG_ERR_GENERAL;
- g_return_val_if_fail (GMIME_IS_PART (mime_part), NULL);
+ if (uid.empty()) return "";
- if (!mime_part->content || !mime_part->content->stream) {
-// g_warning ("no content set on this mime part"); // dbg
- return NULL;
- }
+ gpgme_data_t gpg_buf, gpg_out_buf;
+ gpgme_key_t mykey(0), key;
- GMimeDataWrapper *wrapper = g_mime_part_get_content_object(mime_part);
- GMimeStream *stream = g_mime_stream_mem_new();
- g_mime_data_wrapper_write_to_stream (wrapper, stream);
- GByteArray *bytes = g_mime_stream_mem_get_byte_array((GMimeStreamMem*)stream);
- *len = bytes->len + 1;
- if (bytes->len)
+ StringView v(body);
+ gpgme_data_new_from_mem (&gpg_buf, v.str, v.len, 0);
+ gpgme_data_new (&gpg_out_buf);
+ gpgme_data_set_encoding (gpg_out_buf, GPGME_DATA_ENCODING_BASE64);
+
+ /* find key to uid */
+ fail.err = gpgme_op_keylist_start (gpg_ctx, 0, 0);
+ while (!fail.err)
{
- retval = (char*)g_malloc0(bytes->len + 1);
- memcpy(retval, bytes->data, bytes->len);
+ fail.err = gpgme_op_keylist_next (gpg_ctx, &key);
+ if (fail.err) break;
+ if (strcmp(key->subkeys->keyid, uid.c_str()) == 0) { mykey = key; break; }
}
- g_object_unref(stream);
-
- return retval;
- }
-}
+ if (!mykey) { fail.err = GPG_ERR_NO_PUBKEY; return std::string(""); }
-char *pan::pan_g_mime_message_get_body (GMimeMessage *message, gboolean *is_html)
-{
- GMimeObject *mime_part = NULL;
- GMimeContentType *type;
- GMimeMultipart *multipart;
- char *body = NULL;
- size_t len = 0;
+ gpgme_signers_clear(gpg_ctx);
+ gpgme_signers_add(gpg_ctx, mykey);
- g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
-// g_return_val_if_fail (is_html != NULL, NULL);
+ gpgme_key_t enc_keys[] = { mykey, NULL};
- type = g_mime_object_get_content_type (message->mime_part);
- if (GMIME_IS_MULTIPART (message->mime_part)) {
- /* let's see if we can find a body in the multipart */
- multipart = GMIME_MULTIPART (message->mime_part);
- if (g_mime_content_type_is_type (type, "multipart", "alternative"))
- mime_part = handle_multipart_alternative (multipart, is_html);
- else
- mime_part = handle_multipart_mixed (multipart, is_html);
- } else if (g_mime_content_type_is_type (type, "text", "*")) {
- /* this *has* to be the message body */
- if (g_mime_content_type_is_type (type, "text", "html"))
- *is_html = TRUE;
- else
- *is_html = FALSE;
- mime_part = message->mime_part;
- }
+ fail.err = gpgme_op_encrypt_sign (gpg_ctx, enc_keys, GPGME_ENCRYPT_PREPARE, gpg_buf, gpg_out_buf);
+ fail.enc_res = gpgme_op_encrypt_result (gpg_ctx);
+ fail.sign_res= gpgme_op_sign_result (gpg_ctx);
+ if (fail.err) return std::string("");
- if (mime_part != NULL) {
- body = pan_g_mime_part_get_content (GMIME_PART (mime_part), &len);
- }
- return body;
-}
+ gpgme_data_seek (gpg_out_buf,SEEK_SET, 0);
+ ssize_t ret;
+ std::stringstream ret_str;
+ char buffer[4096]={0};
-void pan::pan_g_mime_message_add_recipients_from_string (GMimeMessage *message, GMimeRecipientType type, const char *string)
-{
- InternetAddressList *addrlist;
- if ((addrlist = internet_address_list_parse_string (string))) {
- for (int i = 0; i < internet_address_list_length (addrlist); ++i) {
- InternetAddress *ia = internet_address_list_get_address (addrlist, i);
- if (INTERNET_ADDRESS_IS_MAILBOX(ia))
- g_mime_message_add_recipient (message, type, internet_address_get_name(ia), internet_address_mailbox_get_addr(INTERNET_ADDRESS_MAILBOX(ia)));
+ while ((ret = gpgme_data_read (gpg_out_buf, buffer, sizeof(buffer))) > 0)
+ {
+ ret_str << buffer;
}
- }
-}
-/**
-* Works around a GMime bug that uses `Message-Id' rather than `Message-ID'
-*/
-void pan::pan_g_mime_message_set_message_id (GMimeMessage *msg, const char *mid)
-{
- g_mime_object_append_header ((GMimeObject *) msg, "Message-ID", mid);
- char * bracketed = g_strdup_printf ("<%s>", mid);
- g_mime_header_list_set (GMIME_OBJECT(msg)->headers, "Message-ID", bracketed);
- g_free (bracketed);
+ gpgme_data_release(gpg_buf);
+ gpgme_data_release(gpg_out_buf);
+
+ fail.err = GPG_ERR_NO_ERROR;
+ return ret_str.str();
+ }
+#endif
}
diff --git a/pan/usenet-utils/mime-utils.h b/pan/usenet-utils/mime-utils.h
index 0047432..b43650d 100644
--- a/pan/usenet-utils/mime-utils.h
+++ b/pan/usenet-utils/mime-utils.h
@@ -76,7 +76,7 @@
namespace pan
{
#ifdef HAVE_GPGME
- GMimeStream* gpg_decrypt_and_verify (GPGSignersInfo& signer_info, GPGDecErr& info, GMimeStream* s, const char* body=0);
+ GMimeStream* gpg_decrypt_and_verify (GPGSignersInfo& signer_info, GPGDecErr& info, GMimeStream* s, int index=0, GMimeObject* parent=0);
GMimeMessage* message_add_signed_part (const std::string& uid, const std::string& body_str, GMimeMessage* body, GPGEncErr& fail);
std::string gpg_encrypt(const std::string& uid, const std::string& body, GPGEncErr& fail);
std::string gpg_encrypt_and_sign(const std::string& uid, const std::string& body, GPGEncErr& fail);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]