Mon Jun 27 16:51:19 2011

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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 80 of file res_calendar_ews.c.

enum xml_op

Enumerator:
XML_OP_FIND 
XML_OP_GET 
XML_OP_CREATE 

Definition at line 59 of file res_calendar_ews.c.

00059             {
00060    XML_OP_FIND = 100,
00061    XML_OP_GET,
00062    XML_OP_CREATE,
00063 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 916 of file res_calendar_ews.c.

static void __unreg_module ( void   )  [static]

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

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

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

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

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

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

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

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

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

static void ewscal_destructor ( void *  obj  )  [static]

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

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

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

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

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

static int ewscal_write_event ( struct ast_calendar_event event  )  [static]

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

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

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

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

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

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

Definition at line 478 of file res_calendar_ews.c.

References XML_OP_CREATE, XML_OP_FIND, and XML_OP_GET.

Referenced by send_ews_request_and_parse().

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

static int load_module ( void   )  [static]

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

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

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

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

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

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

Definition at line 451 of file res_calendar_ews.c.

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

Referenced by ewscal_write_event().

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

static time_t mstime_to_time_t ( char *  mstime  )  [static]

Definition at line 158 of file res_calendar_ews.c.

References ast_mktime(), and ast_strptime().

Referenced by cdata(), and parse_cdata().

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

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

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

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

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

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

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

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

Definition at line 148 of file res_calendar_ews.c.

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

Referenced by ewscal_load_calendar().

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

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

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

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

static int unload_module ( void   )  [static]

Definition at line 904 of file res_calendar_ews.c.

References ast_calendar_unregister(), and ewscal_tech.

00905 {
00906    ne_sock_exit();
00907    ast_calendar_unregister(&ewscal_tech);
00908 
00909    return 0;
00910 }

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

Definition at line 124 of file res_calendar_ews.c.

References ao2_ref, and ast_debug.

Referenced by ewscal_load_calendar().

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

static int update_ewscal ( struct ewscal_pvt pvt  )  [static]

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

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


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

Definition at line 916 of file res_calendar_ews.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 916 of file res_calendar_ews.c.

struct ast_calendar_tech ewscal_tech [static]

Definition at line 50 of file res_calendar_ews.c.

Referenced by load_module(), and unload_module().


Generated on Mon Jun 27 16:51:19 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7