00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 366740 $")
00031
00032 #include <ne_request.h>
00033 #include <ne_session.h>
00034 #include <ne_uri.h>
00035 #include <ne_socket.h>
00036 #include <ne_auth.h>
00037 #include <ne_xml.h>
00038 #include <ne_xmlreq.h>
00039 #include <ne_utils.h>
00040 #include <ne_redirect.h>
00041
00042 #include "asterisk/module.h"
00043 #include "asterisk/calendar.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/config.h"
00046 #include "asterisk/astobj2.h"
00047
00048 static void *ewscal_load_calendar(void *data);
00049 static void *unref_ewscal(void *obj);
00050 static int ewscal_write_event(struct ast_calendar_event *event);
00051
00052 static struct ast_calendar_tech ewscal_tech = {
00053 .type = "ews",
00054 .description = "MS Exchange Web Service calendars",
00055 .module = AST_MODULE,
00056 .load_calendar = ewscal_load_calendar,
00057 .unref_calendar = unref_ewscal,
00058 .write_event = ewscal_write_event,
00059 };
00060
00061 enum xml_op {
00062 XML_OP_FIND = 100,
00063 XML_OP_GET,
00064 XML_OP_CREATE,
00065 };
00066
00067 struct calendar_id {
00068 struct ast_str *id;
00069 AST_LIST_ENTRY(calendar_id) next;
00070 };
00071
00072 struct xml_context {
00073 ne_xml_parser *parser;
00074 struct ast_str *cdata;
00075 struct ast_calendar_event *event;
00076 enum xml_op op;
00077 struct ewscal_pvt *pvt;
00078 AST_LIST_HEAD_NOLOCK(ids, calendar_id) ids;
00079 };
00080
00081
00082 enum {
00083 XML_EVENT_NAME = 10,
00084 XML_EVENT_START,
00085 XML_EVENT_END,
00086 XML_EVENT_BUSY,
00087 XML_EVENT_ORGANIZER,
00088 XML_EVENT_LOCATION,
00089 XML_EVENT_ATTENDEE_LIST,
00090 XML_EVENT_ATTENDEE,
00091 XML_EVENT_MAILBOX,
00092 XML_EVENT_EMAIL_ADDRESS,
00093 XML_EVENT_CATEGORIES,
00094 XML_EVENT_CATEGORY,
00095 XML_EVENT_IMPORTANCE,
00096 };
00097
00098 struct ewscal_pvt {
00099 AST_DECLARE_STRING_FIELDS(
00100 AST_STRING_FIELD(url);
00101 AST_STRING_FIELD(user);
00102 AST_STRING_FIELD(secret);
00103 );
00104 struct ast_calendar *owner;
00105 ne_uri uri;
00106 ne_session *session;
00107 struct ao2_container *events;
00108 unsigned int items;
00109 };
00110
00111 static void ewscal_destructor(void *obj)
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 }
00125
00126 static void *unref_ewscal(void *obj)
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 }
00134
00135 static int auth_credentials(void *userdata, const char *realm, int attempts, char *username, char *secret)
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 }
00149
00150 static int ssl_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
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;
00158 }
00159
00160 static time_t mstime_to_time_t(char *mstime)
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 }
00171
00172 static int startelm(void *userdata, int parent, const char *nspace, const char *name, const char **atts)
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
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
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
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
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
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
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
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
00260 return XML_EVENT_START;
00261 } else if (!strcmp(name, "End")) {
00262
00263 return XML_EVENT_END;
00264 } else if (!strcmp(name, "LegacyFreeBusyStatus")) {
00265
00266 return XML_EVENT_BUSY;
00267 } else if (!strcmp(name, "Organizer") ||
00268 (parent == XML_EVENT_ORGANIZER && (!strcmp(name, "Mailbox") ||
00269 !strcmp(name, "Name")))) {
00270
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
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
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
00292 return XML_EVENT_CATEGORY;
00293 } else if (!strcmp(name, "Importance")) {
00294
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 }
00316
00317 static int cdata(void *userdata, int state, const char *cdata, size_t len)
00318 {
00319 struct xml_context *ctx = userdata;
00320 char data[len + 1];
00321
00322
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 }
00375
00376 static int endelm(void *userdata, int state, const char *nspace, const char *name)
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
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
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
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
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
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 } else {
00428 ast_free(attendee);
00429 }
00430 ast_debug(3, "EWS: XML: attendee address '%s'\n", ast_str_buffer(ctx->cdata));
00431 ast_str_reset(ctx->cdata);
00432 } else if (!strcmp(name, "CalendarItem")) {
00433
00434 ast_debug(3, "EWS: XML: </CalendarItem>\n");
00435 ast_free(ctx->cdata);
00436 if (ctx->event) {
00437 ao2_link(ctx->pvt->events, ctx->event);
00438 ctx->event = ast_calendar_unref_event(ctx->event);
00439 } else {
00440 ast_log(LOG_ERROR, "Event data ended in XML, but event object does not exist!\n");
00441 return 1;
00442 }
00443 } else if (!strcmp(name, "Envelope")) {
00444
00445 ast_debug(3, "EWS: XML: %d of %d event(s) has been parsed…\n", ao2_container_count(ctx->pvt->events), ctx->pvt->items);
00446 if (ao2_container_count(ctx->pvt->events) >= ctx->pvt->items) {
00447 ast_debug(3, "EWS: XML: All events has been parsed, merging…\n");
00448 ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events);
00449 }
00450 }
00451
00452 return 0;
00453 }
00454
00455 static const char *mstime(time_t t, char *buf, size_t buflen)
00456 {
00457 struct timeval tv = {
00458 .tv_sec = t,
00459 };
00460 struct ast_tm tm;
00461
00462 ast_localtime(&tv, &tm, "utc");
00463 ast_strftime(buf, buflen, "%FT%TZ", &tm);
00464
00465 return S_OR(buf, "");
00466 }
00467
00468 static const char *msstatus(enum ast_calendar_busy_state state)
00469 {
00470 switch (state) {
00471 case AST_CALENDAR_BS_BUSY_TENTATIVE:
00472 return "Tentative";
00473 case AST_CALENDAR_BS_BUSY:
00474 return "Busy";
00475 case AST_CALENDAR_BS_FREE:
00476 return "Free";
00477 default:
00478 return "";
00479 }
00480 }
00481
00482 static const char *get_soap_action(enum xml_op op)
00483 {
00484 switch (op) {
00485 case XML_OP_FIND:
00486 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/FindItem\"";
00487 case XML_OP_GET:
00488 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/GetItem\"";
00489 case XML_OP_CREATE:
00490 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem\"";
00491 }
00492
00493 return "";
00494 }
00495
00496 static int send_ews_request_and_parse(struct ast_str *request, struct xml_context *ctx)
00497 {
00498 int ret;
00499 ne_request *req;
00500 ne_xml_parser *parser;
00501
00502 ast_debug(3, "EWS: HTTP request...\n");
00503 if (!(ctx && ctx->pvt)) {
00504 ast_log(LOG_ERROR, "There is no private!\n");
00505 return -1;
00506 }
00507
00508 if (!ast_str_strlen(request)) {
00509 ast_log(LOG_ERROR, "No request to send!\n");
00510 return -1;
00511 }
00512
00513 ast_debug(3, "%s\n", ast_str_buffer(request));
00514
00515
00516 req = ne_request_create(ctx->pvt->session, "POST", ctx->pvt->uri.path);
00517 ne_set_request_flag(req, NE_REQFLAG_IDEMPOTENT, 0);
00518
00519
00520 ne_add_request_header(req, "Content-Type", "text/xml; charset=utf-8");
00521 ne_add_request_header(req, "SOAPAction", get_soap_action(ctx->op));
00522
00523
00524 ne_set_request_body_buffer(req, ast_str_buffer(request), ast_str_strlen(request));
00525
00526
00527 parser = ne_xml_create();
00528 ctx->parser = parser;
00529 ne_xml_push_handler(parser, startelm, cdata, endelm, ctx);
00530
00531
00532 ret = ne_xml_dispatch_request(req, parser);
00533 if (ret != NE_OK) {
00534 ast_log(LOG_WARNING, "Unable to communicate with Exchange Web Service at '%s': %s\n", ctx->pvt->url, ne_get_error(ctx->pvt->session));
00535 ne_request_destroy(req);
00536 ne_xml_destroy(parser);
00537 return -1;
00538 }
00539
00540
00541 ne_request_destroy(req);
00542 ne_xml_destroy(parser);
00543
00544 return 0;
00545 }
00546
00547 static int ewscal_write_event(struct ast_calendar_event *event)
00548 {
00549 struct ast_str *request;
00550 struct ewscal_pvt *pvt = event->owner->tech_pvt;
00551 char start[21], end[21];
00552 struct xml_context ctx = {
00553 .op = XML_OP_CREATE,
00554 .pvt = pvt,
00555 };
00556 int ret;
00557 char *category, *categories;
00558
00559 if (!pvt) {
00560 return -1;
00561 }
00562
00563 if (!(request = ast_str_create(1024))) {
00564 return -1;
00565 }
00566
00567 ast_str_set(&request, 0,
00568 "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
00569 "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
00570 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00571 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00572 "<soap:Body>"
00573 "<CreateItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\" "
00574 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
00575 "SendMeetingInvitations=\"SendToNone\" >"
00576 "<SavedItemFolderId>"
00577 "<t:DistinguishedFolderId Id=\"calendar\"/>"
00578 "</SavedItemFolderId>"
00579 "<Items>"
00580 "<t:CalendarItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00581 "<Subject>%s</Subject>"
00582 "<Body BodyType=\"Text\">%s</Body>"
00583 "<ReminderIsSet>false</ReminderIsSet>"
00584 "<Start>%s</Start>"
00585 "<End>%s</End>"
00586 "<IsAllDayEvent>false</IsAllDayEvent>"
00587 "<LegacyFreeBusyStatus>%s</LegacyFreeBusyStatus>"
00588 "<Location>%s</Location>",
00589 event->summary,
00590 event->description,
00591 mstime(event->start, start, sizeof(start)),
00592 mstime(event->end, end, sizeof(end)),
00593 msstatus(event->busy_state),
00594 event->location
00595 );
00596
00597 switch (event->priority) {
00598 case 1:
00599 case 2:
00600 case 3:
00601 case 4:
00602 ast_str_append(&request, 0, "<Importance>High</Importance>");
00603 break;
00604 case 5:
00605 ast_str_append(&request, 0, "<Importance>Normal</Importance>");
00606 break;
00607 case 6:
00608 case 7:
00609 case 8:
00610 case 9:
00611 ast_str_append(&request, 0, "<Importance>Low</Importance>");
00612 break;
00613 }
00614
00615 if (strlen(event->categories) > 0) {
00616 ast_str_append(&request, 0, "<Categories>");
00617 categories = ast_strdupa(event->categories);
00618 category = strsep(&categories, ",");
00619 while (category != NULL) {
00620 ast_str_append(&request, 0, "<String>%s</String>", category);
00621 category = strsep(&categories, ",");
00622 }
00623 ast_str_append(&request, 0, "</Categories>");
00624 }
00625
00626 ast_str_append(&request, 0, "</t:CalendarItem></Items></CreateItem></soap:Body></soap:Envelope>");
00627
00628 ret = send_ews_request_and_parse(request, &ctx);
00629
00630 ast_free(request);
00631
00632 return ret;
00633 }
00634
00635 static struct calendar_id *get_ewscal_ids_for(struct ewscal_pvt *pvt)
00636 {
00637 char start[21], end[21];
00638 struct ast_tm tm;
00639 struct timeval tv;
00640 struct ast_str *request;
00641 struct xml_context ctx = {
00642 .op = XML_OP_FIND,
00643 .pvt = pvt,
00644 };
00645
00646 ast_debug(5, "EWS: get_ewscal_ids_for()\n");
00647
00648 if (!pvt) {
00649 ast_log(LOG_ERROR, "There is no private!\n");
00650 return NULL;
00651 }
00652
00653
00654 tv = ast_tvnow();
00655 ast_localtime(&tv, &tm, "UTC");
00656 ast_strftime(start, sizeof(start), "%FT%TZ", &tm);
00657 tv.tv_sec += 60 * pvt->owner->timeframe;
00658 ast_localtime(&tv, &tm, "UTC");
00659 ast_strftime(end, sizeof(end), "%FT%TZ", &tm);
00660
00661
00662 if (!(request = ast_str_create(512))) {
00663 return NULL;
00664 }
00665
00666 ast_str_set(&request, 0,
00667 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00668 "xmlns:ns1=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
00669 "xmlns:ns2=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
00670 "<SOAP-ENV:Body>"
00671 "<ns2:FindItem Traversal=\"Shallow\">"
00672 "<ns2:ItemShape>"
00673 "<ns1:BaseShape>IdOnly</ns1:BaseShape>"
00674 "</ns2:ItemShape>"
00675 "<ns2:CalendarView StartDate=\"%s\" EndDate=\"%s\"/>"
00676 "<ns2:ParentFolderIds>"
00677 "<ns1:DistinguishedFolderId Id=\"calendar\"/>"
00678 "</ns2:ParentFolderIds>"
00679 "</ns2:FindItem>"
00680 "</SOAP-ENV:Body>"
00681 "</SOAP-ENV:Envelope>",
00682 start, end
00683 );
00684
00685 AST_LIST_HEAD_INIT_NOLOCK(&ctx.ids);
00686
00687
00688 if (send_ews_request_and_parse(request, &ctx)) {
00689 ast_free(request);
00690 return NULL;
00691 }
00692
00693
00694 ast_free(request);
00695
00696 return AST_LIST_FIRST(&ctx.ids);
00697 }
00698
00699 static int parse_ewscal_id(struct ewscal_pvt *pvt, const char *id) {
00700 struct ast_str *request;
00701 struct xml_context ctx = {
00702 .pvt = pvt,
00703 .op = XML_OP_GET,
00704 };
00705
00706 if (!(request = ast_str_create(512))) {
00707 return -1;
00708 }
00709
00710 ast_str_set(&request, 0,
00711 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00712 "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00713 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00714 "<soap:Body>"
00715 "<GetItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
00716 "<ItemShape>"
00717 "<t:BaseShape>AllProperties</t:BaseShape>"
00718 "</ItemShape>"
00719 "<ItemIds>"
00720 "<t:ItemId Id=\"%s\"/>"
00721 "</ItemIds>"
00722 "</GetItem>"
00723 "</soap:Body>"
00724 "</soap:Envelope>", id
00725 );
00726
00727 if (send_ews_request_and_parse(request, &ctx)) {
00728 ast_free(request);
00729 return -1;
00730 }
00731
00732 ast_free(request);
00733
00734 return 0;
00735 }
00736
00737 static int update_ewscal(struct ewscal_pvt *pvt)
00738 {
00739 struct calendar_id *id_head;
00740 struct calendar_id *iter;
00741
00742 if (!(id_head = get_ewscal_ids_for(pvt))) {
00743 return 0;
00744 }
00745
00746 for (iter = id_head; iter; iter = AST_LIST_NEXT(iter, next)) {
00747 parse_ewscal_id(pvt, ast_str_buffer(iter->id));
00748 ast_free(iter->id);
00749 ast_free(iter);
00750 }
00751
00752 return 0;
00753 }
00754
00755 static void *ewscal_load_calendar(void *void_data)
00756 {
00757 struct ewscal_pvt *pvt;
00758 const struct ast_config *cfg;
00759 struct ast_variable *v;
00760 struct ast_calendar *cal = void_data;
00761 ast_mutex_t refreshlock;
00762
00763 ast_debug(5, "EWS: ewscal_load_calendar()\n");
00764
00765 if (!(cal && (cfg = ast_calendar_config_acquire()))) {
00766 ast_log(LOG_ERROR, "You must enable calendar support for res_ewscal to load\n");
00767 return NULL;
00768 }
00769
00770 if (ao2_trylock(cal)) {
00771 if (cal->unloading) {
00772 ast_log(LOG_WARNING, "Unloading module, load_calendar cancelled.\n");
00773 } else {
00774 ast_log(LOG_WARNING, "Could not lock calendar, aborting!\n");
00775 }
00776 ast_calendar_config_release();
00777 return NULL;
00778 }
00779
00780 if (!(pvt = ao2_alloc(sizeof(*pvt), ewscal_destructor))) {
00781 ast_log(LOG_ERROR, "Could not allocate ewscal_pvt structure for calendar: %s\n", cal->name);
00782 ast_calendar_config_release();
00783 return NULL;
00784 }
00785
00786 pvt->owner = cal;
00787
00788 if (!(pvt->events = ast_calendar_event_container_alloc())) {
00789 ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name);
00790 pvt = unref_ewscal(pvt);
00791 ao2_unlock(cal);
00792 ast_calendar_config_release();
00793 return NULL;
00794 }
00795
00796 if (ast_string_field_init(pvt, 32)) {
00797 ast_log(LOG_ERROR, "Couldn't allocate string field space for calendar: %s\n", cal->name);
00798 pvt = unref_ewscal(pvt);
00799 ao2_unlock(cal);
00800 ast_calendar_config_release();
00801 return NULL;
00802 }
00803
00804 for (v = ast_variable_browse(cfg, cal->name); v; v = v->next) {
00805 if (!strcasecmp(v->name, "url")) {
00806 ast_string_field_set(pvt, url, v->value);
00807 } else if (!strcasecmp(v->name, "user")) {
00808 ast_string_field_set(pvt, user, v->value);
00809 } else if (!strcasecmp(v->name, "secret")) {
00810 ast_string_field_set(pvt, secret, v->value);
00811 }
00812 }
00813
00814 ast_calendar_config_release();
00815
00816 if (ast_strlen_zero(pvt->url)) {
00817 ast_log(LOG_WARNING, "No URL was specified for Exchange Web Service calendar '%s' - skipping.\n", cal->name);
00818 pvt = unref_ewscal(pvt);
00819 ao2_unlock(cal);
00820 return NULL;
00821 }
00822
00823 if (ne_uri_parse(pvt->url, &pvt->uri) || pvt->uri.host == NULL || pvt->uri.path == NULL) {
00824 ast_log(LOG_WARNING, "Could not parse url '%s' for Exchange Web Service calendar '%s' - skipping.\n", pvt->url, cal->name);
00825 pvt = unref_ewscal(pvt);
00826 ao2_unlock(cal);
00827 return NULL;
00828 }
00829
00830 if (pvt->uri.scheme == NULL) {
00831 pvt->uri.scheme = "http";
00832 }
00833
00834 if (pvt->uri.port == 0) {
00835 pvt->uri.port = ne_uri_defaultport(pvt->uri.scheme);
00836 }
00837
00838 ast_debug(3, "ne_uri.scheme = %s\n", pvt->uri.scheme);
00839 ast_debug(3, "ne_uri.host = %s\n", pvt->uri.host);
00840 ast_debug(3, "ne_uri.port = %u\n", pvt->uri.port);
00841 ast_debug(3, "ne_uri.path = %s\n", pvt->uri.path);
00842 ast_debug(3, "user = %s\n", pvt->user);
00843 ast_debug(3, "secret = %s\n", pvt->secret);
00844
00845 pvt->session = ne_session_create(pvt->uri.scheme, pvt->uri.host, pvt->uri.port);
00846 ne_redirect_register(pvt->session);
00847 ne_set_server_auth(pvt->session, auth_credentials, pvt);
00848 ne_set_useragent(pvt->session, "Asterisk");
00849
00850 if (!strcasecmp(pvt->uri.scheme, "https")) {
00851 ne_ssl_trust_default_ca(pvt->session);
00852 ne_ssl_set_verify(pvt->session, ssl_verify, pvt);
00853 }
00854
00855 cal->tech_pvt = pvt;
00856
00857 ast_mutex_init(&refreshlock);
00858
00859
00860 update_ewscal(pvt);
00861
00862 ao2_unlock(cal);
00863
00864
00865 for (;;) {
00866 struct timeval tv = ast_tvnow();
00867 struct timespec ts = {0,};
00868
00869 ts.tv_sec = tv.tv_sec + (60 * pvt->owner->refresh);
00870
00871 ast_mutex_lock(&refreshlock);
00872 while (!pvt->owner->unloading) {
00873 if (ast_cond_timedwait(&pvt->owner->unload, &refreshlock, &ts) == ETIMEDOUT) {
00874 break;
00875 }
00876 }
00877 ast_mutex_unlock(&refreshlock);
00878
00879 if (pvt->owner->unloading) {
00880 ast_debug(10, "Skipping refresh since we got a shutdown signal\n");
00881 return NULL;
00882 }
00883
00884 ast_debug(10, "Refreshing after %d minute timeout\n", pvt->owner->refresh);
00885
00886 update_ewscal(pvt);
00887 }
00888
00889 return NULL;
00890 }
00891
00892 static int load_module(void)
00893 {
00894
00895
00896 if (ne_version_match(0, 29)) {
00897 ast_log(LOG_ERROR, "Exchange Web Service calendar module require neon >= 0.29.1, but %s is installed.\n", ne_version_string());
00898 return AST_MODULE_LOAD_DECLINE;
00899 }
00900
00901 if (ast_calendar_register(&ewscal_tech) && (ne_sock_init() == 0)) {
00902 return AST_MODULE_LOAD_DECLINE;
00903 }
00904
00905 return AST_MODULE_LOAD_SUCCESS;
00906 }
00907
00908 static int unload_module(void)
00909 {
00910 ne_sock_exit();
00911 ast_calendar_unregister(&ewscal_tech);
00912
00913 return 0;
00914 }
00915
00916 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk MS Exchange Web Service Calendar Integration",
00917 .load = load_module,
00918 .unload = unload_module,
00919 .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
00920 );