Re: [gmime-devel] Help appreciated adding attachments to outgoing mails



Hi Steve!

On 8/11/2013 5:14 AM, Steve Kemp wrote:
I'm working on (yet another) console-based mail client and I've successfully
ported the majority of my code from using libmimetic to using GMime for
handling mail-parsing, attachment extraction, etc.

I have only one outstanding area which I'm struggling with.  I wish to allow
the user to add attachments to an outgoing messages.  Currently I have a
"simple" email stored as a file, and a vector of attachments which need to
be added.

The simple message looks something like this:

      To: "Bob" <bob example com>
      From: "Moi" <steve example org>
      Subject: This is the subject

      Body text
      Body text more

      Sig
      --

At the moment I'm struggling on where to go from here.  I guess my
code should be parsing this message, then adding the attachment
before serializing it to a (new) file, on-disk.  (From where it can
be piped to sendmail, and written to the sent-mail folder.)

Parsing the message is as simple as:

     GMimeParser *parser;
     GMimeStream *stream;
     int fd;

     if ((fd = open ( filename.c_str(), O_RDONLY, 0)) == -1)
         return;

     stream = g_mime_stream_fs_new (fd);

     parser = g_mime_parser_new_with_stream (stream);
     g_object_unref (stream);

     m_message = g_mime_parser_construct_message (parser);
     g_object_unref (parser);

Looking good so far...


But from there I'm struggling to understand which parts I need to add.
Clearly I'm going to be looking at an end-result which is multi-part,
rather than the input which is MIME-free.  I'm also looking at the
complex examples/imap-example.c and being a little overwhelmed by
the idea of serializing the final result to a (new) filename.

FWIW, the imap-example.c code's logic for serializing is complex mostly because what it is doing is serializing each mime part individually into different files. This was done mostly to teach all the King's horses and all the King's men how to piece them back together again. Serializing a message to a stream (or any part, really) is as simple as g_mime_object_write_to_stream ():

g_mime_object_write_to_stream ((GMimeObject *) message, stream);

If we can safely assume that the "simple message" is just a plain text / non-mime message (such as your example), then it's fairly easy to do (it gets a little more complex if the message already has mime parts because you'd have to figure out where the best place to add them is, although really you could probably get away with doing exactly the same thing as I'm about to show you and it'd work out fine).

I'm not really fluent in c++ so I'm not sure how to use vectors, but let's pretend we have a char[][] of attachment filenames:

GMimeMultipart *multipart;
GMimePart *attachment;
GMimeDataWrapper *content;
GMimeStream *stream;
int fd;

multipart = g_mime_multipart_new ("mixed");

// first, add the message's toplevel mime part into the multipart
g_mime_multipart_add (multipart, g_mime_message_get_mime_part (message));

// now set the multipart as the message's top-level mime part
g_mime_message_set_mime_part (message, multipart);

// now attach all of the files...
for (int i = 0; filenames[i] != NULL; i++) {
    if ((fd = open (filenames[i], O_RDONLY)) == -1)
        return;

    stream = g_mime_stream_fs_new (fd);

    // the stream isn't encoded, so just use DEFAULT
content = g_mime_data_wrapper_new_with_stream (stream, GMIME_CONTENT_ENCODING_DEFAULT);
    g_object_unref (stream);

// if you knew the mime-type of the file, you could use that instead of application/octet-stream
    attachment = g_mime_part_new_with_type ("application", "octet-stream");
    g_mime_part_set_content_object (attachment, content);
    g_object_unref (content);

    // set the filename?
    g_mime_part_set_filename (attachment, basename (filenames[i]));

    // we might want to base64 encode this for transport...
// Note: if you want o get really fancy, you could use g_mime_part_get_best_content_encoding()
    // to calculate the most efficient encoding algorithm to use.
g_mime_part_set_content_encoding (attachment, GMIME_CONTENT_ENCODING_BASE64);

    // attach the attachment to the multipart
    g_mime_multipart_add (multipart, attachment);
    g_object_unref (attachment);
}

// now that we've finished referencing the multipart directly (the message still holds it's own ref)
// we can unref it.
g_object_unref (multipart);

// we're done!



Any comments/suggestions would be useful.  Frustratingly all the
handling of attachments I could find via online searching were relating
to *extraction*/*decoding* and the test-suit/examples don't seem to
have any code I can be inspired by.

No problem. Hopefully the above code snippet will help get you started.


FWIW, I started working on a client library a long time ago based on GMime that you may be able to look over as an additional source of ideas. It's mostly "dead" at this point because I have too many other things on my plate that I'm working on and have lost interest in writing my own mail client, but you can find the source code up on github:

https://github.com/jstedfast/spruce

The src/ directory contains only a simple mailx-like user-interface that I used to test that things more-or-less worked. You pass it a url to your mail folder and it will show you the contents and allow you to read them, etc.

./spruce-mailx imap://steve:passwd steve org co uk/#INBOX

I forget the syntax, but there's a way to specify auth type, starttls, etc.

This is where you'll find the library sources:

https://github.com/jstedfast/spruce/tree/master/spruce

and the individual protocol implementations are under:

https://github.com/jstedfast/spruce/tree/master/spruce/providers

I've got mbox, maildir, smtp, imap, pop3 and sendmail

I think I slapped it up on github about a year or so ago in the hopes that someone would find it useful since I lost steam on it.

Enjoy!

Jeff


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