Sat Apr 26 2014 22:03:08

Asterisk developer's documentation


res_calendar_ews.c File Reference

Resource for handling MS Exchange Web Service calendars. More...

#include "asterisk.h"
#include <ne_request.h>
#include <ne_session.h>
#include <ne_uri.h>
#include <ne_socket.h>
#include <ne_auth.h>
#include <ne_xml.h>
#include <ne_xmlreq.h>
#include <ne_utils.h>
#include <ne_redirect.h>
#include "asterisk/module.h"
#include "asterisk/calendar.h"
#include "asterisk/lock.h"
#include "asterisk/config.h"
#include "asterisk/astobj2.h"
Include dependency graph for res_calendar_ews.c:

Go to the source code of this file.

Data Structures

struct  calendar_id
struct  ewscal_pvt
struct  xml_context::ids
struct  xml_context

Enumerations

enum  {
  XML_EVENT_CALENDAR_ITEM = 9, XML_EVENT_NAME = 10, XML_EVENT_DESCRIPTION, XML_EVENT_START,
  XML_EVENT_END, XML_EVENT_BUSY, XML_EVENT_ORGANIZER, XML_EVENT_LOCATION,
  XML_EVENT_ATTENDEE_LIST, XML_EVENT_ATTENDEE, XML_EVENT_MAILBOX, XML_EVENT_EMAIL_ADDRESS,
  XML_EVENT_CATEGORIES, XML_EVENT_CATEGORY, XML_EVENT_IMPORTANCE
}
enum  xml_op { XML_OP_FIND = 100, XML_OP_GET, XML_OP_CREATE }

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int auth_credentials (void *userdata, const char *realm, int attempts, char *username, char *secret)
static int cdata (void *userdata, int state, const char *cdata, size_t len)
static int endelm (void *userdata, int state, const char *nspace, const char *name)
static void ewscal_destructor (void *obj)
static void * ewscal_load_calendar (void *data)
static int ewscal_write_event (struct ast_calendar_event *event)
static struct calendar_idget_ewscal_ids_for (struct ewscal_pvt *pvt)
static const char * get_soap_action (enum xml_op op)
static int load_module (void)
static const char * msstatus (enum ast_calendar_busy_state state)
static const char * mstime (time_t t, char *buf, size_t buflen)
static time_t mstime_to_time_t (char *mstime)
static int parse_ewscal_id (struct ewscal_pvt *pvt, const char *id)
static int send_ews_request_and_parse (struct ast_str *request, struct xml_context *ctx)
static int ssl_verify (void *userdata, int failures, const ne_ssl_certificate *cert)
static int startelm (void *userdata, int parent, const char *nspace, const char *name, const char **atts)
static int unload_module (void)
static void * unref_ewscal (void *obj)
static int update_ewscal (struct ewscal_pvt *pvt)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Asterisk MS Exchange Web Service Calendar Integration" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PLUGIN, }
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_calendar_tech ewscal_tech

Detailed Description

Resource for handling MS Exchange Web Service calendars.

Definition in file res_calendar_ews.c.


Enumeration Type Documentation

anonymous enum
Enumerator:
XML_EVENT_CALENDAR_ITEM 
XML_EVENT_NAME 
XML_EVENT_DESCRIPTION 
XML_EVENT_START 
XML_EVENT_END 
XML_EVENT_BUSY 
XML_EVENT_ORGANIZER 
XML_EVENT_LOCATION 
XML_EVENT_ATTENDEE_LIST 
XML_EVENT_ATTENDEE 
XML_EVENT_MAILBOX 
XML_EVENT_EMAIL_ADDRESS 
XML_EVENT_CATEGORIES 
XML_EVENT_CATEGORY 
XML_EVENT_IMPORTANCE 

Definition at line 82 of file res_calendar_ews.c.

enum xml_op
Enumerator:
XML_OP_FIND 
XML_OP_GET 
XML_OP_CREATE 

Definition at line 61 of file res_calendar_ews.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 934 of file res_calendar_ews.c.

static void __unreg_module ( void  ) [static]

Definition at line 934 of file res_calendar_ews.c.

static int auth_credentials ( void *  userdata,
const char *  realm,
int  attempts,
char *  username,
char *  secret 
) [static]

Definition at line 137 of file res_calendar_ews.c.

