Camel.Session

CamelSession is a partially abstract class which defines a context for a given session.

It provides:

  • Service resolution.
  • A base storage location for working data.
  • Interfaces for user communication and queries.
  • Thread management primitives.
  • Interfaces to query for filter driver information.

A given client must subclass CamelSession and provide virtual method overrides for various interfaces in order for any of the Evolution/Camel.Provider plugins to operate. A given client will normally create a system-wide unique singleton instance of this class and from this it will resolve and access any Evolution/Camel.Services.

A subclass should call construct in its new method or constructor, to properly initialise base propreties.

 void camel_session_construct(CamelSession *session, const char *storage_path);

Service resolution

The main application function provided for in CamelSession is service resolution. This will take a given URI string and return an active object representing the URI. How each URI is interpreted depends on the backend Evolution/Camel.Provider driving it; CamelProvider has auto-discovery mechanisms to allow these to be created based on user input.

There are two service types as defined in Evolution/Camel.Provider, and services may be in a disconnected or connected state.

 CamelService *camel_session_get_service(CamelSession *session,
                                         const char *url_string,
                                         CamelProviderType type,
                                         CamelException *ex);
 
 CamelService *camel_session_get_service_connected(CamelSession *session, 
                                                   const char *url_string,
                                                   CamelProviderType type, 
                                                   CamelException *ex);

The utility of get_service_connected is questionable, as all it does it call get_service and then 'connect' it. Note that get_service may also get a connected service, if it already exists.

There are also a couple of helper macros to get specific backend types.

 #define camel_session_get_store(session, url_string, ex) \
        ((CamelStore *)camel_session_get_service_connected(session, url_string, CAMEL_PROVIDER_STORE, ex))
 #define camel_session_get_transport(session, url_string, ex) \
        ((CamelTransport *)camel_session_get_service_connected(session, url_string, CAMEL_PROVIDER_TRANSPORT, ex))

These also have questionable utility.

As far as CamelSession is concerned, the lifecycle of each service is based on reference counting. Internally CamelSession uses a Evolution/Camel.Object#Camel.ObjectBag to manage a cache of active services.

State queries

Now we come to the backend functions. These fall into several basic categories. First, a few functions for the backend implementations to query the current state of the application or other non-user supplied information.

First, a function to get a unique storage location for this service. This will take the base path as passed to construct, and append the protocol a '/', and host:port and/or path information, depending on the provider URL flags. It can also be overriden by the client CamelSession implementation.

 char *camel_session_get_storage_path(CamelSession *session,
                                      CamelService *service,
                                      CamelException *ex);

An important property of the session is whether it is online or not. These functions are used by the client to control the online state, or the client and services to query it. When this is changed, the camel object event "online" will be emitted, with the new online state as the event data.

 gboolean camel_session_is_online(CamelSession *session);
 void camel_session_set_online(CamelSession *session, gboolean online);

Because defining filters requires user input which is out of the scope of Camel, a session query function is used to get an appropriately configured Evolution/Camel.FilterDriver for the given context (type). This can be used by client code, and is used by Evolution/Camel.Folder to perform automatic filtering.

 CamelFilterDriver *camel_session_get_filter_driver(CamelSession *session,
                                                   const char *type,
                                                   CamelException *ex);

The next two functions don't really belong here; junk tests, like filtering, should just be property values on the Evolution/Camel.Service URI, or Evolution/Camel.Folder. They can also be implicitly determined from the Evolution/Camel.Misc#Camel.JunkPlugin member of the CamelSession. Hopefully they will be removed at some point. Perhaps a get_junk_filter method could replace all of it.

 gboolean camel_session_check_junk(CamelSession *session);
 void camel_session_set_check_junk(CamelSession *session, gboolean check_junk);

User interaction

Camel is a system-type library, it has no user-interface components. These functions allow backend code to interact with the user. A client implementation must provide implementations for these methods. Also note that any of these may be called from any thread at any time, so it must also implement its own serialisation mechanism so that the user isn't bombarded with multiple queries or alerts.

