trusty (3) mapistore-documentation.3.gz

Provided by: libmapi-dev_2.0-3_amd64 bug

NAME

       mapistore-documentation - .PP

Contents

       • Revision History
       • 1. Introduction

         • 1.1. Purpose and Scope
         • 1.2. General Overview
       • 2. Technical / MAPI Considerations

         • 2.1. MAPI objects
         • 2.2. MAPI tables
         • 2.3. MAPI properties and mapping
         • 2.4. Named properties vs known properties
       • 3. MAPIStore architecture

         • 3.1. INTERFACE layer
         • 3.2. PROCESSING layer
         • 3.3. BACKENDS layer
         • 3.4. Relationship to OpenChange Dispatcher database
         • 3.5. Object mapping
       • 4. MAPIStore API

         • 4.1. Initialization
         • 4.2. Backend contexts
       • 5. FSOCPF backend

         • 5.1. Definition
         • 5.2. Namespace and Attributes
         • 5.3. Overview
         • 5.4. Documentation and References
   Revision History
       Date Revision Number Author Revision Content  21/05/10 0.3 Julien Kerihuel Add API documentation for
       initialization, backend connection contexts and add programming samples  21/05/10 0.2 Julien Kerihuel
       Merge initial Wiki document and add FSOCPF section  20/05/10 0.1 Julien Kerihuel Draft document.
   1. Introduction
   1.1. Purpose and Scope
       MAPIStore is the SAL component of OpenChange server. SAL stands for Storage Abstraction Layer. It is the
       component used by OpenChange Server to push/get information (messages, folders) to/from storage backends.
       The following document intends to describe the overall/theoretical SAL behavior and constraints we need
       to consider when dealing with MAPI/EMSMDB. It also describes the semantics and inner working of its
       storage backends.
   1.2. General overview
       The main objective of mapistore is to provide an interface layer with a common set of atomic functions
       (operations) used to trigger and dispatch data and commands to the appropriate backend. MAPIStore relies
       on a backend mechanism specifically designed to transparently handle some of the MAPI semantics required
       by any Exchange compatible server.
       The initial idea was to provide to OpenChange a highly customizable storage backend mechanism which would
       fit in any situation and any environments. One of the greatest limitation we have found with existing
       groupware is the storage layer which is generally limited to a single solution, service or format and is
       neither scalable nor modifiable when user requirements evolve upon time.
       MAPIStore solves this problem and go beyond classical limitations. It is not a revolutionary concept, but
       the way openchange uses it makes the whole difference and offer administrators an innovative way to
       customize storage.
       MAPIStore allows you to:
       • use a different backend for any top-foldertransparently move/copy data across backends
       • develop new backends quickly
       • access all the different backends through an unique API
       For example (assuming all associated backends were developed) a user could have the following storage
       organization for his mailbox:
       • Mails stored using an IMAP backend (Cyrus-IMAP or dovecot)
       • Calendar items stored in CalDAV or pushed in Google calendar
       • Sent emails and archives/backup stored in a compression backend
       • Tasks stored in a MySQL database
       • Notes stored on the filesystem
       If the user is not satisfied with one of the backend's performance, they would just have to use an
       administration tool, change the backend, wait for the replication, synchronization to finish and there
       data will be available from the new backend.
       Information can be completely decentralized, stored on one of several servers and still be accessible
       transparently from OpenChange server.
   2. Technical / MAPI Considerations
   2.1. MAPI objects
       An object is a physical (message, folder) or temporary (table) but generic entity which can be
       represented as a 2 columns fixed array with n rows, where each row contains a property of that entity.
       One column repesents the property tags (names), and the other represents the property value (or values).
       From a MAPI perspective (network layer), opening an object means:
       • opening either a private mailbox or public folder store. These are referenced by EssDN and not IDs
       • opening a message given its PR_MID (Message identifier)
       • opening a folder/container given its PR_FID (Folder identifier)
   2.2. MAPI tables
       Another category of MAPI objects are tables (also known as views) created with MAPI ROPs such as
       GetContentsTable, GetHierarchyTable, GetAttachmentTable or GetRulesTable. Views are temporary
       representation of the elements that a container holds at a specific moment. It can be represented as a
       list of n rows (elements) with n columns (property values):
       • GetContentsTables creates a view listing all messages available within a container
       • GetHierarchyTable creates a view listing all containers within a container
       • GetAttachmentTable creates a view listing all the attachment of a message
       • GetRulesTable creates a view listing all permissions associated to a container
       Tables are customized through the SetColumns MAPI ROP which will define the property identifiers to be
       retrieved. The QueryRows MAPI ROP can then be called to sequentially retrieve rows and associated
       property values. A table is similar to a MAPI object except it is virtual, created on demand to represent
       a list of objects rather than a unique object.
   2.3. MAPI properties and mapping
       There is a large set of fixed and known MAPI properties available. If appropriate backends are developed,
       there can be a 1-1 mapping between MAPI properties and backend properties for some of them. This mapping
       may even be enough for common purposes. However there will still be a set of MAPI properties which won't
       fit in this process.
       There are different way to workaround this issue. For example, Kolab is using a Cyrus/IMAP backend and
       associate/store MAPI properties to the message using ANNOTATE-MORE within a XML file format.
       OpenChange provides similar mechanism with its OCPF file format.
   2.4. Named properties vs known properties
       OpenChange server needs to support named properties. An initial set of named properties can be defined at
       provisioning time, but this list must not be static. Users must be able to add, delete, change new
       entries and create their own set of custom named properties as required.
   3. MAPIStore Architecture
       Given that objects representation are similar to SQL database records, an architecture like the sqlite
       one makes sense for our purpose:
       Component Brief description  INTERFACE convenient top-level functions (Public API) accessed through
       EMSMDB providers  PROCESSING set of atomic operations (open, read, write, close, mkdir, rmdir etc.)
       BACKENDS The code which does things in the specified storage system (mysql, fsocpf, imap etc.)
   3.1. INTERFACE layer
       The interface layer doesn't have any knowledge about mapistore internals, how or where objects are
       stored. The interface uses MAPI data structure, supplies PR_FID and PR_MID values and assumes the
       interface layer will return a pack of data it can directly use without further or significant
       modifications. The interface layer functions should also have (as a parameter) a pointer to a mapistore
       context with private/opaque set of information (void *) about the object.
   3.2. PROCESSING layer
       The processing layer is responsible for:
       • mapping OpenChange objects identifiers (PR_FID, PR_MID) to unique backends object identifiers (on
         purpose, depending on the kind of backend).
       • format input/output data: glue between INTERFACE and BACKENDS
       • relay input requests to the correct backend through atomic operations
       • maintain mapistore's integrity
   3.3. BACKENDS layer
       The backends layer has a list of modules identified at mapistore initialization and available across user
       sessions, which means unique initialization at server start-up. Each module is a backend (fsocpf, sqlite,
       imap, etc.) and similarly to many other openchange components is loaded as a DSO object (dynamic shared
       object)
   3.4. Relationship to OpenChange Dispatcher database
       MAPIStore and the openchange 'dispatcher' database (openchange.ldb) are completely unrelated. MAPIStore
       is a standalone API and developers can use it independently from OpenChange server.
       However, the mapistore API has initially been designed to be used by OpenChange server, and OpenChange
       server is using a tiny indexing database which describes user mailboxes top level containers. In
       openchange.ldb the mapistore_uri attribute is attached to top level containers and its value points to a
       valid mapistore URI (namespace + path). Note that a single user can have several different types of
       mapistore databases in use (one for each of the top level containers).
       The is the only relationship between the database and the store: The database points to store locations.
   3.5. Object mapping
       MAPIStore needs to maintain a hash database linking unique OpenChange identifiers to unique backend
       identifiers. This hash table can be stored within a TDB database.
       MAPIStore is responsible for managing IDs mapping between OpenChange objects and backend specific
       objects. It maintains a list of free identifiers which it reallocates on demand whenever a backend needs
       (mapistore_register_id()) one or where it wants to release one (mapistore_unregister_id()).
   4. MAPIStore API
       MAPIStore relies on the talloc library for memory allocation.
   4.1. Initialization
       If there was a 'hello mapistore' program, it would only require to make 2 calls:
       • mapistore_init:
         The initialization routine initializes the mapistore general context used along all mapistore calls,
         the mapping context databases (described below) and finally load all the backends available (DSO). When
         this operation is successful, developers are ready to make mapistore calls.
       • mapistore_release:
         The release operation uninitializes the mapistore general context, closes connections to database and
         frees the remaining allocated memory.
       mapistore_sample1.c
       #include <mapistore/mapistore.h>

       int main(int ac, const char *av[])
       {
               TALLOC_CTX                      *mem_ctx;
               struct mapistore_context        *mstore_ctx;
               int                             retval;

               /* Step 1. Create the talloc memory context */
               mem_ctx = talloc_named(NULL, 0, 'mapistore_sample1');

               /* Step 2. Initialize mapistore system */
               mstore_ctx = mapistore_init(mem_ctx, NULL);
               if (!mstore_ctx) {
                       exit (1);
               }

               /* Step 3. Uninitialize mapistore system */
               retval = mapistore_release(mstore_ctx);
               if (retval != MAPISTORE_SUCCESS) {
                       exit (1);
               }

               return 0;
       }

       $ export PKG_CONFIG_PATH=/usr/local/samba/lib/pkgconfig
       $ gcc mapistore_sample1.c -o mapistore_sample1 `pkg-config --cflags --libs libmapistore`
       $ ./mapistore_sample1
       $
   4.2. Backend contexts
       MAPIStore registers and loads its backends upon initialization. It means they are only
       instantiated/initialized one time during the whole server lifetime and the same code is used for all
       users and all mapistore folders.
       These backend contexts (or connection contexts) are identified by a context id, which is an unsigned 32
       bit integer which references the context during its lifetime. If OpenChange is used in a very large
       environment with many top folders (which implies the same number of mapistore contexts), or if OpenChange
       server has an incredibly long uptime, it would be possible to run out of available context identifiers.
       In order to prevent this situation from happening, mapistore implements context databases where it stores
       available/free/used context identifiers:
       • mapistore_id_mapping_used.tdb: TDB database with used IDs
       • mapistore_id_mapping_free.tdb: TDB database with available pool of IDs
       MAPIStore provides a convenient set of functions to manage backend contexts:
       • mapistore_set_mapping_path: Defines the path where context databases are stored. Call to this function
         is optional and default path would be used instead. However if a call to this function has to be made,
         it must be done before any call to mapistore (even mapistore_init).
       • mapistore_add_context: Add a new connection context to mapistore
       • mapistore_del_context: Delete a connection context from mapistore
       mapistore_sample2.c:
       #include <mapistore/mapistore.h>

       int main(int ac, const char *av[])
       {
               TALLOC_CTX                      *mem_ctx;
               struct mapistore_context        *mstore_ctx;
               int                             retval;
               uint32_t                        context_id = 0;
               uint32_t                        context_id2 = 0;

               /* Step 1. Create the talloc memory context */
               mem_ctx = talloc_named(NULL, 0, 'mapistore_sample1');

               /* Step 2. Set the mapping path to /tmp */
               retval = mapistore_set_mapping_path('/tmp');
               if (retval != MAPISTORE_SUCCESS) {
                       exit (1);
               }

               /* Step 3. Initialize mapistore system */
               mstore_ctx = mapistore_init(mem_ctx, NULL);
               if (!mstore_ctx) {
                       exit (1);
               }

               /* Step 4. Add connection contexts */
               retval = mapistore_add_context(mstore_ctx, 'fsocpf:///tmp/Inbox', &context_id);
               if (retval != MAPISTORE_SUCCESS) {
                       exit (1);
               }

               retval = mapistore_add_context(mstore_ctx, 'fsocpf:///tmp/Sent Items', &context_id2);
               if (retval != MAPISTORE_SUCCESS) {
                       exit (1);
               }

               /* Step 5. Release connection contexts */
               retval = mapistore_del_context(mstore_ctx, context_id);
               retval = mapistore_del_context(mstore_ctx, context_id2);

               /* Step 6. Uninitialize mapistore system */
               retval = mapistore_release(mstore_ctx);
               if (retval != MAPISTORE_SUCCESS) {
                       exit (1);
               }

               return 0;
       }

       $ ./mapistore_sample2
       sqlite3 backend initialized
       fsocpf backend initialized
       namespace is fsocpf:// and backend_uri is '/tmp/Inbox'
       [fsocpf_create_context:49]
       namespace is fsocpf:// and backend_uri is '/tmp/Sent Items'
       [fsocpf_create_context:49]
       $
   5. FSOCPF Backend
   5.1. Definition
       FSOCPF stands for FileSystem and OpenChange Property Files. It is a backend designed to help developers
       testing OpenChange server code easily. The main idea is to have a backend we can manipulate, analyze and
       modify from the Linux console without having to develop a specific tool. This backend uses the UNIX
       filesystem for folder semantics and the OCPF file format as a way to store MAPI objects easily.
   5.2. Namespace and Attributes
       The namespace for this backend is:
       fsocpf://

       The mapistore_uri attribute for the folder definition in openchange.ldb must be a valid path where the
       last part of the URI is the FSOCPF container folder to create on the filesystem.
   5.3. Overview
       [+] Private user storage space
        |
        +-[+] Top-MAPIStore folder (Inbox)
           |
           +-[+] 0xf1000001 (mapistore folder1)
           |  |
           |  +-[+] .properties (OCPF)
           |  |
           |  +-[+] 0xe10000001.ocpf (message - OCPF)
           |  |
           |  +-[+] 0xe10000001 (attachment folder)
           |     |
           |     +-[+] 1.ocpf (PR_ATTACH_NUM)
           |     |
           |     +-[+] 1.data (attachment / stream data)
           |
           +-[+] 0xf2000001 (mapistore folder2)
              |
              +-[+] .properties (OCPF)
       The figure above exposes the storage architecture of the FSOCPF backend using a real-world example. In
       this use case, we have decided to associate the FSOCPF backend to the Inbox folder. It means that any
       folder created under Inbox or any message stored within Inbox at any level of depth is stored and
       retrieved from the path defined in openchange.ldb.
       In openchange.ldb, the mapistore_uri attribute of the Inbox record points to:
       fsocpf://Private user storage space/Inbox

        where Private user storage space can for example be
       /usr/local/samba/private/mapistore/$username

       Under Inbox, we have created 2 folders:
       • 0xf1000001 folder1
       • 0xf2000001 folder2
       These folders are identified/named using their FID. Since they are classical filesystem folders, we can't
       associate attributes to them such as a folder comment, or the container class. All these attributes with
       the displayable name are stored into the .properties file within the folder.
       Inside 0xf1000001 folder, we have 1 message named 0xe10000001.ocpf stored in the OCPF file format
       (property/value pair). Any properties associated to the message (subject, recipient, body) are stored
       within this file.
       This message also has attachments. Attachments are stored within a directory at the same level named
       0xe10000001 (message name without OCPF extension). Within this directory, we find the attachments named
       using the PR_ATTACH_NUM property value and the OCPF file extension. The content of the attachment is
       stored in $PR_ATTACH_NUM.data - in this case 1.data.
   5.4. Documentation and References
       • OpenChange Property File format (OCPF) documentation