References ast_log(), LOG_WARNING, ast_calendar::name, ewscal_pvt::owner, ewscal_pvt::secret, and ewscal_pvt::user.

Referenced by ewscal_load_calendar().

{
   struct ewscal_pvt *pvt = userdata;

   if (attempts > 1) {
      ast_log(LOG_WARNING, "Invalid username or password for Exchange Web Service calendar '%s'\n", pvt->owner->name);
      return -1;
   }

   ne_strnzcpy(username, pvt->user, NE_ABUFSIZ);
   ne_strnzcpy(secret, pvt->secret, NE_ABUFSIZ);

   return 0;
}
static int cdata ( void *  userdata,
int  state,
const char *  cdata,
size_t  len 
) [static]

Definition at line 326 of file res_calendar_ews.c.

References AST_CALENDAR_BS_BUSY, AST_CALENDAR_BS_BUSY_TENTATIVE, AST_CALENDAR_BS_FREE, ast_copy_string(), ast_debug, ast_log(), ast_str_append(), ast_str_buffer(), ast_str_set(), ast_str_strlen(), ast_calendar_event::busy_state, xml_context::cdata, ast_calendar_event::end, xml_context::event, LOG_ERROR, mstime_to_time_t(), ast_calendar_event::start, XML_EVENT_BUSY, XML_EVENT_CATEGORY, XML_EVENT_END, XML_EVENT_START, and XML_OP_CREATE.

{
   struct xml_context *ctx = userdata;
   char data[len + 1];

   /* !!! DON'T USE AST_STRING_FIELD FUNCTIONS HERE, JUST COLLECT CTX->CDATA !!! */
   if (state < XML_EVENT_NAME || ctx->op == XML_OP_CREATE) {
      return 0;
   }

   if (!ctx->event) {
      ast_log(LOG_ERROR, "Parsing event data, but event object does not exist!\n");
      return 1;
   }

   if (!ctx->cdata) {
      ast_log(LOG_ERROR, "String for storing CDATA is unitialized!\n");
      return 1;
   }

   ast_copy_string(data, cdata, len + 1);

   switch (state) {
   case XML_EVENT_START:
      ctx->event->start = mstime_to_time_t(data);
      break;
   case XML_EVENT_END:
      ctx->event->end = mstime_to_time_t(data);
      break;
   case XML_EVENT_BUSY:
      if (!strcmp(data, "Busy") || !strcmp(data, "OOF")) {
         ast_debug(3, "EWS: XML: Busy: yes\n");
         ctx->event->busy_state = AST_CALENDAR_BS_BUSY;
      }
      else if (!strcmp(data, "Tentative")) {
         ast_debug(3, "EWS: XML: Busy: tentative\n");
         ctx->event->busy_state = AST_CALENDAR_BS_BUSY_TENTATIVE;
      }
      else {
         ast_debug(3, "EWS: XML: Busy: no\n");
         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);
   }

   ast_debug(5, "EWS: XML: CDATA: %s\n", ast_str_buffer(ctx->cdata));

   return 0;
}
static int endelm ( void *  userdata,
int  state,
const char *  nspace,
const char *  name 
) [static]

Definition at line 385 of file res_calendar_ews.c.

References ao2_container_count(), ao2_link, ast_calendar_merge_events(), ast_calendar_unref_event(), ast_calloc, ast_debug, ast_free, AST_LIST_INSERT_TAIL, ast_log(), ast_str_buffer(), ast_str_reset(), ast_str_strlen(), ast_strdup, ast_string_field_set, ast_calendar_event::attendees, ast_calendar_event::categories, xml_context::cdata, ast_calendar_attendee::data, ast_calendar_event::description, xml_context::event, ewscal_pvt::events, ewscal_pvt::items, ast_calendar_event::location, LOG_ERROR, xml_context::op, ast_calendar_event::organizer, ewscal_pvt::owner, ast_calendar_event::priority, xml_context::pvt, ast_calendar_event::summary, XML_EVENT_DESCRIPTION, XML_EVENT_EMAIL_ADDRESS, XML_OP_CREATE, and XML_OP_FIND.

Referenced by send_ews_request_and_parse().

