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 #include "asterisk.h"
00027
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 287271 $")
00029
00030 #include <ne_request.h>
00031 #include <ne_session.h>
00032 #include <ne_uri.h>
00033 #include <ne_socket.h>
00034 #include <ne_auth.h>
00035 #include <ne_xml.h>
00036 #include <ne_xmlreq.h>
00037 #include <ne_utils.h>
00038 #include <ne_redirect.h>
00039
00040 #include "asterisk/module.h"
00041 #include "asterisk/calendar.h"
00042 #include "asterisk/lock.h"
00043 #include "asterisk/config.h"
00044 #include "asterisk/astobj2.h"
00045
00046 static void *ewscal_load_calendar(void *data);
00047 static void *unref_ewscal(void *obj);
00048 static int ewscal_write_event(struct ast_calendar_event *event);
00049
00050 static struct ast_calendar_tech ewscal_tech = {
00051 .type = "ews",
00052 .description = "MS Exchange Web Service calendars",
00053 .module = AST_MODULE,
00054 .load_calendar = ewscal_load_calendar,
00055 .unref_calendar = unref_ewscal,
00056 .write_event = ewscal_write_event,
00057 };
00058
00059 enum xml_op {
00060 XML_OP_FIND = 100,
00061 XML_OP_GET,
00062 XML_OP_CREATE,
00063 };
00064
00065 struct calendar_id {
00066 struct ast_str *id;
00067 AST_LIST_ENTRY(calendar_id) next;
00068 };
00069
00070 struct xml_context {
00071 ne_xml_parser *parser;
00072 struct ast_str *cdata;
00073 struct ast_calendar_event *event;
00074 enum xml_op op;
00075 struct ewscal_pvt *pvt;
00076 AST_LIST_HEAD_NOLOCK(ids, calendar_id) ids;
00077 };
00078
00079
00080 enum {
00081 XML_EVENT_NAME = 10,
00082 XML_EVENT_START,
00083 XML_EVENT_END,
00084 XML_EVENT_BUSY,
00085 XML_EVENT_ORGANIZER,
00086 XML_EVENT_LOCATION,
00087 XML_EVENT_ATTENDEE_LIST,
00088 XML_EVENT_ATTENDEE,
00089 XML_EVENT_MAILBOX,
00090 XML_EVENT_EMAIL_ADDRESS,
00091 XML_EVENT_CATEGORIES,
00092 XML_EVENT_CATEGORY,
00093 XML_EVENT_IMPORTANCE,
00094 };
00095
00096 struct ewscal_pvt {
00097 AST_DECLARE_STRING_FIELDS(
00098 AST_STRING_FIELD(url);
00099 AST_STRING_FIELD(user);
00100 AST_STRING_FIELD(secret);
00101 );
00102 struct ast_calendar *owner;
00103 ne_uri uri;
00104 ne_session *session;
00105 struct ao2_container *events;
00106 unsigned int items;
00107 };
00108
00109 static void ewscal_destructor(void *obj)
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 }
00123
00124 static void *unref_ewscal(void *obj)
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 }
00132
00133 static int auth_credentials(void *userdata, const char *realm, int attempts, char *username, char *secret)
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 }
00147
00148 static int ssl_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
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;
00156 }
00157
00158 static time_t mstime_to_time_t(char *mstime)
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 }
00169
00170 static int startelm(void *userdata, int parent, const char *nspace, const char *name, const char **atts)
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
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
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
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
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
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
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
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
00258 return XML_EVENT_START;
00259 } else if (!strcmp(name, "End")) {
00260
00261 return XML_EVENT_END;
00262 } else if (!strcmp(name, "LegacyFreeBusyStatus")) {
00263
00264 return XML_EVENT_BUSY;
00265 } else if (!strcmp(name, "Organizer") ||
00266 (parent == XML_EVENT_ORGANIZER && (!strcmp(name, "Mailbox") ||
00267 !strcmp(name, "Name")))) {
00268
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
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
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
00290 return XML_EVENT_CATEGORY;
00291 } else if (!strcmp(name, "Importance")) {
00292
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 }
00314
00315 static int cdata(void *userdata, int state, const char *cdata, size_t len)
00316 {
00317 struct xml_context *ctx = userdata;
00318 char data[len + 1];
00319
00320
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 }
00373
00374 static int endelm(void *userdata, int state, const char *nspace, const char *name)
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
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
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
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
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
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
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
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 }
00450
00451 static const char *mstime(time_t t, char *buf, size_t buflen)
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 }
00463
00464 static const char *msstatus(enum ast_calendar_busy_state state)
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 }
00477
00478 static const char *get_soap_action(enum xml_op op)
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 }
00491
00492 static int send_ews_request_and_parse(struct ast_str *request, struct xml_context *ctx)
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
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
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
00520 ne_set_request_body_buffer(req, ast_str_buffer(request), ast_str_strlen(request));
00521
00522
00523 parser = ne_xml_create();
00524 ctx->parser = parser;
00525 ne_xml_push_handler(parser, startelm, cdata, endelm, ctx);
00526
00527
00528 ret = ne_xml_dispatch_request(req, parser);
00529 if (ret != NE_OK) {
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
00537 ne_request_destroy(req);
00538 ne_xml_destroy(parser);
00539
00540 return 0;
00541 }
00542
00543 static int ewscal_write_event(struct ast_calendar_event *event)
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
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
00611 if (strlen(event->categories) > 0) {
00612 ast_str_append(&request, 0, "<Categories>");
00613 categories = ast_strdupa(event->categories);
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
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 }
00630
00631 static struct calendar_id *get_ewscal_ids_for(struct ewscal_pvt *pvt)
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
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
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\"/>"
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
00679 );
00680
00681 AST_LIST_HEAD_INIT_NOLOCK(&ctx.ids);
00682
00683
00684 if (send_ews_request_and_parse(request, &ctx)) {
00685 ast_free(request);
00686 return NULL;
00687 }
00688
00689
00690 ast_free(request);
00691
00692 return AST_LIST_FIRST(&ctx.ids);
00693 }
00694
00695 static int parse_ewscal_id(struct ewscal_pvt *pvt, const char *id) {
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 }
00732
00733 static int update_ewscal(struct ewscal_pvt *pvt)
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 }
00750
00751 static void *ewscal_load_calendar(void *void_data)
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
00856 update_ewscal(pvt);
00857
00858 ao2_unlock(cal);
00859
00860
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 }
00887
00888 static int load_module(void)
00889 {
00890
00891
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 }
00903
00904 static int unload_module(void)
00905 {
00906 ne_sock_exit();
00907 ast_calendar_unregister(&ewscal_tech);
00908
00909 return 0;
00910 }
00911
00912 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk MS Exchange Web Service Calendar Integration",
00913 .load = load_module,
00914 .unload = unload_module,
00915 .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
00916 );