res_fax.c: Add chan locked precondition comments.
[asterisk/asterisk.git] / res / res_calendar_ews.c
index 17723b5..05133a2 100644 (file)
  */
 
 /*** MODULEINFO
-       <depend>neon</depend>
+       <depend>neon29</depend>
+       <support_level>core</support_level>
 ***/
+
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ASTERISK_REGISTER_FILE()
 
 #include <ne_request.h>
 #include <ne_session.h>
@@ -35,8 +37,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <ne_xml.h>
 #include <ne_xmlreq.h>
 #include <ne_utils.h>
+#include <ne_redirect.h>
 
 #include "asterisk/module.h"
+#include "asterisk/channel.h"
 #include "asterisk/calendar.h"
 #include "asterisk/lock.h"
 #include "asterisk/config.h"
@@ -77,7 +81,9 @@ struct xml_context {
 
 /* Important states of XML parsing */
 enum {
+       XML_EVENT_CALENDAR_ITEM = 9,
        XML_EVENT_NAME = 10,
+       XML_EVENT_DESCRIPTION,
        XML_EVENT_START,
        XML_EVENT_END,
        XML_EVENT_BUSY,
@@ -87,6 +93,9 @@ enum {
        XML_EVENT_ATTENDEE,
        XML_EVENT_MAILBOX,
        XML_EVENT_EMAIL_ADDRESS,
+       XML_EVENT_CATEGORIES,
+       XML_EVENT_CATEGORY,
+       XML_EVENT_IMPORTANCE,
 };
 
 struct ewscal_pvt {
@@ -99,6 +108,7 @@ struct ewscal_pvt {
        ne_uri uri;
        ne_session *session;
        struct ao2_container *events;
+       unsigned int items;
 };
 
 static void ewscal_destructor(void *obj)
@@ -166,14 +176,14 @@ static int startelm(void *userdata, int parent, const char *nspace, const char *
 {
        struct xml_context *ctx = userdata;
 
-       ast_debug(3, "EWS: XML: Start: %s\n", name);
+       ast_debug(5, "EWS: XML: Start: %s\n", name);
        if (ctx->op == XML_OP_CREATE) {
                return NE_XML_DECLINE;
        }
 
        /* Nodes needed for traversing until CalendarItem is found */
        if (!strcmp(name, "Envelope") ||
-               !strcmp(name, "Body") ||
+               (!strcmp(name, "Body") && parent != XML_EVENT_CALENDAR_ITEM) ||
                !strcmp(name, "FindItemResponse") ||
                !strcmp(name, "GetItemResponse") ||
                !strcmp(name, "CreateItemResponse") ||
@@ -184,18 +194,20 @@ static int startelm(void *userdata, int parent, const char *nspace, const char *
                return 1;
        } else if (!strcmp(name, "RootFolder")) {
                /* Get number of events */
-               int items;
+               unsigned int items;
 
                ast_debug(3, "EWS: XML: <RootFolder>\n");
-               if (sscanf(ne_xml_get_attr(ctx->parser, atts, NULL, "TotalItemsInView"), "%d", &items) != 1) {
+               if (sscanf(ne_xml_get_attr(ctx->parser, atts, NULL, "TotalItemsInView"), "%u", &items) != 1) {
                        /* Couldn't read enything */
                        ne_xml_set_error(ctx->parser, "Could't read number of events.");
                        return NE_XML_ABORT;
                }
 
-               ast_debug(3, "EWS: %d calendar items to load\n", items);
+               ast_debug(3, "EWS: %u calendar items to load\n", items);
+               ctx->pvt->items = items;
                if (items < 1) {
                        /* Stop processing XML if there are no events */
+                       ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events);
                        return NE_XML_DECLINE;
                }
                return 1;
@@ -219,12 +231,12 @@ static int startelm(void *userdata, int parent, const char *nspace, const char *
                        return NE_XML_ABORT;
                }
 
-               return 1;
+               return XML_EVENT_CALENDAR_ITEM;
        } else if (!strcmp(name, "ItemId")) {
                /* Event UID */
                if (ctx->op == XML_OP_FIND) {
                        struct calendar_id *id;
-                       if (!(id = ast_calloc(1, sizeof(id)))) {
+                       if (!(id = ast_calloc(1, sizeof(*id)))) {
                                return NE_XML_ABORT;
                        }
                        if (!(id->id = ast_str_create(256))) {
@@ -246,6 +258,13 @@ static int startelm(void *userdata, int parent, const char *nspace, const char *
                }
                ast_str_reset(ctx->cdata);
                return XML_EVENT_NAME;
+       } else if (!strcmp(name, "Body") && parent == XML_EVENT_CALENDAR_ITEM) {
+               /* Event body/description */
+               if (!ctx->cdata) {
+                       return NE_XML_ABORT;
+               }
+               ast_str_reset(ctx->cdata);
+               return XML_EVENT_DESCRIPTION;
        } else if (!strcmp(name, "Start")) {
                /* Event start time */
                return XML_EVENT_START;
@@ -271,6 +290,23 @@ static int startelm(void *userdata, int parent, const char *nspace, const char *
                }
                ast_str_reset(ctx->cdata);
                return XML_EVENT_LOCATION;
+       } else if (!strcmp(name, "Categories")) {
+               /* Event categories */
+               if (!ctx->cdata) {
+                       return NE_XML_ABORT;
+               }
+               ast_str_reset(ctx->cdata);
+               return XML_EVENT_CATEGORIES;
+       } else if (parent == XML_EVENT_CATEGORIES && !strcmp(name, "String")) {
+               /* Event category */
+               return XML_EVENT_CATEGORY;
+       } else if (!strcmp(name, "Importance")) {
+               /* Event importance (priority) */
+               if (!ctx->cdata) {
+                       return NE_XML_ABORT;
+               }
+               ast_str_reset(ctx->cdata);
+               return XML_EVENT_IMPORTANCE;
        } else if (!strcmp(name, "RequiredAttendees") || !strcmp(name, "OptionalAttendees")) {
                return XML_EVENT_ATTENDEE_LIST;
        } else if (!strcmp(name, "Attendee") && parent == XML_EVENT_ATTENDEE_LIST) {
@@ -331,6 +367,13 @@ static int cdata(void *userdata, int state, const char *cdata, size_t len)
                        ctx->event->busy_state = AST_CALENDAR_BS_FREE;
                }
                break;
+       case XML_EVENT_CATEGORY:
+               if (ast_str_strlen(ctx->cdata) == 0) {
+                       ast_str_set(&ctx->cdata, 0, "%s", data);
+               } else {
+                       ast_str_append(&ctx->cdata, 0, ",%s", data);
+               }
+               break;
        default:
                ast_str_append(&ctx->cdata, 0, "%s", data);
        }
@@ -354,6 +397,11 @@ static int endelm(void *userdata, int state, const char *nspace, const char *nam
                ast_string_field_set(ctx->event, summary, ast_str_buffer(ctx->cdata));
                ast_debug(3, "EWS: XML: Summary: %s\n", ctx->event->summary);
                ast_str_reset(ctx->cdata);
+       } else if (!strcmp(name, "Body") && state == XML_EVENT_DESCRIPTION) {
+               /* Event body/description end */
+               ast_string_field_set(ctx->event, description, ast_str_buffer(ctx->cdata));
+               ast_debug(3, "EWS: XML: Description: %s\n", ctx->event->description);
+               ast_str_reset(ctx->cdata);
        } else if (!strcmp(name, "Organizer")) {
                /* Event organizer end */
                ast_string_field_set(ctx->event, organizer, ast_str_buffer(ctx->cdata));
@@ -364,6 +412,22 @@ static int endelm(void *userdata, int state, const char *nspace, const char *nam
                ast_string_field_set(ctx->event, location, ast_str_buffer(ctx->cdata));
                ast_debug(3, "EWS: XML: Location: %s\n", ctx->event->location);
                ast_str_reset(ctx->cdata);
+       } else if (!strcmp(name, "Categories")) {
+               /* Event categories end */
+               ast_string_field_set(ctx->event, categories, ast_str_buffer(ctx->cdata));
+               ast_debug(3, "EWS: XML: Categories: %s\n", ctx->event->categories);
+               ast_str_reset(ctx->cdata);
+       } else if (!strcmp(name, "Importance")) {
+               /* Event importance end */
+               if (!strcmp(ast_str_buffer(ctx->cdata), "Low")) {
+                       ctx->event->priority = 9;
+               } else if (!strcmp(ast_str_buffer(ctx->cdata), "Normal")) {
+                       ctx->event->priority = 5;
+               } else if (!strcmp(ast_str_buffer(ctx->cdata), "High")) {
+                       ctx->event->priority = 1;
+               }
+               ast_debug(3, "EWS: XML: Importance: %s (%d)\n", ast_str_buffer(ctx->cdata), ctx->event->priority);
+               ast_str_reset(ctx->cdata);
        } else if (state == XML_EVENT_EMAIL_ADDRESS) {
                struct ast_calendar_attendee *attendee;
 
@@ -375,6 +439,8 @@ static int endelm(void *userdata, int state, const char *nspace, const char *nam
                if (ast_str_strlen(ctx->cdata)) {
                        attendee->data = ast_strdup(ast_str_buffer(ctx->cdata));
                        AST_LIST_INSERT_TAIL(&ctx->event->attendees, attendee, next);
+               } else {
+                       ast_free(attendee);
                }
                ast_debug(3, "EWS: XML: attendee address '%s'\n", ast_str_buffer(ctx->cdata));
                ast_str_reset(ctx->cdata);
@@ -391,8 +457,11 @@ static int endelm(void *userdata, int state, const char *nspace, const char *nam
                }
        } else if (!strcmp(name, "Envelope")) {
                /* Events end */
-               ast_debug(3, "EWS: XML: All events has been parsed, merging…\n");
-               ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events);
+               ast_debug(3, "EWS: XML: %d of %u event(s) has been parsed…\n", ao2_container_count(ctx->pvt->events), ctx->pvt->items);
+               if (ao2_container_count(ctx->pvt->events) >= ctx->pvt->items) {
+                       ast_debug(3, "EWS: XML: All events has been parsed, merging…\n");
+                       ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events);
+               }
        }
 
        return 0;
@@ -479,7 +548,6 @@ static int send_ews_request_and_parse(struct ast_str *request, struct xml_contex
        if (ret != NE_OK) { /* Error handling */
                ast_log(LOG_WARNING, "Unable to communicate with Exchange Web Service at '%s': %s\n", ctx->pvt->url, ne_get_error(ctx->pvt->session));
                ne_request_destroy(req);
-               ast_free(request);
                ne_xml_destroy(parser);
                return -1;
        }
@@ -501,6 +569,7 @@ static int ewscal_write_event(struct ast_calendar_event *event)
                .pvt = pvt,
        };
        int ret;
+       char *category, *categories;
 
        if (!pvt) {
                return -1;
@@ -531,12 +600,7 @@ static int ewscal_write_event(struct ast_calendar_event *event)
                                                "<End>%s</End>"
                                                "<IsAllDayEvent>false</IsAllDayEvent>"
                                                "<LegacyFreeBusyStatus>%s</LegacyFreeBusyStatus>"
-                                               "<Location>%s</Location>"
-                                       "</t:CalendarItem>"
-                               "</Items>"
-                       "</CreateItem>"
-               "</soap:Body>"
-               "</soap:Envelope>",
+                                               "<Location>%s</Location>",
                event->summary,
                event->description,
                mstime(event->start, start, sizeof(start)),
@@ -544,6 +608,37 @@ static int ewscal_write_event(struct ast_calendar_event *event)
                msstatus(event->busy_state),
                event->location
        );
+       /* Event priority */
+       switch (event->priority) {
+       case 1:
+       case 2:
+       case 3:
+       case 4:
+               ast_str_append(&request, 0, "<Importance>High</Importance>");
+               break;
+       case 5:
+               ast_str_append(&request, 0, "<Importance>Normal</Importance>");
+               break;
+       case 6:
+       case 7:
+       case 8:
+       case 9:
+               ast_str_append(&request, 0, "<Importance>Low</Importance>");
+               break;
+       }
+       /* Event categories*/
+       if (strlen(event->categories) > 0) {
+               ast_str_append(&request, 0, "<Categories>");
+               categories = ast_strdupa(event->categories);    /* Duplicate string, since strsep() is destructive */
+               category = strsep(&categories, ",");
+               while (category != NULL) {
+                       ast_str_append(&request, 0, "<String>%s</String>", category);
+                       category = strsep(&categories, ",");
+               }
+               ast_str_append(&request, 0, "</Categories>");
+       }
+       /* Finish request */
+       ast_str_append(&request, 0, "</t:CalendarItem></Items></CreateItem></soap:Body></soap:Envelope>");
 
        ret = send_ews_request_and_parse(request, &ctx);
 
@@ -763,6 +858,7 @@ static void *ewscal_load_calendar(void *void_data)
        ast_debug(3, "secret            = %s\n", pvt->secret);
 
        pvt->session = ne_session_create(pvt->uri.scheme, pvt->uri.host, pvt->uri.port);
+       ne_redirect_register(pvt->session);
        ne_set_server_auth(pvt->session, auth_credentials, pvt);
        ne_set_useragent(pvt->session, "Asterisk");
 
@@ -811,8 +907,17 @@ static void *ewscal_load_calendar(void *void_data)
 static int load_module(void)
 {
        /* Actualy, 0.29.1 is required (because of NTLM authentication), but this
-        * function does not support matching patch version. */
-       if (ne_version_match(0, 29)) {
+        * function does not support matching patch version.
+        *
+        * The ne_version_match function returns non-zero if the library
+        * version is not of major version major, or the minor version
+        * is less than minor. For neon versions 0.x, every minor
+        * version is assumed to be incompatible with every other minor
+        * version.
+        *
+        * I.e. for version 1.2..1.9 we would do ne_version_match(1, 2)
+        * but for version 0.29 and 0.30 we need two checks. */
+       if (ne_version_match(0, 29) && ne_version_match(0, 30)) {
                ast_log(LOG_ERROR, "Exchange Web Service calendar module require neon >= 0.29.1, but %s is installed.\n", ne_version_string());
                return AST_MODULE_LOAD_DECLINE;
        }
@@ -832,7 +937,9 @@ static int unload_module(void)
        return 0;
 }
 
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Asterisk MS Exchange Web Service Calendar Integration",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk MS Exchange Web Service Calendar Integration",
+       .support_level = AST_MODULE_SUPPORT_CORE,
        .load = load_module,
        .unload = unload_module,
+       .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
 );