{
   struct xml_context *ctx = userdata;

   ast_debug(5, "EWS: XML: End:   %s\n", name);
   if (ctx->op == XML_OP_FIND || ctx->op == XML_OP_CREATE) {
      return NE_XML_DECLINE;
   }

   if (!strcmp(name, "Subject")) {
      /* Event name end*/
      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));
      ast_debug(3, "EWS: XML: Organizer: %s\n", ctx->event->organizer);
      ast_str_reset(ctx->cdata);
   } else if (!strcmp(name, "Location")) {
      /* Event location end */
      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;

      if (!(attendee = ast_calloc(1, sizeof(*attendee)))) {
         ctx->event = ast_calendar_unref_event(ctx->event);
         return  1;
      }

      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);
   } else if (!strcmp(name, "CalendarItem")) {
      /* Event end */
      ast_debug(3, "EWS: XML: </CalendarItem>\n");
      ast_free(ctx->cdata);
      if (ctx->event) {
         ao2_link(ctx->pvt->events, ctx->event);
         ctx->event = ast_calendar_unref_event(ctx->event);
      } else {
         ast_log(LOG_ERROR, "Event data ended in XML, but event object does not exist!\n");
         return 1;
      }
   } else if (!strcmp(name, "Envelope")) {
      /* Events end */
      ast_debug(3, "EWS: XML: %d of %d 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;
}
static void ewscal_destructor ( void *  obj) [static]

Definition at line 113 of file res_calendar_ews.c.

References ao2_callback, ao2_ref, ast_debug, ast_string_field_free_memory, ewscal_pvt::events, OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, and ewscal_pvt::session.

Referenced by ewscal_load_calendar().

{
   struct ewscal_pvt *pvt = obj;

   ast_debug(1, "Destroying pvt for Exchange Web Service calendar %s\n", "pvt->owner->name");
   if (pvt->session) {
      ne_session_destroy(pvt->session);
   }
   ast_string_field_free_memory(pvt);

   ao2_callback(pvt->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);

   ao2_ref(pvt->events, -1);
}
static void * ewscal_load_calendar ( void *  data) [static]

Definition at line 769 of file res_calendar_ews.c.

References ao2_alloc, ao2_trylock, ao2_unlock, ast_calendar_config_acquire(), ast_calendar_config_release(), ast_calendar_event_container_alloc(), ast_cond_timedwait, ast_debug, ast_log(), ast_mutex_init, ast_mutex_lock, ast_mutex_unlock, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), ast_tvnow(), ast_variable_browse(), auth_credentials(), ewscal_pvt::events, ewscal_destructor(), LOG_ERROR, LOG_WARNING, ast_variable::name, ast_calendar::name, ast_variable::next, ewscal_pvt::owner, ast_calendar::refresh, refreshlock, ewscal_pvt::secret, secret, ewscal_pvt::session, ssl_verify(), ast_calendar::tech_pvt, ast_calendar::unload, ast_calendar::unloading, unref_ewscal(), update_ewscal(), ewscal_pvt::uri, url, ewscal_pvt::url, ewscal_pvt::user, and ast_variable::value.

