On 5/25/2014 4:20 PM, Steve Kemp wrote:
The count differs because your loop is ignoring everything except GMimeParts: if ( (! GMIME_IS_OBJECT( part ) ) || ( !GMIME_IS_PART(part) ) ) continue;Ahh, that makes sense. It's just without it I see: (process:14845): GLib-GObject-WARNING **: invalid cast from `GMimeMessagePart' to `GMimePart' (process:14845): gmime-CRITICAL **: g_mime_part_get_content_object: assertion `GMIME_IS_PART (mime_part)' failed (process:14845): gmime-CRITICAL **: g_mime_data_wrapper_write_to_stream: assertion `GMIME_IS_DATA_WRAPPER (wrapper)' failed which made me think I should ignore the odd parts.
Right - your current code will only work with GMimeParts and won't work with GMimeMessageParts because GMimeMessageParts do not subclass GMimePart and so g_mime_part_*() functions will fail.
what you probably want to do is something like this: GMimeObject *part = g_mime_part_iter_get_current (iter); if (GMIME_IS_MULTIPART (part)) continue; /** * Name of the attachment, if we found one. */ char *aname = NULL; /** * Attachment content, if we found one. */ GByteArray *adata = NULL; /** * Get the content-disposition, so that we can determine * if we're dealing with an attachment, or an inline-part. */ GMimeContentDisposition *disp = NULL; disp = g_mime_object_get_content_disposition (part); if (disp != NULL && !g_ascii_strcasecmp (disp->disposition, "attachment")) { /** * Attempt to get the filename/name. */ aname = (char *)g_mime_object_get_content_disposition_parameter(part, "filename"); if ( aname == NULL ) aname = (char *)g_mime_object_get_content_type_parameter(part, "name"); /** * Did that work? */ if ( aname == NULL ) { std::cout << "\tAttachment has no name." << std::endl; } else { std::cout << "\tAttachment has name : " << aname << std::endl; } } else { if ( disp != NULL && disp->disposition != NULL ) std::cout << "\tInline part with name: " << disp->disposition << std::endl; else std::cout << "\tAnonymous inline part." << std::endl; } /** * Get the attachment data. */ GMimeStream *mem = g_mime_stream_mem_new (); if (GMIME_IS_MESSAGE_PART (part)) { GMimeMessage *msg = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part)); g_mime_object_write_to_stream (GMIME_OBJECT (msg), mem); } else { GMimeDataWrapper *content = g_mime_part_get_content_object (GMIME_PART (part)); g_mime_data_wrapper_write_to_stream (content, mem); } // by setting the owner to FALSE, it means unreffing the memory stream won't // free the GByteArray data. g_mime_stream_mem_set_owner (GMIME_STREAM_MEM (mem), FALSE); // get the GByteArray data adata = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (mem)); // free the memory stream g_object_unref (mem);I'd recommend replacing the malloc & memcpy logic in CAttachment with just using the GByteArray to avoid unnecessary overhead of duplicating the content in memory, but that's up to you.
(FWIW, the check for GMIME_IS_OBJECT is not necessary)Thanks.The multiparts can't ever be considered "attachments", so it's fine to ignore those. However, the message/rfc822 part might be considered an attachment. You can check for message/rfc822 parts by using GMIME_IS_MESSAGE_PART().Switching to "if ( ! GMIME_IS_MESSAGE_PART( parth ) ) continue;" seems to give similar assertions.
This should be solved by the code snippet I pasted above.
Once you know a part is a GMimePart, you can simplify your code to get the file name by using g_mime_part_get_filename() which is a convenience function to do what you are currently doing. There's nothing wrong with your current code for that, but I figured I'd point out a simpler way in case you didn't know about it.Noted, thanks.That said, a GMimeMessagePart does not subclass GMimePart, so keeping your current code is useful if you want to handle message/rfc822 parts as attachments.The ultimate goal has always been to a) discover and b) display arbitrary attachments. So I guess I leaving this alone won't hurt.As far as bogus content sizes goes, this seems to be part of the problem: guint8 len= g_mime_data_wrapper_write_to_stream( content, memstream ); guint8 is an 8-bit integer while g_mime_data_wrapper_write_to_stream() returns a size_t (which is typically a 32-bit integer).D'oh that makes a lot of sense. I've copied that from some other working code I have, and managed to never spot it was a problem. I suspect I'm using copy_stream, and just using the size for my own purposes so I'd not noticed the disparity.You probably also don't want to use the return value of g_mime_data_wrapper_write_to_stream() as an exact number of bytes written because the data goes through a decoder transform which may alter the number of bytes actually written. I think I tried to make GMimeStreamFilter account for that, but it's probably safer to measure the stream length instead (the GByteArray has a 'len' struct member that holds the number of bytes in the 'data' struct member, so that's probably what you want to use).Thanks, that solves the problem.Unfortunately, even after these changes that I made locally, it still gave me bogus size values when printing out the CAttachment vector items. I think the problem is that you are creating the CAttachment items on the stack instead of the heap, so when the caller function iterates over the vector, the items are completely bogus.I've since fixed that to use the heap, because I spotted the same problem. (Also I wanted to delete the malloced memory.)I'm not very sharp with c++, though, so my understanding of the CAttachment constructor syntax could be totally wrong - either way, the size values printed in your "for (CAttachment cur : result)" loop do not match the size values that I was printing inside of handle_mail(), so something isn't quite right with the vector results.It looks like I didn't setup m_size in the constructor for CAttachment.
Ah, that would make sense. I didn't catch that.
Oh, and it's not necessary to call g_mime_stream_close() if you are going to unref it. The final unref of the stream will close it for you.Thanks.Hope that helps,You're awesome, and very patient. Making the obvious changes still hasn't gotten me the result I want, but I'm getting closer to finding out why.
Hopefully my code snippet above helps get you closer.There's one last thing you'll need to be aware of with the code snippet I pasted above, though, which is that the iterator will descend into attached messages, so you'll be extracting both the attached messages *and* each of the attachments they contain with the logic in my snippet above. I'm not sure how you want to handle things. Maybe that's ok for what your intention is or maybe it's not.
If you don't want to descend, you could probably check if g_mime_part_iter_get_parent() returns a GMimeMessagePart. If it does, just continue to the next part.
so something like this: GMimeObject *parent = g_mime_part_iter_get_parent (iter); GMimeObject *part = g_mime_part_iter_get_current (iter); if (GMIME_IS_MESSAGE_PART (parent)) continue;
My advice to anybody writing a mail client is to stick with mutt .. ;)
Been down that road and scarily enough, I'm half considering going down that road again. Lord help me.
Hope that helps, Jeff
Attachment:
smime.p7s
Description: S/MIME Cryptographic Signature