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: 425286 $")
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_CALENDAR_ITEM = 9,
00084 XML_EVENT_NAME = 10,
00085 XML_EVENT_DESCRIPTION,
00086 XML_EVENT_START,
00087 XML_EVENT_END,
00088 XML_EVENT_BUSY,
00089 XML_EVENT_ORGANIZER,
00090 XML_EVENT_LOCATION,
00091 XML_EVENT_ATTENDEE_LIST,
00092 XML_EVENT_ATTENDEE,
00093 XML_EVENT_MAILBOX,
00094 XML_EVENT_EMAIL_ADDRESS,
00095 XML_EVENT_CATEGORIES,
00096 XML_EVENT_CATEGORY,
00097 XML_EVENT_IMPORTANCE,
00098 };
00099
00100 struct ewscal_pvt {
00101 AST_DECLARE_STRING_FIELDS(
00102 AST_STRING_FIELD(url);
00103 AST_STRING_FIELD(user);
00104 AST_STRING_FIELD(secret);
00105 );
00106 struct ast_calendar *owner;
00107 ne_uri uri;
00108 ne_session *session;
00109 struct ao2_container *events;
00110 unsigned int items;
00111 };
00112
00113 static void ewscal_destructor(void *obj)
00114 {
00115 struct ewscal_pvt *pvt = obj;
00116
00117 ast_debug(1, "Destroying pvt for Exchange Web Service calendar %s\n", "pvt->owner->name");
00118 if (pvt->session) {
00119 ne_session_destroy(pvt->session);
00120 }
00121 ast_string_field_free_memory(pvt);
00122
00123 ao2_callback(pvt->events, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
00124
00125 ao2_ref(pvt->events, -1);
00126 }
00127
00128 static void *unref_ewscal(void *obj)
00129 {
00130 struct ewscal_pvt *pvt = obj;
00131
00132 ast_debug(5, "EWS: unref_ewscal()\n");
00133 ao2_ref(pvt, -1);
00134 return NULL;
00135 }
00136
00137 static int auth_credentials(void *userdata, const char *realm, int attempts, char *username, char *secret)
00138 {
00139 struct ewscal_pvt *pvt = userdata;
00140
00141 if (attempts > 1) {
00142 ast_log(LOG_WARNING, "Invalid username or password for Exchange Web Service calendar '%s'\n", pvt->owner->name);
00143 return -1;
00144 }
00145
00146 ne_strnzcpy(username, pvt->user, NE_ABUFSIZ);
00147 ne_strnzcpy(secret, pvt->secret, NE_ABUFSIZ);
00148
00149 return 0;
00150 }
00151
00152 static int ssl_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
00153 {
00154 struct ewscal_pvt *pvt = userdata;
00155 if (failures & NE_SSL_UNTRUSTED) {
00156 ast_log(LOG_WARNING, "Untrusted SSL certificate for calendar %s!\n", pvt->owner->name);
00157 return 0;
00158 }
00159 return 1;
00160 }
00161
00162 static time_t mstime_to_time_t(char *mstime)
00163 {
00164 struct ast_tm tm;
00165 struct timeval tv;
00166
00167 if (ast_strptime(mstime, "%FT%TZ", &tm)) {
00168 tv = ast_mktime(&tm, "UTC");
00169 return tv.tv_sec;
00170 }
00171 return 0;
00172 }
00173
00174 static int startelm(void *userdata, int parent, const char *nspace, const char *name, const char **atts)
00175 {
00176 struct xml_context *ctx = userdata;
00177
00178 ast_debug(5, "EWS: XML: Start: %s\n", name);
00179 if (ctx->op == XML_OP_CREATE) {
00180 return NE_XML_DECLINE;
00181 }
00182
00183
00184 if (!strcmp(name, "Envelope") ||
00185 (!strcmp(name, "Body") && parent != XML_EVENT_CALENDAR_ITEM) ||
00186 !strcmp(name, "FindItemResponse") ||
00187 !strcmp(name, "GetItemResponse") ||
00188 !strcmp(name, "CreateItemResponse") ||
00189 !strcmp(name, "ResponseMessages") ||
00190 !strcmp(name, "FindItemResponseMessage") || !strcmp(name, "GetItemResponseMessage") ||
00191 !strcmp(name, "Items")
00192 ) {
00193 return 1;
00194 } else if (!strcmp(name, "RootFolder")) {
00195
00196 unsigned int items;
00197
00198 ast_debug(3, "EWS: XML: <RootFolder>\n");
00199 if (sscanf(ne_xml_get_attr(ctx->parser, atts, NULL, "TotalItemsInView"), "%u", &items) != 1) {
00200
00201 ne_xml_set_error(ctx->parser, "Could't read number of events.");
00202 return NE_XML_ABORT;
00203 }
00204
00205 ast_debug(3, "EWS: %u calendar items to load\n", items);
00206 ctx->pvt->items = items;
00207 if (items < 1) {
00208
00209 ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events);
00210 return NE_XML_DECLINE;
00211 }
00212 return 1;
00213 } else if (!strcmp(name, "CalendarItem")) {
00214
00215 ast_debug(3, "EWS: XML: <CalendarItem>\n");
00216 if (!(ctx->pvt && ctx->pvt->owner)) {
00217 ast_log(LOG_ERROR, "Require a private structure with an owner\n");
00218 return NE_XML_ABORT;
00219 }
00220
00221 ctx->event = ast_calendar_event_alloc(ctx->pvt->owner);
00222 if (!ctx->event) {
00223 ast_log(LOG_ERROR, "Could not allocate an event!\n");
00224 return NE_XML_ABORT;
00225 }
00226
00227 ctx->cdata = ast_str_create(64);
00228 if (!ctx->cdata) {
00229 ast_log(LOG_ERROR, "Could not allocate CDATA!\n");
00230 return NE_XML_ABORT;
00231 }
00232
00233 return XML_EVENT_CALENDAR_ITEM;
00234 } else if (!strcmp(name, "ItemId")) {
00235
00236 if (ctx->op == XML_OP_FIND) {
00237 struct calendar_id *id;
00238 if (!(id = ast_calloc(1, sizeof(*id)))) {
00239 return NE_XML_ABORT;
00240 }
00241 if (!(id->id = ast_str_create(256))) {
00242 ast_free(id);
00243 return NE_XML_ABORT;
00244 }
00245 ast_str_set(&id->id, 0, "%s", ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
00246 AST_LIST_INSERT_TAIL(&ctx->ids, id, next);
00247 ast_debug(3, "EWS_FIND: XML: UID: %s\n", ast_str_buffer(id->id));
00248 } else {
00249 ast_debug(3, "EWS_GET: XML: UID: %s\n", ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
00250 ast_string_field_set(ctx->event, uid, ne_xml_get_attr(ctx->parser, atts, NULL, "Id"));
00251 }
00252 return XML_EVENT_NAME;
00253 } else if (!strcmp(name, "Subject")) {
00254
00255 if (!ctx->cdata) {
00256 return NE_XML_ABORT;
00257 }
00258 ast_str_reset(ctx->cdata);
00259 return XML_EVENT_NAME;
00260 } else if (!strcmp(name, "Body") && parent == XML_EVENT_CALENDAR_ITEM) {
00261
00262 if (!ctx->cdata) {
00263 return NE_XML_ABORT;
00264 }
00265 ast_str_reset(ctx->cdata);
00266 return XML_EVENT_DESCRIPTION;
00267 } else if (!strcmp(name, "Start")) {
00268
00269 return XML_EVENT_START;
00270 } else if (!strcmp(name, "End")) {
00271
00272 return XML_EVENT_END;
00273 } else if (!strcmp(name, "LegacyFreeBusyStatus")) {
00274
00275 return XML_EVENT_BUSY;
00276 } else if (!strcmp(name, "Organizer") ||
00277 (parent == XML_EVENT_ORGANIZER && (!strcmp(name, "Mailbox") ||
00278 !strcmp(name, "Name")))) {
00279
00280 if (!ctx->cdata) {
00281 return NE_XML_ABORT;
00282 }
00283 ast_str_reset(ctx->cdata);
00284 return XML_EVENT_ORGANIZER;
00285 } else if (!strcmp(name, "Location")) {
00286
00287 if (!ctx->cdata) {
00288 return NE_XML_ABORT;
00289 }
00290 ast_str_reset(ctx->cdata);
00291 return XML_EVENT_LOCATION;
00292 } else if (!strcmp(name, "Categories")) {
00293
00294 if (!ctx->cdata) {
00295 return NE_XML_ABORT;
00296 }
00297 ast_str_reset(ctx->cdata);
00298 return XML_EVENT_CATEGORIES;
00299 } else if (parent == XML_EVENT_CATEGORIES && !strcmp(name, "String")) {
00300
00301 return XML_EVENT_CATEGORY;
00302 } else if (!strcmp(name, "Importance")) {
00303
00304 if (!ctx->cdata) {
00305 return NE_XML_ABORT;
00306 }
00307 ast_str_reset(ctx->cdata);
00308 return XML_EVENT_IMPORTANCE;
00309 } else if (!strcmp(name, "RequiredAttendees") || !strcmp(name, "OptionalAttendees")) {
00310 return XML_EVENT_ATTENDEE_LIST;
00311 } else if (!strcmp(name, "Attendee") && parent == XML_EVENT_ATTENDEE_LIST) {
00312 return XML_EVENT_ATTENDEE;
00313 } else if (!strcmp(name, "Mailbox") && parent == XML_EVENT_ATTENDEE) {
00314 return XML_EVENT_MAILBOX;
00315 } else if (!strcmp(name, "EmailAddress") && parent == XML_EVENT_MAILBOX) {
00316 if (!ctx->cdata) {
00317 return NE_XML_ABORT;
00318 }
00319 ast_str_reset(ctx->cdata);
00320 return XML_EVENT_EMAIL_ADDRESS;
00321 }
00322
00323 return NE_XML_DECLINE;
00324 }
00325
00326 static int cdata(void *userdata, int state, const char *cdata, size_t len)
00327 {
00328 struct xml_context *ctx = userdata;
00329 char data[len + 1];
00330
00331
00332 if (state < XML_EVENT_NAME || ctx->op == XML_OP_CREATE) {
00333 return 0;
00334 }
00335
00336 if (!ctx->event) {
00337 ast_log(LOG_ERROR, "Parsing event data, but event object does not exist!\n");
00338 return 1;
00339 }
00340
00341 if (!ctx->cdata) {
00342 ast_log(LOG_ERROR, "String for storing CDATA is unitialized!\n");
00343 return 1;
00344 }
00345
00346 ast_copy_string(data, cdata, len + 1);
00347
00348 switch (state) {
00349 case XML_EVENT_START:
00350 ctx->event->start = mstime_to_time_t(data);
00351 break;
00352 case XML_EVENT_END:
00353 ctx->event->end = mstime_to_time_t(data);
00354 break;
00355 case XML_EVENT_BUSY:
00356 if (!strcmp(data, "Busy") || !strcmp(data, "OOF")) {
00357 ast_debug(3, "EWS: XML: Busy: yes\n");
00358 ctx->event->busy_state = AST_CALENDAR_BS_BUSY;
00359 }
00360 else if (!strcmp(data, "Tentative")) {
00361 ast_debug(3, "EWS: XML: Busy: tentative\n");
00362 ctx->event->busy_state = AST_CALENDAR_BS_BUSY_TENTATIVE;
00363 }
00364 else {
00365 ast_debug(3, "EWS: XML: Busy: no\n");
00366 ctx->event->busy_state = AST_CALENDAR_BS_FREE;
00367 }
00368 break;
00369 case XML_EVENT_CATEGORY:
00370 if (ast_str_strlen(ctx->cdata) == 0) {
00371 ast_str_set(&ctx->cdata, 0, "%s", data);
00372 } else {
00373 ast_str_append(&ctx->cdata, 0, ",%s", data);
00374 }
00375 break;
00376 default:
00377 ast_str_append(&ctx->cdata, 0, "%s", data);
00378 }
00379
00380 ast_debug(5, "EWS: XML: CDATA: %s\n", ast_str_buffer(ctx->cdata));
00381
00382 return 0;
00383 }
00384
00385 static int endelm(void *userdata, int state, const char *nspace, const char *name)
00386 {
00387 struct xml_context *ctx = userdata;
00388
00389 ast_debug(5, "EWS: XML: End: %s\n", name);
00390 if (ctx->op == XML_OP_FIND || ctx->op == XML_OP_CREATE) {
00391 return NE_XML_DECLINE;
00392 }
00393
00394 if (!strcmp(name, "Subject")) {
00395
00396 ast_string_field_set(ctx->event, summary, ast_str_buffer(ctx->cdata));
00397 ast_debug(3, "EWS: XML: Summary: %s\n", ctx->event->summary);
00398 ast_str_reset(ctx->cdata);
00399 } else if (!strcmp(name, "Body") && state == XML_EVENT_DESCRIPTION) {
00400
00401 ast_string_field_set(ctx->event, description, ast_str_buffer(ctx->cdata));
00402 ast_debug(3, "EWS: XML: Description: %s\n", ctx->event->description);
00403 ast_str_reset(ctx->cdata);
00404 } else if (!strcmp(name, "Organizer")) {
00405
00406 ast_string_field_set(ctx->event, organizer, ast_str_buffer(ctx->cdata));
00407 ast_debug(3, "EWS: XML: Organizer: %s\n", ctx->event->organizer);
00408 ast_str_reset(ctx->cdata);
00409 } else if (!strcmp(name, "Location")) {
00410
00411 ast_string_field_set(ctx->event, location, ast_str_buffer(ctx->cdata));
00412 ast_debug(3, "EWS: XML: Location: %s\n", ctx->event->location);
00413 ast_str_reset(ctx->cdata);
00414 } else if (!strcmp(name, "Categories")) {
00415
00416 ast_string_field_set(ctx->event, categories, ast_str_buffer(ctx->cdata));
00417 ast_debug(3, "EWS: XML: Categories: %s\n", ctx->event->categories);
00418 ast_str_reset(ctx->cdata);
00419 } else if (!strcmp(name, "Importance")) {
00420
00421 if (!strcmp(ast_str_buffer(ctx->cdata), "Low")) {
00422 ctx->event->priority = 9;
00423 } else if (!strcmp(ast_str_buffer(ctx->cdata), "Normal")) {
00424 ctx->event->priority = 5;
00425 } else if (!strcmp(ast_str_buffer(ctx->cdata), "High")) {
00426 ctx->event->priority = 1;
00427 }
00428 ast_debug(3, "EWS: XML: Importance: %s (%d)\n", ast_str_buffer(ctx->cdata), ctx->event->priority);
00429 ast_str_reset(ctx->cdata);
00430 } else if (state == XML_EVENT_EMAIL_ADDRESS) {
00431 struct ast_calendar_attendee *attendee;
00432
00433 if (!(attendee = ast_calloc(1, sizeof(*attendee)))) {
00434 ctx->event = ast_calendar_unref_event(ctx->event);
00435 return 1;
00436 }
00437
00438 if (ast_str_strlen(ctx->cdata)) {
00439 attendee->data = ast_strdup(ast_str_buffer(ctx->cdata));
00440 AST_LIST_INSERT_TAIL(&ctx->event->attendees, attendee, next);
00441 } else {
00442 ast_free(attendee);
00443 }
00444 ast_debug(3, "EWS: XML: attendee address '%s'\n", ast_str_buffer(ctx->cdata));
00445 ast_str_reset(ctx->cdata);
00446 } else if (!strcmp(name, "CalendarItem")) {
00447
00448 ast_debug(3, "EWS: XML: </CalendarItem>\n");
00449 ast_free(ctx->cdata);
00450 if (ctx->event) {
00451 ao2_link(ctx->pvt->events, ctx->event);
00452 ctx->event = ast_calendar_unref_event(ctx->event);
00453 } else {
00454 ast_log(LOG_ERROR, "Event data ended in XML, but event object does not exist!\n");
00455 return 1;
00456 }
00457 } else if (!strcmp(name, "Envelope")) {
00458
00459 ast_debug(3, "EWS: XML: %d of %u event(s) has been parsed…\n", ao2_container_count(ctx->pvt->events), ctx->pvt->items);
00460 if (ao2_container_count(ctx->pvt->events) >= ctx->pvt->items) {
00461 ast_debug(3, "EWS: XML: All events has been parsed, merging…\n");
00462 ast_calendar_merge_events(ctx->pvt->owner, ctx->pvt->events);
00463 }
00464 }
00465
00466 return 0;
00467 }
00468
00469 static const char *mstime(time_t t, char *buf, size_t buflen)
00470 {
00471 struct timeval tv = {
00472 .tv_sec = t,
00473 };
00474 struct ast_tm tm;
00475
00476 ast_localtime(&tv, &tm, "utc");
00477 ast_strftime(buf, buflen, "%FT%TZ", &tm);
00478
00479 return S_OR(buf, "");
00480 }
00481
00482 static const char *msstatus(enum ast_calendar_busy_state state)
00483 {
00484 switch (state) {
00485 case AST_CALENDAR_BS_BUSY_TENTATIVE:
00486 return "Tentative";
00487 case AST_CALENDAR_BS_BUSY:
00488 return "Busy";
00489 case AST_CALENDAR_BS_FREE:
00490 return "Free";
00491 default:
00492 return "";
00493 }
00494 }
00495
00496 static const char *get_soap_action(enum xml_op op)
00497 {
00498 switch (op) {
00499 case XML_OP_FIND:
00500 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/FindItem\"";
00501 case XML_OP_GET:
00502 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/GetItem\"";
00503 case XML_OP_CREATE:
00504 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem\"";
00505 }
00506
00507 return "";
00508 }
00509
00510 static int send_ews_request_and_parse(struct ast_str *request, struct xml_context *ctx)
00511 {
00512 int ret;
00513 ne_request *req;
00514 ne_xml_parser *parser;
00515
00516 ast_debug(3, "EWS: HTTP request...\n");
00517 if (!(ctx && ctx->pvt)) {
00518 ast_log(LOG_ERROR, "There is no private!\n");
00519 return -1;
00520 }
00521
00522 if (!ast_str_strlen(request)) {
00523 ast_log(LOG_ERROR, "No request to send!\n");
00524 return -1;
00525 }
00526
00527 ast_debug(3, "%s\n", ast_str_buffer(request));
00528
00529
00530 req = ne_request_create(ctx->pvt->session, "POST", ctx->pvt->uri.path);
00531 ne_set_request_flag(req, NE_REQFLAG_IDEMPOTENT, 0);
00532
00533
00534 ne_add_request_header(req, "Content-Type", "text/xml; charset=utf-8");
00535 ne_add_request_header(req, "SOAPAction", get_soap_action(ctx->op));
00536
00537
00538 ne_set_request_body_buffer(req, ast_str_buffer(request), ast_str_strlen(request));
00539
00540
00541 parser = ne_xml_create();
00542 ctx->parser = parser;
00543 ne_xml_push_handler(parser, startelm, cdata, endelm, ctx);
00544
00545
00546 ret = ne_xml_dispatch_request(req, parser);
00547 if (ret != NE_OK) {
00548 ast_log(LOG_WARNING, "Unable to communicate with Exchange Web Service at '%s': %s\n", ctx->pvt->url, ne_get_error(ctx->pvt->session));
00549 ne_request_destroy(req);
00550 ne_xml_destroy(parser);
00551 return -1;
00552 }
00553
00554
00555 ne_request_destroy(req);
00556 ne_xml_destroy(parser);
00557
00558 return 0;
00559 }
00560
00561 static int ewscal_write_event(struct ast_calendar_event *event)
00562 {
00563 struct ast_str *request;
00564 struct ewscal_pvt *pvt = event->owner->tech_pvt;
00565 char start[21], end[21];
00566 struct xml_context ctx = {
00567 .op = XML_OP_CREATE,
00568 .pvt = pvt,
00569 };
00570 int ret;
00571 char *category, *categories;
00572
00573 if (!pvt) {
00574 return -1;
00575 }
00576
00577 if (!(request = ast_str_create(1024))) {
00578 return -1;
00579 }
00580
00581 ast_str_set(&request, 0,
00582 "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
00583 "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
00584 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00585 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00586 "<soap:Body>"
00587 "<CreateItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\" "
00588 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
00589 "SendMeetingInvitations=\"SendToNone\" >"
00590 "<SavedItemFolderId>"
00591 "<t:DistinguishedFolderId Id=\"calendar\"/>"
00592 "</SavedItemFolderId>"
00593 "<Items>"
00594 "<t:CalendarItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00595 "<Subject>%s</Subject>"
00596 "<Body BodyType=\"Text\">%s</Body>"
00597 "<ReminderIsSet>false</ReminderIsSet>"
00598 "<Start>%s</Start>"
00599 "<End>%s</End>"
00600 "<IsAllDayEvent>false</IsAllDayEvent>"
00601 "<LegacyFreeBusyStatus>%s</LegacyFreeBusyStatus>"
00602 "<Location>%s</Location>",
00603 event->summary,
00604 event->description,
00605 mstime(event->start, start, sizeof(start)),
00606 mstime(event->end, end, sizeof(end)),
00607 msstatus(event->busy_state),
00608 event->location
00609 );
00610
00611 switch (event->priority) {
00612 case 1:
00613 case 2:
00614 case 3:
00615 case 4:
00616 ast_str_append(&request, 0, "<Importance>High</Importance>");
00617 break;
00618 case 5:
00619 ast_str_append(&request, 0, "<Importance>Normal</Importance>");
00620 break;
00621 case 6:
00622 case 7:
00623 case 8:
00624 case 9:
00625 ast_str_append(&request, 0, "<Importance>Low</Importance>");
00626 break;
00627 }
00628
00629 if (strlen(event->categories) > 0) {
00630 ast_str_append(&request, 0, "<Categories>");
00631 categories = ast_strdupa(event->categories);
00632 category = strsep(&categories, ",");
00633 while (category != NULL) {
00634 ast_str_append(&request, 0, "<String>%s</String>", category);
00635 category = strsep(&categories, ",");
00636 }
00637 ast_str_append(&request, 0, "</Categories>");
00638 }
00639
00640 ast_str_append(&request, 0, "</t:CalendarItem></Items></CreateItem></soap:Body></soap:Envelope>");
00641
00642 ret = send_ews_request_and_parse(request, &ctx);
00643
00644 ast_free(request);
00645
00646 return ret;
00647 }
00648
00649 static struct calendar_id *get_ewscal_ids_for(struct ewscal_pvt *pvt)
00650 {
00651 char start[21], end[21];
00652 struct ast_tm tm;
00653 struct timeval tv;
00654 struct ast_str *request;
00655 struct xml_context ctx = {
00656 .op = XML_OP_FIND,
00657 .pvt = pvt,
00658 };
00659
00660 ast_debug(5, "EWS: get_ewscal_ids_for()\n");
00661
00662 if (!pvt) {
00663 ast_log(LOG_ERROR, "There is no private!\n");
00664 return NULL;
00665 }
00666
00667
00668 tv = ast_tvnow();
00669 ast_localtime(&tv, &tm, "UTC");
00670 ast_strftime(start, sizeof(start), "%FT%TZ", &tm);
00671 tv.tv_sec += 60 * pvt->owner->timeframe;
00672 ast_localtime(&tv, &tm, "UTC");
00673 ast_strftime(end, sizeof(end), "%FT%TZ", &tm);
00674
00675
00676 if (!(request = ast_str_create(512))) {
00677 return NULL;
00678 }
00679
00680 ast_str_set(&request, 0,
00681 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00682 "xmlns:ns1=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
00683 "xmlns:ns2=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
00684 "<SOAP-ENV:Body>"
00685 "<ns2:FindItem Traversal=\"Shallow\">"
00686 "<ns2:ItemShape>"
00687 "<ns1:BaseShape>IdOnly</ns1:BaseShape>"
00688 "</ns2:ItemShape>"
00689 "<ns2:CalendarView StartDate=\"%s\" EndDate=\"%s\"/>"
00690 "<ns2:ParentFolderIds>"
00691 "<ns1:DistinguishedFolderId Id=\"calendar\"/>"
00692 "</ns2:ParentFolderIds>"
00693 "</ns2:FindItem>"
00694 "</SOAP-ENV:Body>"
00695 "</SOAP-ENV:Envelope>",
00696 start, end
00697 );
00698
00699 AST_LIST_HEAD_INIT_NOLOCK(&ctx.ids);
00700
00701
00702 if (send_ews_request_and_parse(request, &ctx)) {
00703 ast_free(request);
00704 return NULL;
00705 }
00706
00707
00708 ast_free(request);
00709
00710 return AST_LIST_FIRST(&ctx.ids);
00711 }
00712
00713 static int parse_ewscal_id(struct ewscal_pvt *pvt, const char *id) {
00714 struct ast_str *request;
00715 struct xml_context ctx = {
00716 .pvt = pvt,
00717 .op = XML_OP_GET,
00718 };
00719
00720 if (!(request = ast_str_create(512))) {
00721 return -1;
00722 }
00723
00724 ast_str_set(&request, 0,
00725 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00726 "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
00727 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
00728 "<soap:Body>"
00729 "<GetItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
00730 "<ItemShape>"
00731 "<t:BaseShape>AllProperties</t:BaseShape>"
00732 "</ItemShape>"
00733 "<ItemIds>"
00734 "<t:ItemId Id=\"%s\"/>"
00735 "</ItemIds>"
00736 "</GetItem>"
00737 "</soap:Body>"
00738 "</soap:Envelope>", id
00739 );
00740
00741 if (send_ews_request_and_parse(request, &ctx)) {
00742 ast_free(request);
00743 return -1;
00744 }
00745
00746 ast_free(request);
00747
00748 return 0;
00749 }
00750
00751 static int update_ewscal(struct ewscal_pvt *pvt)
00752 {
00753 struct calendar_id *id_head;
00754 struct calendar_id *iter;
00755
00756 if (!(id_head = get_ewscal_ids_for(pvt))) {
00757 return 0;
00758 }
00759
00760 for (iter = id_head; iter; iter = AST_LIST_NEXT(iter, next)) {
00761 parse_ewscal_id(pvt, ast_str_buffer(iter->id));
00762 ast_free(iter->id);
00763 ast_free(iter);
00764 }
00765
00766 return 0;
00767 }
00768
00769 static void *ewscal_load_calendar(void *void_data)
00770 {
00771 struct ewscal_pvt *pvt;
00772 const struct ast_config *cfg;
00773 struct ast_variable *v;
00774 struct ast_calendar *cal = void_data;
00775 ast_mutex_t refreshlock;
00776
00777 ast_debug(5, "EWS: ewscal_load_calendar()\n");
00778
00779 if (!(cal && (cfg = ast_calendar_config_acquire()))) {
00780 ast_log(LOG_ERROR, "You must enable calendar support for res_ewscal to load\n");
00781 return NULL;
00782 }
00783
00784 if (ao2_trylock(cal)) {
00785 if (cal->unloading) {
00786 ast_log(LOG_WARNING, "Unloading module, load_calendar cancelled.\n");
00787 } else {
00788 ast_log(LOG_WARNING, "Could not lock calendar, aborting!\n");
00789 }
00790 ast_calendar_config_release();
00791 return NULL;
00792 }
00793
00794 if (!(pvt = ao2_alloc(sizeof(*pvt), ewscal_destructor))) {
00795 ast_log(LOG_ERROR, "Could not allocate ewscal_pvt structure for calendar: %s\n", cal->name);
00796 ast_calendar_config_release();
00797 return NULL;
00798 }
00799
00800 pvt->owner = cal;
00801
00802 if (!(pvt->events = ast_calendar_event_container_alloc())) {
00803 ast_log(LOG_ERROR, "Could not allocate space for fetching events for calendar: %s\n", cal->name);
00804 pvt = unref_ewscal(pvt);
00805 ao2_unlock(cal);
00806 ast_calendar_config_release();
00807 return NULL;
00808 }
00809
00810 if (ast_string_field_init(pvt, 32)) {
00811 ast_log(LOG_ERROR, "Couldn't allocate string field space for calendar: %s\n", cal->name);
00812 pvt = unref_ewscal(pvt);
00813 ao2_unlock(cal);
00814 ast_calendar_config_release();
00815 return NULL;
00816 }
00817
00818 for (v = ast_variable_browse(cfg, cal->name); v; v = v->next) {
00819 if (!strcasecmp(v->name, "url")) {
00820 ast_string_field_set(pvt, url, v->value);
00821 } else if (!strcasecmp(v->name, "user")) {
00822 ast_string_field_set(pvt, user, v->value);
00823 } else if (!strcasecmp(v->name, "secret")) {
00824 ast_string_field_set(pvt, secret, v->value);
00825 }
00826 }
00827
00828 ast_calendar_config_release();
00829
00830 if (ast_strlen_zero(pvt->url)) {
00831 ast_log(LOG_WARNING, "No URL was specified for Exchange Web Service calendar '%s' - skipping.\n", cal->name);
00832 pvt = unref_ewscal(pvt);
00833 ao2_unlock(cal);
00834 return NULL;
00835 }
00836
00837 if (ne_uri_parse(pvt->url, &pvt->uri) || pvt->uri.host == NULL || pvt->uri.path == NULL) {
00838 ast_log(LOG_WARNING, "Could not parse url '%s' for Exchange Web Service calendar '%s' - skipping.\n", pvt->url, cal->name);
00839 pvt = unref_ewscal(pvt);
00840 ao2_unlock(cal);
00841 return NULL;
00842 }
00843
00844 if (pvt->uri.scheme == NULL) {
00845 pvt->uri.scheme = "http";
00846 }
00847
00848 if (pvt->uri.port == 0) {
00849 pvt->uri.port = ne_uri_defaultport(pvt->uri.scheme);
00850 }
00851
00852 ast_debug(3, "ne_uri.scheme = %s\n", pvt->uri.scheme);
00853 ast_debug(3, "ne_uri.host = %s\n", pvt->uri.host);
00854 ast_debug(3, "ne_uri.port = %u\n", pvt->uri.port);
00855 ast_debug(3, "ne_uri.path = %s\n", pvt->uri.path);
00856 ast_debug(3, "user = %s\n", pvt->user);
00857 ast_debug(3, "secret = %s\n", pvt->secret);
00858
00859 pvt->session = ne_session_create(pvt->uri.scheme, pvt->uri.host, pvt->uri.port);
00860 ne_redirect_register(pvt->session);
00861 ne_set_server_auth(pvt->session, auth_credentials, pvt);
00862 ne_set_useragent(pvt->session, "Asterisk");
00863
00864 if (!strcasecmp(pvt->uri.scheme, "https")) {
00865 ne_ssl_trust_default_ca(pvt->session);
00866 ne_ssl_set_verify(pvt->session, ssl_verify, pvt);
00867 }
00868
00869 cal->tech_pvt = pvt;
00870
00871 ast_mutex_init(&refreshlock);
00872
00873
00874 update_ewscal(pvt);
00875
00876 ao2_unlock(cal);
00877
00878
00879 for (;;) {
00880 struct timeval tv = ast_tvnow();
00881 struct timespec ts = {0,};
00882
00883 ts.tv_sec = tv.tv_sec + (60 * pvt->owner->refresh);
00884
00885 ast_mutex_lock(&refreshlock);
00886 while (!pvt->owner->unloading) {
00887 if (ast_cond_timedwait(&pvt->owner->unload, &refreshlock, &ts) == ETIMEDOUT) {
00888 break;
00889 }
00890 }
00891 ast_mutex_unlock(&refreshlock);
00892
00893 if (pvt->owner->unloading) {
00894 ast_debug(10, "Skipping refresh since we got a shutdown signal\n");
00895 return NULL;
00896 }
00897
00898 ast_debug(10, "Refreshing after %d minute timeout\n", pvt->owner->refresh);
00899
00900 update_ewscal(pvt);
00901 }
00902
00903 return NULL;
00904 }
00905
00906 static int load_module(void)
00907 {
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919 if (ne_version_match(0, 29) && ne_version_match(0, 30)) {
00920 ast_log(LOG_ERROR, "Exchange Web Service calendar module require neon >= 0.29.1, but %s is installed.\n", ne_version_string());
00921 return AST_MODULE_LOAD_DECLINE;
00922 }
00923
00924 if (ast_calendar_register(&ewscal_tech) && (ne_sock_init() == 0)) {
00925 return AST_MODULE_LOAD_DECLINE;
00926 }
00927
00928 return AST_MODULE_LOAD_SUCCESS;
00929 }
00930
00931 static int unload_module(void)
00932 {
00933 ne_sock_exit();
00934 ast_calendar_unregister(&ewscal_tech);
00935
00936 return 0;
00937 }
00938
00939 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Asterisk MS Exchange Web Service Calendar Integration",
00940 .load = load_module,
00941 .unload = unload_module,
00942 .load_pri = AST_MODPRI_DEVSTATE_PLUGIN,
00943 );