{
   struct ewscal_pvt *pvt;
   const struct ast_config *cfg;
   struct ast_variable *v;
   struct ast_calendar *cal = void_data;
   ast_mutex_t refreshlock;

   ast_debug(5, "EWS: ewscal_load_calendar()\n");

   if (!(cal && (cfg = ast_calendar_config_acquire()))) {
      ast_log(LOG_ERROR, "You must enable calendar support for res_ewscal to load\n");
      return NULL;
   }

   if (ao2_trylock(cal)) {
      if (cal->unloading) {
         ast_log(LOG_WARNING, "Unloading module, load_calendar cancelled.\n");
      } else {
         ast_log(LOG_WARNING, "Could not lock calendar, aborting!\n");
      }
      ast_calendar_config_release();
      return NULL;
   }

   if (!(pvt = ao2_alloc(sizeof(*pvt), ewscal_destructor))) {
      ast_log(LOG_ERROR, "Could not allocate ewscal_pvt structure for calendar: %s\n", cal->name);
      ast_calendar_config_release();
      return NULL;
   }

   pvt->owner = cal;

   if (!(pvt->events = ast_calendar_event_container_alloc())) {
      ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name);
      pvt = unref_ewscal(pvt);
      ao2_unlock(cal);
      ast_calendar_config_release();
      return NULL;
   }

   if (ast_string_field_init(pvt, 32)) {
      ast_log(LOG_ERROR, "Couldn't allocate string field space for calendar: %s\n", cal->name);
      pvt = unref_ewscal(pvt);
      ao2_unlock(cal);
      ast_calendar_config_release();
      return NULL;
   }

   for (v = ast_variable_browse(cfg, cal->name); v; v = v->next) {
      if (!strcasecmp(v->name, "url")) {
         ast_string_field_set(pvt, url, v->value);
      } else if (!strcasecmp(v->name, "user")) {
         ast_string_field_set(pvt, user, v->value);
      } else if (!strcasecmp(v->name, "secret")) {
         ast_string_field_set(pvt, secret, v->value);
      }
   }

   ast_calendar_config_release();

   if (ast_strlen_zero(pvt->url)) {
      ast_log(LOG_WARNING, "No URL was specified for Exchange Web Service calendar '%s' - skipping.\n", cal->name);
      pvt = unref_ewscal(pvt);
      ao2_unlock(cal);
      return NULL;
   }

   if (ne_uri_parse(pvt->url, &pvt->uri) || pvt->uri.host == NULL || pvt->uri.path == NULL) {
      ast_log(LOG_WARNING, "Could not parse url '%s' for Exchange Web Service calendar '%s' - skipping.\n", pvt->url, cal->name);
      pvt = unref_ewscal(pvt);
      ao2_unlock(cal);
      return NULL;
   }

   if (pvt->uri.scheme == NULL) {
      pvt->uri.scheme = "http";
   }

   if (pvt->uri.port == 0) {
      pvt->uri.port = ne_uri_defaultport(pvt->uri.scheme);
   }

   ast_debug(3, "ne_uri.scheme   = %s\n", pvt->uri.scheme);
   ast_debug(3, "ne_uri.host  = %s\n", pvt->uri.host);
   ast_debug(3, "ne_uri.port  = %u\n", pvt->uri.port);
   ast_debug(3, "ne_uri.path  = %s\n", pvt->uri.path);
   ast_debug(3, "user      = %s\n", pvt->user);
   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");

   if (!strcasecmp(pvt->uri.scheme, "https")) {
      ne_ssl_trust_default_ca(pvt->session);
      ne_ssl_set_verify(pvt->session, ssl_verify, pvt);
   }

   cal->tech_pvt = pvt;

   ast_mutex_init(&refreshlock);

   /* Load it the first time */
   update_ewscal(pvt);

   ao2_unlock(cal);

   /* The only writing from another thread will be if unload is true */
   for (;;) {
      struct timeval tv = ast_tvnow();
      struct timespec ts = {0,};

      ts.tv_sec = tv.tv_sec + (60 * pvt->owner->refresh);

      ast_mutex_lock(&refreshlock);
      while (!pvt->owner->unloading) {
         if (ast_cond_timedwait(&pvt->owner->unload, &refreshlock, &ts) == ETIMEDOUT) {
            break;
         }
      }
      ast_mutex_unlock(&refreshlock);

      if (pvt->owner->unloading) {
         ast_debug(10, "Skipping refresh since we got a shutdown signal\n");
         return NULL;
      }

      ast_debug(10, "Refreshing after %d minute timeout\n", pvt->owner->refresh);

      update_ewscal(pvt);
   }

   return NULL;
}
static int ewscal_write_event ( struct ast_calendar_event event) [static]

Definition at line 561 of file res_calendar_ews.c.

References ast_free, ast_str_append(), ast_str_create(), ast_str_set(), ast_calendar_event::busy_state, ast_calendar_event::categories, ast_calendar_event::description, ast_calendar_event::end, ast_calendar_event::location, msstatus(), mstime(), xml_context::op, ewscal_pvt::owner, ast_calendar_event::priority, xml_context::pvt, send_ews_request_and_parse(), ast_calendar_event::start, ast_calendar_event::summary, ast_calendar::tech_pvt, and XML_OP_CREATE.

