libEtPan! API
Prev | Chapter 5. Storages, folders, messages | Next

# Message

## Message driver

    #include <libetpan/libetpan.h>

    struct mailmessage_driver {
      char * msg_name;

      int (* msg_initialize)(mailmessage * msg_info);

      void (* msg_uninitialize)(mailmessage * msg_info);

      void (* msg_flush)(mailmessage * msg_info);

      void (* msg_check)(mailmessage * msg_info);

      void (* msg_fetch_result_free)(mailmessage * msg_info,
    			     char * msg);

      int (* msg_fetch)(mailmessage * msg_info,
    		char ** result,
    		size_t * result_len);

      int (* msg_fetch_header)(mailmessage * msg_info,
    		       char ** result,
    		       size_t * result_len);

      int (* msg_fetch_body)(mailmessage * msg_info,
    		     char ** result, size_t * result_len);

      int (* msg_fetch_size)(mailmessage * msg_info,
    		     size_t * result);

      int (* msg_get_bodystructure)(mailmessage * msg_info,
    			    struct mailmime ** result);

      int (* msg_fetch_section)(mailmessage * msg_info,
    			struct mailmime * mime,
    			char ** result, size_t * result_len);

      int (* msg_fetch_section_header)(mailmessage * msg_info,
    			       struct mailmime * mime,
    			       char ** result,
    			       size_t * result_len);

      int (* msg_fetch_section_mime)(mailmessage * msg_info,
    			     struct mailmime * mime,
    			     char ** result,
    			     size_t * result_len);

      int (* msg_fetch_section_body)(mailmessage * msg_info,
    			     struct mailmime * mime,
    			     char ** result,
    			     size_t * result_len);

      int (* msg_fetch_envelope)(mailmessage * msg_info,
    			 struct mailimf_fields ** result);

      int (* msg_get_flags)(mailmessage * msg_info,
    		    struct mail_flags ** result);
    };

- msg_name is the name of the driver.

