Sending a message

About

This tutorial is about sending an E-mail, usually one that you recently created, in an asynchronous way. This wiki page about how to do a Drafts folder is related too.

The basic idea

On the page about designing and implementing a asynchronous transport mechanism we learn that Tinymail comes with a asynchronous queue for sending E-mail. For more end-developer information you can read about the TnySendQueue type.

Tinymail has indeed a few such queue implementations. It also allows you to implement your own queue and/or classifier that routes messages to different queues.

This can be useful if the mobile device is suppose to detect which transport technique it should employ at a specific moment in time. For example picking an SMTP service depending on the network that is active (a TCP/IP service, a GPRS service, an insecure Wifi service, an SMS service and tomorrow the world wide web of automobile interconnectivity data exchange bla bla).

We will in this tutorial focus on the TnySendQueue API itself.

Sample code

This example shows the default TnySendQueue of Tinymail: the TnyCamelSendQueue. The TnyCamelSendQueue uses a TnyCamelTransportAccount to make the actual transport of the message happen. Therefore it's a very simple queue with no classification or routing.

Most E-mail clients will want to do something similar to this. Depending on the environment where the E-mail client will be deployed, it's possible however that you want to have more control over the classification of how messages will be sent. It's for example perfectly legal within the TnySendQueue API contract to embed another TnySendQueue inside a TnySendQueue implementation. It's for example possible to have multiple transport methods in one TnySendQueue and depending on certain conditions you are going to pick one of those to make the delivery of the message happen.

static TnySendQueue *queue = NULL;
static TnyAccount *account = ...;

static void
send_message (TnyMsg *msg)
{
	if (!queue)
	    queue = tny_camel_send_queue_new (account);
	tny_send_queue_add (queue, msg);
}

Getting the outbox and sentbox folders

static void
some_routine (void)
{
	TnyFolder *outbox = tny_send_queue_get_outbox (queue);
	TnyFolder *sentbox = tny_send_queue_get_sentbox (queue);

	...

	g_object_unref (outbox);
	g_object_unref (sentbox);
}

Implementing your own TnySendQueue

You are advised to take a look at TnyCamelSendQueue for an example of a TnySendQueue implementation.

Running the usual tool

../tools/gtypeinterface-h-files-to-c-file.pl \
	TnyMySendQueue tny-send-queue.h > \
	tny-my-send-queue.c

Will give you something like this (tny-my-send-queue.c)

/* Your copyright here */

#include <config.h>
#include <glib.h>
#include <glib/gi18n-lib.h>

#include <tny-my-send-queue.h>
#include "tny-my-send-queue-priv.h"

static GObjectClass *parent_class = NULL;

static void
tny_my_send_queue_msg_sent (TnySendQueue *self, TnyMsg *msg, guint nth, guint total)
{
}

static void
tny_my_send_queue_error_happened (TnySendQueue *self, TnyMsg *msg, guint nth, guint total)
{
}

static void
tny_my_send_queue_add (TnySendQueue *self, TnyMsg *msg, GError **err)
{
}

static TnyFolder*
tny_my_send_queue_get_sentbox (TnySendQueue *self)
{
        return tny_folder_new ()
}

static TnyFolder*
tny_my_send_queue_get_outbox (TnySendQueue *self)
{
        return tny_folder_new ()
}

static void
tny_my_send_queue_finalize (GObject *object)
{
        parent_class->finalize (object);
}

static void
tny_send_queue_init (TnySendQueueIface *klass)
{
        klass->msg_sent = tny_my_send_queue_msg_sent;
        klass->error_happened = tny_my_send_queue_error_happened;
        klass->add_func = tny_my_send_queue_add;
        klass->get_sentbox_func = tny_my_send_queue_get_sentbox;
        klass->get_outbox_func = tny_my_send_queue_get_outbox;
}