{
   struct ast_str *request;
   struct ewscal_pvt *pvt = event->owner->tech_pvt;
   char start[21], end[21];
   struct xml_context ctx = {
      .op = XML_OP_CREATE,
      .pvt = pvt,
   };
   int ret;
   char *category, *categories;

   if (!pvt) {
      return -1;
   }

   if (!(request = ast_str_create(1024))) {
      return -1;
   }

   ast_str_set(&request, 0,
      "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
         "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
         "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
         "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
         "<soap:Body>"
         "<CreateItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\" "
            "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
            "SendMeetingInvitations=\"SendToNone\" >"
            "<SavedItemFolderId>"
               "<t:DistinguishedFolderId Id=\"calendar\"/>"
            "</SavedItemFolderId>"
            "<Items>"
               "<t:CalendarItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
                  "<Subject>%s</Subject>"
                  "<Body BodyType=\"Text\">%s</Body>"
                  "<ReminderIsSet>false</ReminderIsSet>"
                  "<Start>%s</Start>"
                  "<End>%s</End>"
                  "<IsAllDayEvent>false</IsAllDayEvent>"
                  "<LegacyFreeBusyStatus>%s</LegacyFreeBusyStatus>"
                  "<Location>%s</Location>",
      event->summary,
      event->description,
      mstime(event->start, start, sizeof(start)),
      mstime(event->end, end, sizeof(end)),
      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);

   ast_free(request);

   return ret;
}
static struct calendar_id* get_ewscal_ids_for ( struct ewscal_pvt pvt) [static, read]

Definition at line 649 of file res_calendar_ews.c.

References ast_debug, ast_free, AST_LIST_FIRST, AST_LIST_HEAD_INIT_NOLOCK, ast_localtime(), ast_log(), ast_str_create(), ast_str_set(), ast_strftime(), ast_tvnow(), xml_context::ids, LOG_ERROR, xml_context::op, ewscal_pvt::owner, xml_context::pvt, send_ews_request_and_parse(), ast_calendar::timeframe, and XML_OP_FIND.

Referenced by update_ewscal().

{
   char start[21], end[21];
   struct ast_tm tm;
   struct timeval tv;
   struct ast_str *request;
   struct xml_context ctx = {
      .op = XML_OP_FIND,
      .pvt = pvt,
   };

   ast_debug(5, "EWS: get_ewscal_ids_for()\n");

   if (!pvt) {
      ast_log(LOG_ERROR, "There is no private!\n");
      return NULL;
   }

   /* Prepare timeframe strings */
   tv = ast_tvnow();
   ast_localtime(&tv, &tm, "UTC");
   ast_strftime(start, sizeof(start), "%FT%TZ", &tm);
   tv.tv_sec += 60 * pvt->owner->timeframe;
   ast_localtime(&tv, &tm, "UTC");
   ast_strftime(end, sizeof(end), "%FT%TZ", &tm);

   /* Prepare SOAP request */
   if (!(request = ast_str_create(512))) {
      return NULL;
   }

   ast_str_set(&request, 0,
      "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
      "xmlns:ns1=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
      "xmlns:ns2=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
         "<SOAP-ENV:Body>"
            "<ns2:FindItem Traversal=\"Shallow\">"
               "<ns2:ItemShape>"
                  "<ns1:BaseShape>IdOnly</ns1:BaseShape>"
               "</ns2:ItemShape>"
               "<ns2:CalendarView StartDate=\"%s\" EndDate=\"%s\"/>" /* Timeframe */
               "<ns2:ParentFolderIds>"
                  "<ns1:DistinguishedFolderId Id=\"calendar\"/>"
               "</ns2:ParentFolderIds>"
            "</ns2:FindItem>"
         "</SOAP-ENV:Body>"
      "</SOAP-ENV:Envelope>",
      start, end  /* Timeframe */
   );

   AST_LIST_HEAD_INIT_NOLOCK(&ctx.ids);

   /* Dispatch request and parse response as XML */
   if (send_ews_request_and_parse(request, &ctx)) {
      ast_free(request);
      return NULL;
   }

   /* Cleanup */
   ast_free(request);

   return AST_LIST_FIRST(&ctx.ids);
}
static const char* get_soap_action ( enum xml_op  op) [static]

Definition at line 496 of file res_calendar_ews.c.

References XML_OP_CREATE, XML_OP_FIND, and XML_OP_GET.

Referenced by send_ews_request_and_parse().