First, a password interface. Passwords may need to be forced to be forgotten by the backend code if they fail, otherwise loops can be formed.

If a password is requested and the user cancels the request, then the get_password implementation must set the Evolution/Camel.Exception type to CAMEL_EXCEPTION_USER_CANCEL.

 enum {
        CAMEL_SESSION_PASSWORD_REPROMPT = 1 << 0,
        CAMEL_SESSION_PASSWORD_SECRET = 1 << 2,
        CAMEL_SESSION_PASSWORD_STATIC = 1 << 3,
 };
 
 char *camel_session_get_password(CamelSession *session,
                                  CamelService *service,
                                  const char *domain,
                                  const char *prompt,
                                  const char *item,
                                  guint32 flags,
                                  CamelException *ex);
 
 void camel_session_forget_password(CamelSession *session,
                                    CamelService *service,
                                    const char *domain,
                                    const char *item,
                                    CamelException *ex);

The service, domain and item parameters are used to uniquely identify which resource is being asked for. The item is normally "password", but can be any unique string. The special item popb4smtp_uri is also a possibility, see Evolution/Camel.SASL#Camel.SASLPOPB4SMTP for more information.

And then a user-alert function. This can also be used to ask simple Ok/Cancel questions if cancel is TRUE. If cancel is false, then it is fine to run this alert asynchronously and return immediately otherwise it will need to block the thread for the answer.

 typedef enum {
        CAMEL_SESSION_ALERT_INFO,
        CAMEL_SESSION_ALERT_WARNING,
        CAMEL_SESSION_ALERT_ERROR
 } CamelSessionAlertType;
 
 gboolean camel_session_alert_user(CamelSession *session,
                                   CamelSessionAlertType type,
                                   const char *prompt,
                                   gboolean cancel);

Note that this function is for out of band data or simple queries, it should not be used instead of just passing back an error code or Evolution/Camel.Exception from a method.

Threads

The thread primitives are really an asynchronous message passing interface with processing carried out in worker threads. They are used internally, and by backend code that needs to run work in other threads, asynchronously or synchronously.

Each message created is a stand-alone processing packet. It provides callbacks for the processing element, and a free function.

 struct _CamelSessionThreadOps {
        void (*receive)(CamelSession *session, struct _CamelSessionThreadMsg *m);
        void (*free)(CamelSession *session, struct _CamelSessionThreadMsg *m);
 };

Each message created is based on the EMsg part of the EMsgPort interface in libedataserver. This means while being processed or not in use, messages may be stored in an EDList; or even passed to other EMsgPorts.

 struct _CamelSessionThreadMsg {
        EMsg msg; 
 
        int id;
 
        CamelException ex;
        CamelSessionThreadOps *ops;
        struct _CamelOperation *op;
        CamelSession *session; 
 
        void *data;
 };

Note that data is for the class implementing these methods by subclassing CamelSession, not for application code - they simply prepend this whole structure in their own, and allocate accordingly.

The other fields contain handy data which an implementation should define, and the receive function should use them where appropriate.

The basic functions are very simple and are explained in the API documentation.

 void *camel_session_thread_msg_new(CamelSession *session, CamelSessionThreadOps *ops, unsigned int size);
 void camel_session_thread_msg_free(CamelSession *session, CamelSessionThreadMsg *msg);
 int camel_session_thread_queue(CamelSession *session, CamelSessionThreadMsg *msg, int flags);
 void camel_session_thread_wait(CamelSession *session, int id);

Note that CamelSession provides a pthreads based implementation already, although in Evolution, MailSession provides its own implementation so that it can monitor the Evolution/Camel.Operation and trap the final exception for display to the user; which is something most client implementations will need to do.

Apps/Evolution/Camel.Session (last edited 2013-08-08 22:50:07 by WilliamJonMcCann)