static void
tny_my_send_queue_class_init (TnyMySendQueueClass *klass)
{
        GObjectClass *object_class;

        parent_class = g_type_class_peek_parent (klass);
        object_class = (GObjectClass*) klass;
        object_class->finalize = tny_my_send_queue_finalize;
}
GType
tny_my_send_queue_get_type (void)
{
        static GType type = 0;
        if (G_UNLIKELY(type == 0))
        {
                static const GTypeInfo info = 
                {
                        sizeof (TnyMySendQueueClass),
                        NULL,   /* base_init */
                        NULL,   /* base_finalize */
                        (GClassInitFunc) tny_my_send_queue_class_init,   /* class_init */
                        NULL,   /* class_finalize */
                        NULL,   /* class_data */
                        sizeof (TnyMySendQueue),
                        0,      /* n_preallocs */
                        tny_my_send_queue_instance_init,    /* instance_init */
                        NULL
                };


                static const GInterfaceInfo tny_send_queue_info = 
                {
                        (GInterfaceInitFunc) tny_send_queue_init, /* interface_init */
                        NULL,         /* interface_finalize */
                        NULL          /* interface_data */
                }

                type = g_type_register_static (G_TYPE_OBJECT,
                        "TnyMySendQueue",
                        &info, 0);

                g_type_add_interface_static (type, TNY_SEND_QUEUE,
                        &tny_send_queue_info

        }
        return type;
}

Removing the signal signatures

static void
tny_send_queue_init (TnySendQueueIface *klass)
{
-       klass->msg_sent = tny_my_send_queue_msg_sent;
-       klass->error_happened = tny_my_send_queue_error_happened;
        klass->add_func = tny_my_send_queue_add;
        klass->get_sentbox_func = tny_my_send_queue_get_sentbox;
        klass->get_outbox_func = tny_my_send_queue_get_outbox;
}

Implementing the methods

tny_my_send_queue_add

In this method the message is added to the queue. It should probably be appended to the outbox so that your worker thread can start processing it from there.

static void
tny_my_send_queue_add (TnySendQueue *self, TnyMsg *msg, GError **err)
{
	g_mutex_lock (todo_lock);
	{
		TnyFolder *outbox;
		TnyList *headers = tny_simple_list_new ();
		outbox = tny_send_queue_get_outbox (self);
		tny_folder_get_headers (outbox, headers, TRUE, NULL);
		priv->total = tny_list_get_length (headers);
		g_object_unref (headers);
		tny_folder_add_msg (outbox, msg, NULL);
		priv->total++;
		if (priv->total >= 1)
			create_worker (self);
		g_object_unref (outbox);
	}
	g_mutex_unlock (todo_lock);
}

tny_my_send_queue_get_sentbox

the sentbox must be a TnyFolder in which the been-sent messages will be stored

static TnyFolder*
tny_my_send_queue_get_sentbox (TnySendQueue *self)
{
        return tny_my_sentbox_folder_new ()
}

tny_my_send_queue_get_outbox

The outbox must be a TnyFolder in which the outgoing messages will be stored

static TnyFolder*
tny_my_send_queue_get_outbox (TnySendQueue *self)
{
        return tny_my_outbox_folder_new ()
}

The public constructor

/**
 * tny_my_send_queue_new:
 * @...
 *
 * Return value: A new #TnySendQueue instance implemented for my platform
 **/
TnySendQueue*
tny_my_send_queue_new (...)
{
	TnyMySendQueue *self = g_object_new (TNY_TYPE_MY_SEND_QUEUE, NULL);
	
	g_mutex_lock (todo_lock);
	{
		TnyFolder *outbox;
		TnyList *headers = tny_simple_list_new ();

		outbox = tny_send_queue_get_outbox (TNY_SEND_QUEUE (self));
		tny_folder_get_headers (outbox, headers, TRUE, NULL);
		priv->total = tny_list_get_length (headers);
		g_object_unref (headers);
		if (priv->total >= 1)
			create_worker (TNY_SEND_QUEUE (self));
		g_object_unref (outbox);
	}
	g_mutex_unlock (todo_lock);

	return TNY_SEND_QUEUE (self);
}

The public .h file (tny-my-send-queue.h)

#ifndef TNY_MY_SEND_QUEUE_H
#define TNY_MY_SEND_QUEUE_H

/* Your copyright */

#include <glib.h>
#include <glib-object.h>

#include <tny-send-queue.h>
#include <tny-msg.h>

G_BEGIN_DECLS

#define TNY_TYPE_MY_SEND_QUEUE \
	(tny_my_send_queue_get_type ())
#define TNY_MY_SEND_QUEUE(obj) \
	(G_TYPE_CHECK_INSTANCE_CAST ((obj), TNY_TYPE_MY_SEND_QUEUE, TnyMySendQueue))
#define TNY_MY_SEND_QUEUE_CLASS(vtable) \
	(G_TYPE_CHECK_CLASS_CAST ((vtable), TNY_TYPE_MY_SEND_QUEUE, TnyMySendQueueClass))
#define TNY_IS_MY_SEND_QUEUE(obj) \
	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), TNY_TYPE_MY_SEND_QUEUE))
#define TNY_IS_MY_SEND_QUEUE_CLASS(vtable) \
	(G_TYPE_CHECK_CLASS_TYPE ((vtable), TNY_TYPE_MY_SEND_QUEUE))
#define TNY_MY_SEND_QUEUE_GET_CLASS(inst) \
	(G_TYPE_INSTANCE_GET_CLASS ((inst), TNY_TYPE_MY_SEND_QUEUE, TnyMySendQueueClass))

typedef struct _TnyMySendQueue TnyMySendQueue;
typedef struct _TnyMySendQueueClass TnyMySendQueueClass;

struct _TnyMySendQueue
{
	GObject parent;
};

struct _TnyMySendQueueClass 
{
	GObjectClass parent;
};

GType tny_my_send_queue_get_type (void);

TnySendQueue* tny_my_send_queue_new (...);

G_END_DECLS

#endif

Adjusting Makefile.am

libtinymail_my_1_0_headers = \
+       tny-my-send-queue.h \
        tny-my-password-dialog.h \
        tny-my-device.h \
        tny-my-platform-factory.h

libtinymail_my_1_0_la_SOURCES = \
        $(libtinymail_my_1_0_headers) \
+       tny-my-send-queue.c \
        tny-my-device-priv.h \
        tny-my-device.c \
        tny-my-password-dialog.c \
        tny-my-platform-factory.c

References to related other documentation