{
   switch (op) {
   case XML_OP_FIND:
      return "\"http://schemas.microsoft.com/exchange/services/2006/messages/FindItem\"";
   case XML_OP_GET:
      return "\"http://schemas.microsoft.com/exchange/services/2006/messages/GetItem\"";
   case XML_OP_CREATE:
      return "\"http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem\"";
   }

   return "";
}
static int load_module ( void  ) [static]

Definition at line 906 of file res_calendar_ews.c.

References ast_calendar_register(), ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, and LOG_ERROR.

{
   /* 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)) {
      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;
   }

   if (ast_calendar_register(&ewscal_tech) && (ne_sock_init() == 0)) {
      return AST_MODULE_LOAD_DECLINE;
   }

   return AST_MODULE_LOAD_SUCCESS;
}
static const char* msstatus ( enum ast_calendar_busy_state  state) [static]

Definition at line 482 of file res_calendar_ews.c.

References AST_CALENDAR_BS_BUSY, AST_CALENDAR_BS_BUSY_TENTATIVE, and AST_CALENDAR_BS_FREE.

Referenced by ewscal_write_event().

{
   switch (state) {
   case AST_CALENDAR_BS_BUSY_TENTATIVE:
      return "Tentative";
   case AST_CALENDAR_BS_BUSY:
      return "Busy";
   case AST_CALENDAR_BS_FREE:
      return "Free";
   default:
      return "";
   }
}
static const char* mstime ( time_t  t,
char *  buf,
size_t  buflen 
) [static]

Definition at line 469 of file res_calendar_ews.c.

References ast_localtime(), ast_strftime(), and S_OR.

Referenced by ewscal_write_event().

{
   struct timeval tv = {
      .tv_sec = t,
   };
   struct ast_tm tm;

   ast_localtime(&tv, &tm, "utc");
   ast_strftime(buf, buflen, "%FT%TZ", &tm);

   return S_OR(buf, "");
}
static time_t mstime_to_time_t ( char *  mstime) [static]

Definition at line 162 of file res_calendar_ews.c.

References ast_mktime(), and ast_strptime().

Referenced by cdata().

{
   struct ast_tm tm;
   struct timeval tv;

   if (ast_strptime(mstime, "%FT%TZ", &tm)) {
      tv = ast_mktime(&tm, "UTC");
      return tv.tv_sec;
   }
   return 0;
}
static int parse_ewscal_id ( struct ewscal_pvt pvt,
const char *  id 
) [static]

Definition at line 713 of file res_calendar_ews.c.

References ast_free, ast_str_create(), ast_str_set(), xml_context::pvt, send_ews_request_and_parse(), and XML_OP_GET.

Referenced by update_ewscal().

                                                                   {
   struct ast_str *request;
   struct xml_context ctx = {
      .pvt = pvt,
      .op = XML_OP_GET,
   };

   if (!(request = ast_str_create(512))) {
      return -1;
   }

   ast_str_set(&request, 0,
      "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
      "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
      "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
      "<soap:Body>"
         "<GetItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
            "<ItemShape>"
               "<t:BaseShape>AllProperties</t:BaseShape>"
            "</ItemShape>"
            "<ItemIds>"
               "<t:ItemId Id=\"%s\"/>"
            "</ItemIds>"
         "</GetItem>"
      "</soap:Body>"
      "</soap:Envelope>", id
   );

   if (send_ews_request_and_parse(request, &ctx)) {
      ast_free(request);
      return -1;
   }

   ast_free(request);

   return 0;
}
static int send_ews_request_and_parse ( struct ast_str request,
struct xml_context ctx 
) [static]

Definition at line 510 of file res_calendar_ews.c.

References ast_debug, ast_log(), ast_str_buffer(), ast_str_strlen(), endelm(), get_soap_action(), LOG_ERROR, LOG_WARNING, xml_context::op, xml_context::parser, xml_context::pvt, ewscal_pvt::session, startelm(), ewscal_pvt::uri, and ewscal_pvt::url.

Referenced by ewscal_write_event(), get_ewscal_ids_for(), and parse_ewscal_id().

{
   int ret;
   ne_request *req;
   ne_xml_parser *parser;

   ast_debug(3, "EWS: HTTP request...\n");
   if (!(ctx && ctx->pvt)) {
      ast_log(LOG_ERROR, "There is no private!\n");
      return -1;
   }

   if (!ast_str_strlen(request)) {
      ast_log(LOG_ERROR, "No request to send!\n");
      return -1;
   }

   ast_debug(3, "%s\n", ast_str_buffer(request));

   /* Prepare HTTP POST request */
   req = ne_request_create(ctx->pvt->session, "POST", ctx->pvt->uri.path);
   ne_set_request_flag(req, NE_REQFLAG_IDEMPOTENT, 0);

   /* Set headers--should be application/soap+xml, but MS… :/ */
   ne_add_request_header(req, "Content-Type", "text/xml; charset=utf-8");
   ne_add_request_header(req, "SOAPAction", get_soap_action(ctx->op));

   /* Set body to SOAP request */
   ne_set_request_body_buffer(req, ast_str_buffer(request), ast_str_strlen(request));

   /* Prepare XML parser */
   parser = ne_xml_create();
   ctx->parser = parser;
   ne_xml_push_handler(parser, startelm, cdata, endelm, ctx);  /* Callbacks */

   /* Dispatch request and parse response as XML */
   ret = ne_xml_dispatch_request(req, parser);
   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);
      ne_xml_destroy(parser);
      return -1;
   }

   /* Cleanup */
   ne_request_destroy(req);
   ne_xml_destroy(parser);

   return 0;
}
static int ssl_verify ( void *  userdata,
int  failures,
const ne_ssl_certificate *  cert 
) [static]

