Re: [gmime-devel] Accessing attachments including inline-parts.



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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]