- msg_initialize() will initialize the internal message state (field msg_data of mailmessage structure (see the Section called Message).

- msg_uninitialize() will free the internal message state.

- msg_flush() will release memory used by the MIME structure of the message.

- msg_check() will store the flags of the message into the session, so that the message can be released without the flags are lost.

- msg_fetch_result_free() will free a string returned by any fetch_XXX() function.

- msg_fetch() will fetch a message.

- msg_fetch_header() will fetch the header fields of a message.

- msg_fetch_body() will fetch a message without its main header.

- msg_fetch_size() will return the size of a message.

- msg_get_bodystructure will retrieve the MIME structure of the message. The returned structure must NOT be freed.

- msg_fetch_section() will fetch the content of the section of the message.

- msg_fetch_section_header() will fetch the header of a section of the message if the content of the section is a message.

- msg_fetch_section_mime() will fetch the MIME header of a section of the message.

- msg_fetch_section_body() will fetch the body of a section (without the headers) of the message if the content of the section is a message.

- msg_fetch_envelope() will return a given number of parsed header fields.

- msg_get_flags() will return the flags of the message. The returned structure must NOT be freed.

## Message

    #include <libetpan/libetpan.h>

    struct mailmessage {
      mailsession * msg_session;
      mailmessage_driver * msg_driver;
      uint32_t msg_index;
      char * msg_uid;

      size_t msg_size;
      struct mailimf_fields * msg_fields;
      struct mail_flags * msg_flags;

      int msg_resolved;
      struct mailimf_single_fields msg_single_fields;
      struct mailmime * msg_mime;

      /* internal data */

      int msg_cached;
      void * msg_data;

     /*
       msg_folder field :
       used to reference the mailfolder, this is a workaround due
       to the problem with initial conception, where folder notion
       did not exist.
     */
      void * msg_folder;
      /* user data */
      void * msg_user_data;
    };

- msg_session is the session related to the message (see the Section called Session).

- msg_driver is the driver used for the message (see the Section called Message driver).

- msg_index is an index to indentify the message.

- msg_uid is the unique identifier of the message, valid accross disconnections.

- msg_size is the size of the message.

- msg_fields is the list of parsed header fields of the message. This can be NULL (see the Section called mailimf_fields - list of header fields in Chapter 3).

- msg_flags is the flags of the message. This can be NULL (see the Section called Message flags).

- msg_resolved will tell if the field msg_single_fields has been initialized.

- msg_single_fields will be filled using msg_fields (see the Section called mailimf_single_fields - simplified fields in Chapter 3).

- msg_mime is the MIME structure of the message. It is intialized at least when get_bodystructure() is called once.

- msg_cached is 1 when the message was cached. This is used internally.

- msg_data is the internal state of the message. The content depends on the driver.

- msg_folder is used to reference the mailfolder, this is a workaround due to the problem with initial conception, where folder notion did not exist.

- msg_user_data is a field for free use. The user can store any data in that field.

## mailmessage_new

    #include <libetpan/libetpan.h>

    mailmessage * mailmessage_new(void);

    void mailmessage_free(mailmessage * info);

mailmessage_new() will create a new message (without driver). This is used internally by drivers.

mailmessage_free() will free the memory used by the given message.

## mailmessage_init

    #include <libetpan/libetpan.h>

    int mailmessage_init(mailmessage * msg_info,
    		     mailsession * session,
    		     mailmessage_driver * driver,
    		     uint32_t index, size_t size);

mailmessage_init() will initialize a message with a driver.

- msg_info is the message to initialize (see the Section called Message).

- session is the session related to the message (see the Section called Session).

- driver is the driver to use for the message (see the Section called Message driver).

- index is the index of the message.

- size is the size of the message.

## mailmessage_flush

    #include <libetpan/libetpan.h>

    int mailmessage_flush(mailmessage * info);

This function will release the memory used by the MIME structure of the message.

## mailmessage_check

    #include <libetpan/libetpan.h>

    int mailmessage_check(mailmessage * info);

After you set some flags, if you want to notify them to the session before destroying the message, you can use this function.

## mailmessage_fetch_result_free

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_result_free(mailmessage * msg_info,
    				  char * msg);

This function will free a string returned by any mailmessage_fetch_XXX() function.

## mailmessage_fetch

    #include <libetpan/libetpan.h>

    int mailmessage_fetch(mailmessage * msg_info,
    		      char ** result,
    		      size_t * result_len);

This function returns the content of the message (headers and text).

## mailmessage_fetch_header

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_header(mailmessage * msg_info,
    			     char ** result,
    			     size_t * result_len);

This function returns the header of the message as a string.

## mailmessage_fetch_body

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_body(mailmessage * msg_info,
    			   char ** result, size_t * result_len);

This function returns the content of the message (without headers).

## mailmessage_fetch_size

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_size(mailmessage * msg_info,
    			   size_t * result);

This function returns the size of the message content.

## mailmessage_get_bodystructure

    #include <libetpan/libetpan.h>

    int mailmessage_get_bodystructure(mailmessage * msg_info,
    				  struct mailmime ** result);

This functions returns the MIME structure of the message. The returned information MUST not be freed by hand. It is freed by mailmessage_flush() or mailmessage_free() (see the Section called mailmime - MIME part in Chapter 4).

## mailmessage_fetch_section

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_section(mailmessage * msg_info,
    			      struct mailmime * mime,
    			      char ** result, size_t * result_len);

This function returns the content of a MIME part.

## mailmessage_fetch_section_header

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_section_header(mailmessage * msg_info,
    				     struct mailmime * mime,
    				     char ** result,
    				     size_t * result_len);

This function returns the header of the message contained in the given MIME part.

## mailmessage_fetch_section_mime

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_section_mime(mailmessage * msg_info,
    				   struct mailmime * mime,
    				   char ** result,
    				   size_t * result_len);

This function returns the MIME header of the given MIME part.

## mailmessage_fetch_section_body

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_section_body(mailmessage * msg_info,
    				   struct mailmime * mime,
    				   char ** result,
    				   size_t * result_len);

This function returns the text part of the message contained in the given MIME part.

## mailmessage_fetch_envelope

    #include <libetpan/libetpan.h>

    int mailmessage_fetch_envelope(mailmessage * msg_info,
    			       struct mailimf_fields ** result);

## mailmessage_get_flags

    #include <libetpan/libetpan.h>

    int mailmessage_get_flags(mailmessage * msg_info,
    			  struct mail_flags ** result);

This function returns the flags related to the message. The returned information MUST not be freed by hand. It is freed by mailmessage_free().

## mailmessage_resolve_single_fields

    #include <libetpan/libetpan.h>

    void mailmessage_resolve_single_fields(mailmessage * msg_info);

This function will use the fields information to fill the single_fields structure in the mailmessage structure.

## Message list

    #include <libetpan/libetpan.h>

    struct mailmessage_list {
      carray * msg_tab; /* elements are (mailmessage *) */
    };

    struct mailmessage_list * mailmessage_list_new(carray * msg_tab);

    void mailmessage_list_free(struct mailmessage_list * env_list);

This is a list of messages.

msg_tab is an array containing the messages (see linkend="carray").

mailmessage_list_new() will initialize a list of messages, using a given array of messages.

mailmessage_list_free() will free the memory used by the list of messages. This will also free the messages.

## Message tree

    #include <libetpan/libetpan.h>

    struct mailmessage_tree {
      struct mailmessage_tree * node_parent;
      char * node_msgid;
      time_t node_date;
      mailmessage * node_msg;
      carray * node_children; /* array of (struct mailmessage_tree *) */

      /* private, used for threading */
      int node_is_reply;
      char * node_base_subject;
    };

    struct mailmessage_tree *
    mailmessage_tree_new(char * node_msgid, time_t node_date,
        mailmessage * node_msg);

    void mailmessage_tree_free(struct mailmessage_tree * tree);

    void mailmessage_tree_free_recursive(struct mailmessage_tree * tree);

This is a node of a tree of messages.

- node_parent is the parent of this node.

- node_msgid is the content of the field Message-ID of the message.

- node_date is the date in UNIX format.

- node_msg is the message of the node. The message should have the msg_fields field initialized.

- node_children is the list of children of this node.

- node_is_reply is set to 1 if the message is a reply.

- node_base_subject is the base subject of the message (base subject is defined in definition of IMAP thread draft).

mailmessage_tree_new() will initialize a message node.

mailmessage_tree_free() will release memory used by the node. This will NOT free the message.

## Message flags

    #include <libetpan/libetpan.h>

    enum {
      MAIL_FLAG_NEW       = 1 << 0,
      MAIL_FLAG_SEEN      = 1 << 1,
      MAIL_FLAG_FLAGGED   = 1 << 2,
      MAIL_FLAG_DELETED   = 1 << 3,
      MAIL_FLAG_ANSWERED  = 1 << 4,
      MAIL_FLAG_FORWARDED = 1 << 5,
      MAIL_FLAG_CANCELLED = 1 << 6,
    };

    struct mail_flags {
      uint32_t fl_flags;
      clist * fl_extension; /* elements are (char *) */
    };

    struct mail_flags * mail_flags_new(uint32_t fl_flags, clist * fl_ext);

    void mail_flags_free(struct mail_flags * flags);

    int mail_flags_add_extension(struct mail_flags * flags,
    			     char * ext_flag);

    int mail_flags_remove_extension(struct mail_flags * flags,
    				char * ext_flag);

    int mail_flags_has_extension(struct mail_flags * flags,
    			     char * ext_flag);

This is the structure containing the message flags.

- fl_flags will contain the standards flags. The value will be a combinaison (with or binary operation) of MAIL_FLAG_XXX values.

- fl_extension will be a list (see the Section called List in Chapter 2) of strings representing the non-standard flags.

## Example

Example 5-3. use of message

    #include <libetpan/libetpan.h>

    #define DEST_CHARSET "iso-8859-1"

    enum {
      NO_ERROR,
      ERROR_FILE,
      ERROR_MEMORY,
      ERROR_INVAL,
      ERROR_FETCH,
    };

    /* returns TRUE is given MIME part is a text part */

    int etpan_mime_is_text(struct mailmime * build_info)
    {
      if (build_info->mm_type == MAILMIME_SINGLE) {
        if (build_info->mm_content_type != NULL) {
          if (build_info->mm_content_type->ct_type->tp_type ==
              MAILMIME_TYPE_DISCRETE_TYPE) {
            if (build_info->mm_content_type->ct_type->tp_data.tp_discrete_type->dt_type ==
                MAILMIME_DISCRETE_TYPE_TEXT)
              return 1;
          }
        }
        else
          return 1;
      }

      return 0;
    }

    /* display content type */

    int show_part_info(FILE * f,
        struct mailmime_single_fields * mime_fields,
        struct mailmime_content * content)
    {
      char * description;
      char * filename;
      int col;
      int r;

      description = mime_fields->fld_description;
      filename = mime_fields->fld_disposition_filename;

      col = 0;

      r = fprintf(f, " [ Part ");
      if (r < 0)
        goto err;

      if (content != NULL) {
        r = mailmime_content_type_write(f, &col, content);
        if (r != MAILIMF_NO_ERROR)
          goto err;
      }

      if (filename != NULL) {
        r = fprintf(f, " (%s)", filename);
        if (r < 0)
          goto err;
      }

      if (description != NULL) {
        r = fprintf(f, " : %s", description);
        if (r < 0)
          goto err;
      }

      r = fprintf(f, " ]\n\n");
      if (r < 0)
        goto err;

      return NO_ERROR;

     err:
      return ERROR_FILE;
    }

    /* fetch message and decode if it is base64 or quoted-printable */

    int etpan_fetch_message(mailmessage * msg_info,
        struct mailmime * mime_part,
        struct mailmime_single_fields * fields,
        char ** result, size_t * result_len)
    {
      char * data;
      size_t len;
      int r;
      int encoding;
      char * decoded;
      size_t decoded_len;
      size_t cur_token;
      int res;
      int encoded;

      encoded = 0;

      r = mailmessage_fetch_section(msg_info,
          mime_part, &data, &len);
      if (r != MAIL_NO_ERROR) {
        res = ERROR_FETCH;
        goto err;
      }

      encoded = 1;

      /* decode message */

      if (encoded) {
        if (fields->fld_encoding != NULL)
          encoding = fields->fld_encoding->enc_type;
        else
          encoding = MAILMIME_MECHANISM_8BIT;
      }
      else {
        encoding = MAILMIME_MECHANISM_8BIT;
      }

      cur_token = 0;
      r = mailmime_part_parse(data, len, &cur_token,
    			  encoding, &decoded, &decoded_len);
      if (r != MAILIMF_NO_ERROR) {
        res = ERROR_FETCH;
        goto free;
      }

      mailmessage_fetch_result_free(msg_info, data);

      * result = decoded;
      * result_len = decoded_len;

      return NO_ERROR;

     free:
      mailmessage_fetch_result_free(msg_info, data);
     err:
      return res;
    }

    /* fetch fields */

    struct mailimf_fields * fetch_fields(mailmessage * msg_info,
        struct mailmime * mime)
    {
      char * data;
      size_t len;
      int r;
      size_t cur_token;
      struct mailimf_fields * fields;

      r = mailmessage_fetch_section_header(msg_info, mime,
        &data, &len);
      if (r != MAIL_NO_ERROR)
        return NULL;

      cur_token = 0;
      r = mailimf_envelopes_fields_parse(data, len,
        &cur_token, &fields);
      if (r != MAILIMF_NO_ERROR) {
        mailmessage_fetch_result_free(msg_info, data);
        return NULL;
      }

      mailmessage_fetch_result_free(msg_info, data);

      return fields;
    }

    /* render message */

    static int etpan_render_mime(FILE * f, mailmessage * msg_info,
        struct mailmime * mime)
    {
      int r;
      clistiter * cur;
      int col;
      int text;
      int show;
      struct mailmime_single_fields fields;
      int res;

      mailmime_single_fields_init(&fields, mime->mm_mime_fields,
          mime->mm_content_type);

      text = etpan_mime_is_text(mime);

      r = show_part_info(f, &fields, mime->mm_content_type);
      if (r != NO_ERROR) {
        res = r;
        goto err;
      }

      switch(mime->mm_type) {
      case MAILMIME_SINGLE:
        show = 0;
        if (text)
          show = 1;

        if (show) {
          char * data;
          size_t len;
          char * converted;
          size_t converted_len;
          char * source_charset;
          size_t write_len;

          /* viewable part */

          r = etpan_fetch_message(msg_info, mime,
              &fields, &data, &len);
          if (r != NO_ERROR) {
            res = r;
            goto err;
          }

          source_charset = fields.fld_content_charset;
          if (source_charset == NULL)
            source_charset = DEST_CHARSET;

          r = charconv_buffer(source_charset, DEST_CHARSET,
              data, len, &converted, &converted_len);
          if (r != MAIL_CHARCONV_NO_ERROR) {

            r = fprintf(f, "[ error converting charset from %s to %s ]\n",
                source_charset, DEST_CHARSET);
              if (r < 0) {
                res = ERROR_FILE;
                goto err;
              }

              write_len = fwrite(data, 1, len, f);
              if (write_len != len) {
                mailmime_decoded_part_free(data);
                res = r;
                goto err;
              }
            }
            else {
              write_len = fwrite(converted, 1, converted_len, f);
              if (write_len != len) {
                charconv_buffer_free(converted);
                mailmime_decoded_part_free(data);
                res = r;
                goto err;
              }

              charconv_buffer_free(converted);
            }

            write_len = fwrite("\r\n\r\n", 1, 4, f);
            if (write_len < 4) {
              mailmime_decoded_part_free(data);
              res = ERROR_FILE;
              goto err;
            }

          mailmime_decoded_part_free(data);
        }
        else {
          /* not viewable part */

          r = fprintf(f, "   (not shown)\n\n");
          if (r < 0) {
            res = ERROR_FILE;
            goto err;
          }
        }

        break;

      case MAILMIME_MULTIPLE:

        if (strcasecmp(mime->mm_content_type->ct_subtype,
          "alternative") == 0) {
          struct mailmime * prefered_body;
          int prefered_score;

          /* case of multiple/alternative */

          /*
            we choose the better part,
            alternative preference :

    	text/plain => score 3
    	text/xxx   => score 2
    	other      => score 1
          */

          prefered_body = NULL;
          prefered_score = 0;

          for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
              cur != NULL ; cur = clist_next(cur)) {
    	struct mailmime * submime;
    	int score;

    	score = 1;
    	submime = clist_content(cur);
            if (etpan_mime_is_text(submime))
              score = 2;

    	if (submime->mm_content_type != NULL) {
              if (strcasecmp(submime->mm_content_type->ct_subtype,
                "plain") == 0)
                score = 3;
    	}

    	if (score > prefered_score) {
    	  prefered_score = score;
    	  prefered_body = submime;
    	}
          }

          if (prefered_body != NULL) {
    	r = etpan_render_mime(f, msg_info, prefered_body);
    	if (r != NO_ERROR) {
    	  res = r;
              goto err;
            }
          }
        }
        else {
          for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
              cur != NULL ; cur = clist_next(cur)) {

            r = etpan_render_mime(f, msg_info, clist_content(cur));
            if (r != NO_ERROR) {
              res = r;
              goto err;
            }
          }
        }

        break;

      case MAILMIME_MESSAGE:

        if (mime->mm_data.mm_message.mm_fields != NULL) {
          struct mailimf_fields * fields;

          if (msg_info != NULL) {
            fields = fetch_fields(msg_info, mime);
            if (fields == NULL) {
              res = ERROR_FETCH;
              goto err;
            }

            col = 0;
            r = mailimf_fields_write(f, &col, fields);
            if (r != NO_ERROR) {
              mailimf_fields_free(fields);
              res = r;
              goto err;
            }

            mailimf_fields_free(fields);
          }
          else {
            col = 0;
            r = fields_write(f, &col, mime->mm_data.mm_message.mm_fields);
            if (r != NO_ERROR) {
              res = r;
              goto err;
            }
          }

          r = fprintf(f, "\r\n");
          if (r < 0) {
            res = ERROR_FILE;
            goto err;
          }
        }

        if (mime->mm_data.mm_message.mm_msg_mime != NULL) {
          r = etpan_render_mime(f, msg_info,
            mime->mm_data.mm_message.mm_msg_mime);
          if (r != NO_ERROR) {
            res = r;
            goto err;
          }
        }

        break;
      }

      return NO_ERROR;

     err:
      return res;
    }

    int main(void)
    {
      struct mailstorage * storage;
      int r;

      storage = mailstorage_new(NULL);

      imap_mailstorage_init(storage, "imap.my-servers.org", 0,
        NULL, CONNECTION_TYPE_TRY_STARTTLS, IMAP_AUTH_TYPE_PLAIN,
        "my-login", "my-password", 1, "/home/login/.libetpan/cache");

      r = mailstorage_connect(storage);
      if (r == MAIL_NO_ERROR) {
        struct mailfolder * folder;

        folder = mailfolder_new(storage, "INBOX", NULL);

        r = mailfolder_connect(folder);
        if (r == MAIL_NO_ERROR) {
          struct mailmessage_list * msg_list;
          mailmessage * msg;

          mailfolder_get_messages_list(folder, &msg_list);

          if (carray_count(msg_list->msg_tab) > 0) {
            struct mailmime * mime;

            msg = carray_get(msg_list->msg_tab, 0);

            mailmessage_get_bodystructure(msg, &mime);

            recursive_fetch(msg, mime);

            /* do the things */

            mailmessage_flush(msg);
          }
          mailmessage_list_free(msg_list);

          mailfolder_disconnect(folder);
        }

        mailstorage_disconnect(storage);
      }

      mailstorage_free(storage);
    }

Prev | Home | Next
Folder | Up | Session