Definition at line 152 of file res_calendar_ews.c.

References ast_log(), LOG_WARNING, ast_calendar::name, and ewscal_pvt::owner.

Referenced by ewscal_load_calendar().

{
   struct ewscal_pvt *pvt = userdata;
   if (failures & NE_SSL_UNTRUSTED) {
      ast_log(LOG_WARNING, "Untrusted SSL certificate for calendar %s!\n", pvt->owner->name);
      return 0;
   }
   return 1;   /* NE_SSL_NOTYETVALID, NE_SSL_EXPIRED, NE_SSL_IDMISMATCH */
}
static int startelm ( void *  userdata,
int  parent,
const char *  nspace,
const char *  name,
const char **  atts 
) [static]

Definition at line 174 of file res_calendar_ews.c.

References ast_calendar_event_alloc(), ast_calendar_merge_events(), ast_calloc, ast_debug, ast_free, AST_LIST_INSERT_TAIL, ast_log(), ast_str_buffer(), ast_str_create(), ast_str_reset(), ast_str_set(), ast_string_field_set, xml_context::cdata, xml_context::event, ewscal_pvt::events, calendar_id::id, xml_context::ids, ewscal_pvt::items, LOG_ERROR, xml_context::op, ewscal_pvt::owner, xml_context::parser, xml_context::pvt, XML_EVENT_ATTENDEE, XML_EVENT_ATTENDEE_LIST, XML_EVENT_BUSY, XML_EVENT_CALENDAR_ITEM, XML_EVENT_CATEGORIES, XML_EVENT_CATEGORY, XML_EVENT_DESCRIPTION, XML_EVENT_EMAIL_ADDRESS, XML_EVENT_END, XML_EVENT_IMPORTANCE, XML_EVENT_LOCATION, XML_EVENT_MAILBOX, XML_EVENT_NAME, XML_EVENT_ORGANIZER, XML_EVENT_START, XML_OP_CREATE, and XML_OP_FIND.

Referenced by send_ews_request_and_parse().

