Mon Mar 19 11:30:53 2012

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"

Go to the source code of this file.

Data Structures

struct  calendar_id
struct  ewscal_pvt
struct  xml_context
struct  xml_context::ids

Enumerations

enum  {
  XML_EVENT_NAME = 10, 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 = "88eaa8f5c1bd988bedd71113385e0886" , .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_NAME 
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.

00061             {
00062    XML_OP_FIND = 100,
00063    XML_OP_GET,
00064    XML_OP_CREATE,
00065 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 918 of file res_calendar_ews.c.

static void __unreg_module ( void   )  [static]

Definition at line 918 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 135 of file res_calendar_ews.c.

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

00136 {
00137    struct ewscal_pvt *pvt = userdata;
00138 
00139    if (attempts > 1) {
00140       ast_log(LOG_WARNING, "Invalid username or password for Exchange Web Service calendar '%s'\n", pvt->owner->name);
00141       return -1;
00142    }
00143 
00144    ne_strnzcpy(username, pvt->user, NE_ABUFSIZ);
00145    ne_strnzcpy(secret, pvt->secret, NE_ABUFSIZ);
00146 
00147    return 0;
00148 }

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

Definition at line 317 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.

Referenced by send_ews_request_and_parse().

00318 {
00319    struct xml_context *ctx = userdata;
00320    char data[len + 1];
00321 
00322    /* !!! DON'T USE AST_STRING_FIELD FUNCTIONS HERE, JUST COLLECT CTX->CDATA !!! */
00323    if (state < XML_EVENT_NAME || ctx->op == XML_OP_CREATE) {
00324       return 0;
00325    }
00326 
00327    if (!ctx->event) {
00328       ast_log(LOG_ERROR, "Parsing event data, but event object does not exist!\n");
00329       return 1;
00330    }
00331 
00332    if (!ctx->cdata) {
00333       ast_log(LOG_ERROR, "String for storing CDATA is unitialized!\n");
00334       return 1;
00335    }
00336 
00337    ast_copy_string(data, cdata, len + 1);
00338 
00339    switch (state) {
00340    case XML_EVENT_START:
00341       ctx->event->start = mstime_to_time_t(data);
00342       break;
00343    case XML_EVENT_END:
00344       ctx->event->end = mstime_to_time_t(data);
00345       break;
00346    case XML_EVENT_BUSY:
00347       if (!strcmp(data, "Busy") || !strcmp(data, "OOF")) {
00348          ast_debug(3, "EWS: XML: Busy: yes\n");
00349          ctx->event->busy_state = AST_CALENDAR_BS_BUSY;
00350       }
00351       else if (!strcmp(data, "Tentative")) {
00352          ast_debug(3, "EWS: XML: Busy: tentative\n");
00353          ctx->event->busy_state = AST_CALENDAR_BS_BUSY_TENTATIVE;
00354       }
00355       else {
00356          ast_debug(3, "EWS: XML: Busy: no\n");
00357          ctx->event->busy_state = AST_CALENDAR_BS_FREE;
00358       }
00359       break;
00360    case XML_EVENT_CATEGORY:
00361       if (ast_str_strlen(ctx->cdata) == 0) {
00362          ast_str_set(&ctx->cdata, 0, "%s", data);
00363       } else {
00364          ast_str_append(&ctx->cdata, 0, ",%s", data);
00365       }
00366       break;
00367    default:
00368       ast_str_append(&ctx->cdata, 0, "%s", data);
00369    }
00370 
00371    ast_debug(5, "EWS: XML: CDATA: %s\n", ast_str_buffer(ctx->cdata));
00372 
00373    return 0;
00374 }

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

Definition at line 376 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, xml_context::event, ewscal_pvt::events, ewscal_pvt::items, ast_calendar_event::location, LOG_ERROR, calendar_id::next, xml_context::op, ast_calendar_event::organizer, ewscal_pvt::owner, ast_calendar_event::priority, xml_context::pvt, ast_calendar_event::summary, XML_EVENT_EMAIL_ADDRESS, XML_OP_CREATE, and XML_OP_FIND.

Referenced by send_ews_request_and_parse().

00377 {
00378    struct xml_context *ctx = userdata;
00379 
00380    ast_debug(5, "EWS: XML: End:   %s\n", name);
00381    if (ctx->op == XML_OP_FIND || ctx->op == XML_OP_CREATE) {
00382       return NE_XML_DECLINE;
00383    }
00384 
00385    if (!strcmp(name, "Subject")) {
00386       /* Event name end*/
00387       ast_string_field_set(ctx->event, summary, ast_str_buffer(ctx->cdata));
00388       ast_debug(3, "EWS: XML: Summary: %s\n", ctx->event->summary);
00389       ast_str_reset(ctx->cdata);
00390    } else if (!strcmp(name, "Organizer")) {
00391       /* Event organizer end */
00392       ast_string_field_set(ctx->event, organizer, ast_str_buffer(ctx->cdata));
00393       ast_debug(3, "EWS: XML: Organizer: %s\n", ctx->event->organizer);
00394       ast_str_reset(ctx->cdata);
00395    } else if (!strcmp(name, "Location")) {
00396       /* Event location end */
00397       ast_string_field_set(ctx->event, location, ast_str_buffer(ctx->cdata));
00398       ast_debug(3, "EWS: XML: Location: %s\n", ctx->event->location);
00399       ast_str_reset(ctx->cdata);
00400    } else if (!strcmp(name, "Categories")) {
00401       /* Event categories end */
00402       ast_string_field_set(ctx->event, categories, ast_str_buffer(ctx->cdata));
00403       ast_debug(3, "EWS: XML: Categories: %s\n", ctx->event->categories);
00404       ast_str_reset(ctx->cdata);
00405    } else if (!strcmp(name, "Importance")) {
00406       /* Event importance end */
00407       if (!strcmp(ast_str_buffer(ctx->cdata), "Low")) {
00408          ctx->event->priority = 9;
00409       } else if (!strcmp(ast_str_buffer(ctx->cdata), "Normal")) {
00410          ctx->event->priority = 5;
00411       } else if (!strcmp(ast_str_buffer(ctx->cdata), "High")) {
00412          ctx->event->priority = 1;
00413       }
00414       ast_debug(3, "EWS: XML: Importance: %s (%d)\n", ast_str_buffer(ctx->cdata), ctx->event->priority);
00415       ast_str_reset(ctx->cdata);
00416    } else if (state == XML_EVENT_EMAIL_ADDRESS) {
00417       struct ast_calendar_attendee *attendee;
00418 
00419       if (!(attendee = ast_calloc(1, sizeof(*attendee)))) {
00420          ctx->event = ast_calendar_unref_event(ctx->event);
00421          return  1;
00422       }
00423 
00424       if (ast_str_strlen(ctx->cdata)) {
00425          attendee->data = ast_strdup(ast_str_buffer(ctx->cdata));
00426          AST_LIST_INSERT_TAIL(&ctx->event->attendees, attendee, next);
00427       }
00428       ast_debug(3, "EWS: XML: attendee address '%s'\n", ast_str_buffer(ctx->cdata));
00429       ast_str_reset(ctx->cdata);
00430    } else if (!strcmp(name, "CalendarItem")) {
00431       /* Event end */
00432       ast_debug(3, "EWS: XML: </CalendarItem>\n");
00433       ast_free(ctx->cdata);
00434       if (ctx->event) {
00435          ao2_link(ctx->pvt->events, ctx->event);
00436          ctx->event = ast_calendar_unref_event(ctx->event);
00437       } else {
00438          ast_log(LOG_ERROR, "Event data ended in XML, but event object does not exist!\n");
00439          return 1;
00440       }
00441    } else if (!strcmp(name, "Envelope")) {
00442       /* Events end */
00443       ast_debug(3, "EWS: XML: %d of %d event(s) has been parsed…\n", ao2_container_count(ctx->pvt->events), ctx->pvt->items);
00444       if (ao2_container_count(ctx->pvt->events) >= ctx->pvt->items) {
00445          ast_debug(3, "EWS: XML: All events has been parsed, merging…\n");
00446          ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events);
00447       }
00448    }
00449 
00450    return 0;
00451 }

static void ewscal_destructor ( void *  obj  )  [static]

Definition at line 111 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().

00112 {
00113    struct ewscal_pvt *pvt = obj;
00114 
00115    ast_debug(1, "Destroying pvt for Exchange Web Service calendar %s\n", "pvt->owner->name");
00116    if (pvt->session) {
00117       ne_session_destroy(pvt->session);
00118    }
00119    ast_string_field_free_memory(pvt);
00120 
00121    ao2_callback(pvt->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
00122 
00123    ao2_ref(pvt->events, -1);
00124 }

static void * ewscal_load_calendar ( void *  data  )  [static]

Definition at line 753 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_destructor(), LOG_ERROR, LOG_WARNING, ast_variable::name, ast_calendar::name, ast_variable::next, refreshlock, secret, ssl_verify(), ast_calendar::tech_pvt, tv, ast_calendar::unloading, unref_ewscal(), update_ewscal(), url, and ast_variable::value.

00754 {
00755    struct ewscal_pvt *pvt;
00756    const struct ast_config *cfg;
00757    struct ast_variable *v;
00758    struct ast_calendar *cal = void_data;
00759    ast_mutex_t refreshlock;
00760 
00761    ast_debug(5, "EWS: ewscal_load_calendar()\n");
00762 
00763    if (!(cal && (cfg = ast_calendar_config_acquire()))) {
00764       ast_log(LOG_ERROR, "You must enable calendar support for res_ewscal to load\n");
00765       return NULL;
00766    }
00767 
00768    if (ao2_trylock(cal)) {
00769       if (cal->unloading) {
00770          ast_log(LOG_WARNING, "Unloading module, load_calendar cancelled.\n");
00771       } else {
00772          ast_log(LOG_WARNING, "Could not lock calendar, aborting!\n");
00773       }
00774       ast_calendar_config_release();
00775       return NULL;
00776    }
00777 
00778    if (!(pvt = ao2_alloc(sizeof(*pvt), ewscal_destructor))) {
00779       ast_log(LOG_ERROR, "Could not allocate ewscal_pvt structure for calendar: %s\n", cal->name);
00780       ast_calendar_config_release();
00781       return NULL;
00782    }
00783 
00784    pvt->owner = cal;
00785 
00786    if (!(pvt->events = ast_calendar_event_container_alloc())) {
00787       ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name);
00788       pvt = unref_ewscal(pvt);
00789       ao2_unlock(cal);
00790       ast_calendar_config_release();
00791       return NULL;
00792    }
00793 
00794    if (ast_string_field_init(pvt, 32)) {
00795       ast_log(LOG_ERROR, "Couldn't allocate string field space for calendar: %s\n", cal->name);
00796       pvt = unref_ewscal(pvt);
00797       ao2_unlock(cal);
00798       ast_calendar_config_release();
00799       return NULL;
00800    }
00801 
00802    for (v = ast_variable_browse(cfg, cal->name); v; v = v->next) {
00803       if (!strcasecmp(v->name, "url")) {
00804          ast_string_field_set(pvt, url, v->value);
00805       } else if (!strcasecmp(v->name, "user")) {
00806          ast_string_field_set(pvt, user, v->value);
00807       } else if (!strcasecmp(v->name, "secret")) {
00808          ast_string_field_set(pvt, secret, v->value);
00809       }
00810    }
00811 
00812    ast_calendar_config_release();
00813 
00814    if (ast_strlen_zero(pvt->url)) {
00815       ast_log(LOG_WARNING, "No URL was specified for Exchange Web Service calendar '%s' - skipping.\n", cal->name);
00816       pvt = unref_ewscal(pvt);
00817       ao2_unlock(cal);
00818       return NULL;
00819    }
00820 
00821    if (ne_uri_parse(pvt->url, &pvt->uri) || pvt->uri.host == NULL || pvt->uri.path == NULL) {
00822       ast_log(LOG_WARNING, "Could not parse url '%s' for Exchange Web Service calendar '%s' - skipping.\n", pvt->url, cal->name);
00823       pvt = unref_ewscal(pvt);
00824       ao2_unlock(cal);
00825       return NULL;
00826    }
00827 
00828    if (pvt->uri.scheme == NULL) {
00829       pvt->uri.scheme = "http";
00830    }
00831 
00832    if (pvt->uri.port == 0) {
00833       pvt->uri.port = ne_uri_defaultport(pvt->uri.scheme);
00834    }
00835 
00836    ast_debug(3, "ne_uri.scheme   = %s\n", pvt->uri.scheme);
00837    ast_debug(3, "ne_uri.host  = %s\n", pvt->uri.host);
00838    ast_debug(3, "ne_uri.port  = %u\n", pvt->uri.port);
00839    ast_debug(3, "ne_uri.path  = %s\n", pvt->uri.path);
00840    ast_debug(3, "user      = %s\n", pvt->user);
00841    ast_debug(3, "secret    = %s\n", pvt->secret);
00842 
00843    pvt->session = ne_session_create(pvt->uri.scheme, pvt->uri.host, pvt->uri.port);
00844    ne_redirect_register(pvt->session);
00845    ne_set_server_auth(pvt->session, auth_credentials, pvt);
00846    ne_set_useragent(pvt->session, "Asterisk");
00847 
00848    if (!strcasecmp(pvt->uri.scheme, "https")) {
00849       ne_ssl_trust_default_ca(pvt->session);
00850       ne_ssl_set_verify(pvt->session, ssl_verify, pvt);
00851    }
00852 
00853    cal->tech_pvt = pvt;
00854 
00855    ast_mutex_init(&refreshlock);
00856 
00857    /* Load it the first time */
00858    update_ewscal(pvt);
00859 
00860    ao2_unlock(cal);
00861 
00862    /* The only writing from another thread will be if unload is true */
00863    for (;;) {
00864       struct timeval tv = ast_tvnow();
00865       struct timespec ts = {0,};
00866 
00867       ts.tv_sec = tv.tv_sec + (60 * pvt->owner->refresh);
00868 
00869       ast_mutex_lock(&refreshlock);
00870       while (!pvt->owner->unloading) {
00871          if (ast_cond_timedwait(&pvt->owner->unload, &refreshlock, &ts) == ETIMEDOUT) {
00872             break;
00873          }
00874       }
00875       ast_mutex_unlock(&refreshlock);
00876 
00877       if (pvt->owner->unloading) {
00878          ast_debug(10, "Skipping refresh since we got a shutdown signal\n");
00879          return NULL;
00880       }
00881 
00882       ast_debug(10, "Refreshing after %d minute timeout\n", pvt->owner->refresh);
00883 
00884       update_ewscal(pvt);
00885    }
00886 
00887    return NULL;
00888 }

static int ewscal_write_event ( struct ast_calendar_event event  )  [static]

Definition at line 545 of file res_calendar_ews.c.

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

00546 {
00547    struct ast_str *request;
00548    struct ewscal_pvt *pvt = event->owner->tech_pvt;
00549    char start[21], end[21];
00550    struct xml_context ctx = {
00551       .op = XML_OP_CREATE,
00552       .pvt = pvt,
00553    };
00554    int ret;
00555    char *category, *categories;
00556 
00557    if (!pvt) {
00558       return -1;
00559    }
00560 
00561    if (!(request = ast_str_create(1024))) {
00562       return -1;
00563    }
00564 
00565    ast_str_set(&request, 0,
00566       "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
00567          "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
00568          "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00569          "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00570          "<soap:Body>"
00571          "<CreateItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\" "
00572             "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
00573             "SendMeetingInvitations=\"SendToNone\" >"
00574             "<SavedItemFolderId>"
00575                "<t:DistinguishedFolderId Id=\"calendar\"/>"
00576             "</SavedItemFolderId>"
00577             "<Items>"
00578                "<t:CalendarItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00579                   "<Subject>%s</Subject>"
00580                   "<Body BodyType=\"Text\">%s</Body>"
00581                   "<ReminderIsSet>false</ReminderIsSet>"
00582                   "<Start>%s</Start>"
00583                   "<End>%s</End>"
00584                   "<IsAllDayEvent>false</IsAllDayEvent>"
00585                   "<LegacyFreeBusyStatus>%s</LegacyFreeBusyStatus>"
00586                   "<Location>%s</Location>",
00587       event->summary,
00588       event->description,
00589       mstime(event->start, start, sizeof(start)),
00590       mstime(event->end, end, sizeof(end)),
00591       msstatus(event->busy_state),
00592       event->location
00593    );
00594    /* Event priority */
00595    switch (event->priority) {
00596    case 1:
00597    case 2:
00598    case 3:
00599    case 4:
00600       ast_str_append(&request, 0, "<Importance>High</Importance>");
00601       break;
00602    case 5:
00603       ast_str_append(&request, 0, "<Importance>Normal</Importance>");
00604       break;
00605    case 6:
00606    case 7:
00607    case 8:
00608    case 9:
00609       ast_str_append(&request, 0, "<Importance>Low</Importance>");
00610       break;
00611    }
00612    /* Event categories*/
00613    if (strlen(event->categories) > 0) {
00614       ast_str_append(&request, 0, "<Categories>");
00615       categories = ast_strdupa(event->categories); /* Duplicate string, since strsep() is destructive */
00616       category = strsep(&categories, ",");
00617       while (category != NULL) {
00618          ast_str_append(&request, 0, "<String>%s</String>", category);
00619          category = strsep(&categories, ",");
00620       }
00621       ast_str_append(&request, 0, "</Categories>");
00622    }
00623    /* Finish request */
00624    ast_str_append(&request, 0, "</t:CalendarItem></Items></CreateItem></soap:Body></soap:Envelope>");
00625 
00626    ret = send_ews_request_and_parse(request, &ctx);
00627 
00628    ast_free(request);
00629 
00630    return ret;
00631 }

static struct calendar_id* get_ewscal_ids_for ( struct ewscal_pvt pvt  )  [static]

Definition at line 633 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().

00634 {
00635    char start[21], end[21];
00636    struct ast_tm tm;
00637    struct timeval tv;
00638    struct ast_str *request;
00639    struct xml_context ctx = {
00640       .op = XML_OP_FIND,
00641       .pvt = pvt,
00642    };
00643 
00644    ast_debug(5, "EWS: get_ewscal_ids_for()\n");
00645 
00646    if (!pvt) {
00647       ast_log(LOG_ERROR, "There is no private!\n");
00648       return NULL;
00649    }
00650 
00651    /* Prepare timeframe strings */
00652    tv = ast_tvnow();
00653    ast_localtime(&tv, &tm, "UTC");
00654    ast_strftime(start, sizeof(start), "%FT%TZ", &tm);
00655    tv.tv_sec += 60 * pvt->owner->timeframe;
00656    ast_localtime(&tv, &tm, "UTC");
00657    ast_strftime(end, sizeof(end), "%FT%TZ", &tm);
00658 
00659    /* Prepare SOAP request */
00660    if (!(request = ast_str_create(512))) {
00661       return NULL;
00662    }
00663 
00664    ast_str_set(&request, 0,
00665       "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00666       "xmlns:ns1=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
00667       "xmlns:ns2=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
00668          "<SOAP-ENV:Body>"
00669             "<ns2:FindItem Traversal=\"Shallow\">"
00670                "<ns2:ItemShape>"
00671                   "<ns1:BaseShape>IdOnly</ns1:BaseShape>"
00672                "</ns2:ItemShape>"
00673                "<ns2:CalendarView StartDate=\"%s\" EndDate=\"%s\"/>" /* Timeframe */
00674                "<ns2:ParentFolderIds>"
00675                   "<ns1:DistinguishedFolderId Id=\"calendar\"/>"
00676                "</ns2:ParentFolderIds>"
00677             "</ns2:FindItem>"
00678          "</SOAP-ENV:Body>"
00679       "</SOAP-ENV:Envelope>",
00680       start, end  /* Timeframe */
00681    );
00682 
00683    AST_LIST_HEAD_INIT_NOLOCK(&ctx.ids);
00684 
00685    /* Dispatch request and parse response as XML */
00686    if (send_ews_request_and_parse(request, &ctx)) {
00687       ast_free(request);
00688       return NULL;
00689    }
00690 
00691    /* Cleanup */
00692    ast_free(request);
00693 
00694    return AST_LIST_FIRST(&ctx.ids);
00695 }

static const char* get_soap_action ( enum xml_op  op  )  [static]

Definition at line 480 of file res_calendar_ews.c.

References XML_OP_CREATE, XML_OP_FIND, and XML_OP_GET.

Referenced by send_ews_request_and_parse().

00481 {
00482    switch (op) {
00483    case XML_OP_FIND:
00484       return "\"http://schemas.microsoft.com/exchange/services/2006/messages/FindItem\"";
00485    case XML_OP_GET:
00486       return "\"http://schemas.microsoft.com/exchange/services/2006/messages/GetItem\"";
00487    case XML_OP_CREATE:
00488       return "\"http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem\"";
00489    }
00490 
00491    return "";
00492 }

static int load_module ( void   )  [static]

Definition at line 890 of file res_calendar_ews.c.

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

00891 {
00892    /* Actualy, 0.29.1 is required (because of NTLM authentication), but this
00893     * function does not support matching patch version. */
00894    if (ne_version_match(0, 29)) {
00895       ast_log(LOG_ERROR, "Exchange Web Service calendar module require neon >= 0.29.1, but %s is installed.\n", ne_version_string());
00896       return AST_MODULE_LOAD_DECLINE;
00897    }
00898 
00899    if (ast_calendar_register(&ewscal_tech) && (ne_sock_init() == 0)) {
00900       return AST_MODULE_LOAD_DECLINE;
00901    }
00902 
00903    return AST_MODULE_LOAD_SUCCESS;
00904 }

static const char* msstatus ( enum ast_calendar_busy_state  state  )  [static]

Definition at line 466 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().

00467 {
00468    switch (state) {
00469    case AST_CALENDAR_BS_BUSY_TENTATIVE:
00470       return "Tentative";
00471    case AST_CALENDAR_BS_BUSY:
00472       return "Busy";
00473    case AST_CALENDAR_BS_FREE:
00474       return "Free";
00475    default:
00476       return "";
00477    }
00478 }

static const char* mstime ( time_t  t,
char *  buf,
size_t  buflen 
) [static]

Definition at line 453 of file res_calendar_ews.c.

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

Referenced by ewscal_write_event().

00454 {
00455    struct timeval tv = {
00456       .tv_sec = t,
00457    };
00458    struct ast_tm tm;
00459 
00460    ast_localtime(&tv, &tm, "utc");
00461    ast_strftime(buf, buflen, "%FT%TZ", &tm);
00462 
00463    return S_OR(buf, "");
00464 }

static time_t mstime_to_time_t ( char *  mstime  )  [static]

Definition at line 160 of file res_calendar_ews.c.

References ast_mktime(), and ast_strptime().

Referenced by cdata(), and parse_cdata().

00161 {
00162    struct ast_tm tm;
00163    struct timeval tv;
00164 
00165    if (ast_strptime(mstime, "%FT%TZ", &tm)) {
00166       tv = ast_mktime(&tm, "UTC");
00167       return tv.tv_sec;
00168    }
00169    return 0;
00170 }

static int parse_ewscal_id ( struct ewscal_pvt pvt,
const char *  id 
) [static]

Definition at line 697 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().

00697                                                                    {
00698    struct ast_str *request;
00699    struct xml_context ctx = {
00700       .pvt = pvt,
00701       .op = XML_OP_GET,
00702    };
00703 
00704    if (!(request = ast_str_create(512))) {
00705       return -1;
00706    }
00707 
00708    ast_str_set(&request, 0,
00709       "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00710       "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00711       "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00712       "<soap:Body>"
00713          "<GetItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
00714             "<ItemShape>"
00715                "<t:BaseShape>AllProperties</t:BaseShape>"
00716             "</ItemShape>"
00717             "<ItemIds>"
00718                "<t:ItemId Id=\"%s\"/>"
00719             "</ItemIds>"
00720          "</GetItem>"
00721       "</soap:Body>"
00722       "</soap:Envelope>", id
00723    );
00724 
00725    if (send_ews_request_and_parse(request, &ctx)) {
00726       ast_free(request);
00727       return -1;
00728    }
00729 
00730    ast_free(request);
00731 
00732    return 0;
00733 }

static int send_ews_request_and_parse ( struct ast_str request,
struct xml_context ctx 
) [static]

Definition at line 494 of file res_calendar_ews.c.

References ast_debug, ast_log(), ast_str_buffer(), ast_str_strlen(), cdata(), 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().

00495 {
00496    int ret;
00497    ne_request *req;
00498    ne_xml_parser *parser;
00499 
00500    ast_debug(3, "EWS: HTTP request...\n");
00501    if (!(ctx && ctx->pvt)) {
00502       ast_log(LOG_ERROR, "There is no private!\n");
00503       return -1;
00504    }
00505 
00506    if (!ast_str_strlen(request)) {
00507       ast_log(LOG_ERROR, "No request to send!\n");
00508       return -1;
00509    }
00510 
00511    ast_debug(3, "%s\n", ast_str_buffer(request));
00512 
00513    /* Prepare HTTP POST request */
00514    req = ne_request_create(ctx->pvt->session, "POST", ctx->pvt->uri.path);
00515    ne_set_request_flag(req, NE_REQFLAG_IDEMPOTENT, 0);
00516 
00517    /* Set headers--should be application/soap+xml, but MS… :/ */
00518    ne_add_request_header(req, "Content-Type", "text/xml; charset=utf-8");
00519    ne_add_request_header(req, "SOAPAction", get_soap_action(ctx->op));
00520 
00521    /* Set body to SOAP request */
00522    ne_set_request_body_buffer(req, ast_str_buffer(request), ast_str_strlen(request));
00523 
00524    /* Prepare XML parser */
00525    parser = ne_xml_create();
00526    ctx->parser = parser;
00527    ne_xml_push_handler(parser, startelm, cdata, endelm, ctx);  /* Callbacks */
00528 
00529    /* Dispatch request and parse response as XML */
00530    ret = ne_xml_dispatch_request(req, parser);
00531    if (ret != NE_OK) { /* Error handling */
00532       ast_log(LOG_WARNING, "Unable to communicate with Exchange Web Service at '%s': %s\n", ctx->pvt->url, ne_get_error(ctx->pvt->session));
00533       ne_request_destroy(req);
00534       ne_xml_destroy(parser);
00535       return -1;
00536    }
00537 
00538    /* Cleanup */
00539    ne_request_destroy(req);
00540    ne_xml_destroy(parser);
00541 
00542    return 0;
00543 }

static int ssl_verify ( void *  userdata,
int  failures,
const ne_ssl_certificate *  cert 
) [static]

Definition at line 150 of file res_calendar_ews.c.

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

Referenced by ewscal_load_calendar().

00151 {
00152    struct ewscal_pvt *pvt = userdata;
00153    if (failures & NE_SSL_UNTRUSTED) {
00154       ast_log(LOG_WARNING, "Untrusted SSL certificate for calendar %s!\n", pvt->owner->name);
00155       return 0;
00156    }
00157    return 1;   /* NE_SSL_NOTYETVALID, NE_SSL_EXPIRED, NE_SSL_IDMISMATCH */
00158 }

static int startelm ( void *  userdata,
int  parent,
const char *  nspace,
const char *  name,
const char **  atts 
) [static]

Definition at line 172 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, calendar_id::next, xml_context::op, ewscal_pvt::owner, xml_context::parser, xml_context::pvt, XML_EVENT_ATTENDEE, XML_EVENT_ATTENDEE_LIST, XML_EVENT_BUSY, XML_EVENT_CATEGORIES, XML_EVENT_CATEGORY, 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().

00173 {
00174    struct xml_context *ctx = userdata;
00175 
00176    ast_debug(5, "EWS: XML: Start: %s\n", name);
00177    if (ctx->op == XML_OP_CREATE) {
00178       return NE_XML_DECLINE;
00179    }
00180 
00181    /* Nodes needed for traversing until CalendarItem is found */
00182    if (!strcmp(name, "Envelope") ||
00183       !strcmp(name, "Body") ||
00184       !strcmp(name, "FindItemResponse") ||
00185       !strcmp(name, "GetItemResponse") ||
00186       !strcmp(name, "CreateItemResponse") ||
00187       !strcmp(name, "ResponseMessages") ||
00188       !strcmp(name, "FindItemResponseMessage") || !strcmp(name, "GetItemResponseMessage") ||
00189       !strcmp(name, "Items")
00190    ) {
00191       return 1;
00192    } else if (!strcmp(name, "RootFolder")) {
00193       /* Get number of events */
00194       unsigned int items;
00195 
00196       ast_debug(3, "EWS: XML: <RootFolder>\n");
00197       if (sscanf(ne_xml_get_attr(ctx->parser, atts, NULL, "TotalItemsInView"), "%u", &items) != 1) {
00198          /* Couldn't read enything */
00199          ne_xml_set_error(ctx->parser, "Could't read number of events.");
00200          return NE_XML_ABORT;
00201       }
00202 
00203       ast_debug(3, "EWS: %u calendar items to load\n", items);
00204       ctx->pvt->items = items;
00205       if (items < 1) {
00206          /* Stop processing XML if there are no events */
00207          ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events);
00208          return NE_XML_DECLINE;
00209       }
00210       return 1;
00211    } else if (!strcmp(name, "CalendarItem")) {
00212       /* Event start */
00213       ast_debug(3, "EWS: XML: <CalendarItem>\n");
00214       if (!(ctx->pvt && ctx->pvt->owner)) {
00215          ast_log(LOG_ERROR, "Require a private structure with an owner\n");
00216          return NE_XML_ABORT;
00217       }
00218 
00219       ctx->event = ast_calendar_event_alloc(ctx->pvt->owner);
00220       if (!ctx->event) {
00221          ast_log(LOG_ERROR, "Could not allocate an event!\n");
00222          return NE_XML_ABORT;
00223       }
00224 
00225       ctx->cdata = ast_str_create(64);
00226       if (!ctx->cdata) {
00227          ast_log(LOG_ERROR, "Could not allocate CDATA!\n");
00228          return NE_XML_ABORT;
00229       }
00230 
00231       return 1;
00232    } else if (!strcmp(name, "ItemId")) {
00233       /* Event UID */
00234       if (ctx->op == XML_OP_FIND) {
00235          struct calendar_id *id;
00236          if (!(id = ast_calloc(1, sizeof(id)))) {
00237             return NE_XML_ABORT;
00238          }
00239          if (!(id->id = ast_str_create(256))) {
00240             ast_free(id);
00241             return NE_XML_ABORT;
00242          }
00243          ast_str_set(&id->id, 0, "%s", ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
00244          AST_LIST_INSERT_TAIL(&ctx->ids, id, next);
00245          ast_debug(3, "EWS_FIND: XML: UID: %s\n", ast_str_buffer(id->id));
00246       } else {
00247          ast_debug(3, "EWS_GET: XML: UID: %s\n", ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
00248          ast_string_field_set(ctx->event, uid, ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
00249       }
00250       return XML_EVENT_NAME;
00251    } else if (!strcmp(name, "Subject")) {
00252       /* Event name */
00253       if (!ctx->cdata) {
00254          return NE_XML_ABORT;
00255       }
00256       ast_str_reset(ctx->cdata);
00257       return XML_EVENT_NAME;
00258    } else if (!strcmp(name, "Start")) {
00259       /* Event start time */
00260       return XML_EVENT_START;
00261    } else if (!strcmp(name, "End")) {
00262       /* Event end time */
00263       return XML_EVENT_END;
00264    } else if (!strcmp(name, "LegacyFreeBusyStatus")) {
00265       /* Event busy state */
00266       return XML_EVENT_BUSY;
00267    } else if (!strcmp(name, "Organizer") ||
00268          (parent == XML_EVENT_ORGANIZER && (!strcmp(name, "Mailbox") ||
00269          !strcmp(name, "Name")))) {
00270       /* Event organizer */
00271       if (!ctx->cdata) {
00272          return NE_XML_ABORT;
00273       }
00274       ast_str_reset(ctx->cdata);
00275       return XML_EVENT_ORGANIZER;
00276    } else if (!strcmp(name, "Location")) {
00277       /* Event location */
00278       if (!ctx->cdata) {
00279          return NE_XML_ABORT;
00280       }
00281       ast_str_reset(ctx->cdata);
00282       return XML_EVENT_LOCATION;
00283    } else if (!strcmp(name, "Categories")) {
00284       /* Event categories */
00285       if (!ctx->cdata) {
00286          return NE_XML_ABORT;
00287       }
00288       ast_str_reset(ctx->cdata);
00289       return XML_EVENT_CATEGORIES;
00290    } else if (parent == XML_EVENT_CATEGORIES && !strcmp(name, "String")) {
00291       /* Event category */
00292       return XML_EVENT_CATEGORY;
00293    } else if (!strcmp(name, "Importance")) {
00294       /* Event importance (priority) */
00295       if (!ctx->cdata) {
00296          return NE_XML_ABORT;
00297       }
00298       ast_str_reset(ctx->cdata);
00299       return XML_EVENT_IMPORTANCE;
00300    } else if (!strcmp(name, "RequiredAttendees") || !strcmp(name, "OptionalAttendees")) {
00301       return XML_EVENT_ATTENDEE_LIST;
00302    } else if (!strcmp(name, "Attendee") && parent == XML_EVENT_ATTENDEE_LIST) {
00303       return XML_EVENT_ATTENDEE;
00304    } else if (!strcmp(name, "Mailbox") && parent == XML_EVENT_ATTENDEE) {
00305       return XML_EVENT_MAILBOX;
00306    } else if (!strcmp(name, "EmailAddress") && parent == XML_EVENT_MAILBOX) {
00307       if (!ctx->cdata) {
00308          return NE_XML_ABORT;
00309       }
00310       ast_str_reset(ctx->cdata);
00311       return XML_EVENT_EMAIL_ADDRESS;
00312    }
00313 
00314    return NE_XML_DECLINE;
00315 }

static int unload_module ( void   )  [static]

Definition at line 906 of file res_calendar_ews.c.

References ast_calendar_unregister(), and ewscal_tech.

00907 {
00908    ne_sock_exit();
00909    ast_calendar_unregister(&ewscal_tech);
00910 
00911    return 0;
00912 }

static void * unref_ewscal ( void *  obj  )  [static]

Definition at line 126 of file res_calendar_ews.c.

References ao2_ref, and ast_debug.

Referenced by ewscal_load_calendar().

00127 {
00128    struct ewscal_pvt *pvt = obj;
00129 
00130    ast_debug(5, "EWS: unref_ewscal()\n");
00131    ao2_ref(pvt, -1);
00132    return NULL;
00133 }

static int update_ewscal ( struct ewscal_pvt pvt  )  [static]

Definition at line 735 of file res_calendar_ews.c.

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

Referenced by ewscal_load_calendar().

00736 {
00737    struct calendar_id *id_head;
00738    struct calendar_id *iter;
00739 
00740    if (!(id_head = get_ewscal_ids_for(pvt))) {
00741       return 0;
00742    }
00743 
00744    for (iter = id_head; iter; iter = AST_LIST_NEXT(iter, next)) {
00745       parse_ewscal_id(pvt, ast_str_buffer(iter->id));
00746       ast_free(iter->id);
00747       ast_free(iter);
00748    }
00749 
00750    return 0;
00751 }


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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PLUGIN, } [static]

Definition at line 918 of file res_calendar_ews.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 918 of file res_calendar_ews.c.

struct ast_calendar_tech ewscal_tech [static]

Definition at line 52 of file res_calendar_ews.c.

Referenced by load_module(), and unload_module().


Generated on Mon Mar 19 11:30:53 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7