{
   struct xml_context *ctx = userdata;

   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") && parent != XML_EVENT_CALENDAR_ITEM) ||
      !strcmp(name, "FindItemResponse") ||
      !strcmp(name, "GetItemResponse") ||
      !strcmp(name, "CreateItemResponse") ||
      !strcmp(name, "ResponseMessages") ||
      !strcmp(name, "FindItemResponseMessage") || !strcmp(name, "GetItemResponseMessage") ||
      !strcmp(name, "Items")
   ) {
      return 1;
   } else if (!strcmp(name, "RootFolder")) {
      /* Get number of events */
      unsigned int items;

      ast_debug(3, "EWS: XML: <RootFolder>\n");
      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: %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;
   } else if (!strcmp(name, "CalendarItem")) {
      /* Event start */
      ast_debug(3, "EWS: XML: <CalendarItem>\n");
      if (!(ctx->pvt && ctx->pvt->owner)) {
         ast_log(LOG_ERROR, "Require a private structure with an owner\n");
         return NE_XML_ABORT;
      }

      ctx->event = ast_calendar_event_alloc(ctx->pvt->owner);
      if (!ctx->event) {
         ast_log(LOG_ERROR, "Could not allocate an event!\n");
         return NE_XML_ABORT;
      }

      ctx->cdata = ast_str_create(64);
      if (!ctx->cdata) {
         ast_log(LOG_ERROR, "Could not allocate CDATA!\n");
         return NE_XML_ABORT;
      }

      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)))) {
            return NE_XML_ABORT;
         }
         if (!(id->id = ast_str_create(256))) {
            ast_free(id);
            return NE_XML_ABORT;
         }
         ast_str_set(&id->id, 0, "%s", ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
         AST_LIST_INSERT_TAIL(&ctx->ids, id, next);
         ast_debug(3, "EWS_FIND: XML: UID: %s\n", ast_str_buffer(id->id));
      } else {
         ast_debug(3, "EWS_GET: XML: UID: %s\n", ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
         ast_string_field_set(ctx->event, uid, ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
      }
      return XML_EVENT_NAME;
   } else if (!strcmp(name, "Subject")) {
      /* Event name */
      if (!ctx->cdata) {
         return NE_XML_ABORT;
      }
      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;
   } else if (!strcmp(name, "End")) {
      /* Event end time */
      return XML_EVENT_END;
   } else if (!strcmp(name, "LegacyFreeBusyStatus")) {
      /* Event busy state */
      return XML_EVENT_BUSY;
   } else if (!strcmp(name, "Organizer") ||
         (parent == XML_EVENT_ORGANIZER && (!strcmp(name, "Mailbox") ||
         !strcmp(name, "Name")))) {
      /* Event organizer */
      if (!ctx->cdata) {
         return NE_XML_ABORT;
      }
      ast_str_reset(ctx->cdata);
      return XML_EVENT_ORGANIZER;
   } else if (!strcmp(name, "Location")) {
      /* Event location */
      if (!ctx->cdata) {
         return NE_XML_ABORT;
      }
      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) {
      return XML_EVENT_ATTENDEE;
   } else if (!strcmp(name, "Mailbox") && parent == XML_EVENT_ATTENDEE) {
      return XML_EVENT_MAILBOX;
   } else if (!strcmp(name, "EmailAddress") && parent == XML_EVENT_MAILBOX) {
      if (!ctx->cdata) {
         return NE_XML_ABORT;
      }
      ast_str_reset(ctx->cdata);
      return XML_EVENT_EMAIL_ADDRESS;
   }

   return NE_XML_DECLINE;
}
static int unload_module ( void  ) [static]

Definition at line 922 of file res_calendar_ews.c.

References ast_calendar_unregister().

{
   ne_sock_exit();
   ast_calendar_unregister(&ewscal_tech);

   return 0;
}
static void * unref_ewscal ( void *  obj) [static]

Definition at line 128 of file res_calendar_ews.c.

References ao2_ref, and ast_debug.

Referenced by ewscal_load_calendar().

{
   struct ewscal_pvt *pvt = obj;

   ast_debug(5, "EWS: unref_ewscal()\n");
   ao2_ref(pvt, -1);
   return NULL;
}
static int update_ewscal ( struct ewscal_pvt pvt) [static]

Definition at line 751 of file res_calendar_ews.c.

References ast_free, AST_LIST_NEXT, ast_str_buffer(), get_ewscal_ids_for(), calendar_id::id, and parse_ewscal_id().

Referenced by ewscal_load_calendar().

{
   struct calendar_id *id_head;
   struct calendar_id *iter;

   if (!(id_head = get_ewscal_ids_for(pvt))) {
      return 0;
   }

   for (iter = id_head; iter; iter = AST_LIST_NEXT(iter, next)) {
      parse_ewscal_id(pvt, ast_str_buffer(iter->id));
      ast_free(iter->id);
      ast_free(iter);
   }

   return 0;
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Asterisk MS Exchange Web Service Calendar Integration" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PLUGIN, } [static]

Definition at line 934 of file res_calendar_ews.c.

Definition at line 934 of file res_calendar_ews.c.

struct ast_calendar_tech ewscal_tech [static]

Definition at line 52 of file res_calendar_ews.c.