32 #include <ne_request.h>
33 #include <ne_session.h>
35 #include <ne_socket.h>
38 #include <ne_xmlreq.h>
40 #include <ne_redirect.h>
54 .description =
"MS Exchange Web Service calendars",
117 ast_debug(1,
"Destroying pvt for Exchange Web Service calendar %s\n",
"pvt->owner->name");
119 ne_session_destroy(pvt->
session);
146 ne_strnzcpy(username, pvt->
user, NE_ABUFSIZ);
147 ne_strnzcpy(secret, pvt->
secret, NE_ABUFSIZ);
152 static int ssl_verify(
void *userdata,
int failures,
const ne_ssl_certificate *cert)
155 if (failures & NE_SSL_UNTRUSTED) {
174 static int startelm(
void *userdata,
int parent,
const char *nspace,
const char *
name,
const char **atts)
176 struct xml_context *ctx = userdata;
178 ast_debug(5,
"EWS: XML: Start: %s\n", name);
180 return NE_XML_DECLINE;
184 if (!strcmp(name,
"Envelope") ||
186 !strcmp(name,
"FindItemResponse") ||
187 !strcmp(name,
"GetItemResponse") ||
188 !strcmp(name,
"CreateItemResponse") ||
189 !strcmp(name,
"ResponseMessages") ||
190 !strcmp(name,
"FindItemResponseMessage") || !strcmp(name,
"GetItemResponseMessage") ||
191 !strcmp(name,
"Items")
194 }
else if (!strcmp(name,
"RootFolder")) {
198 ast_debug(3,
"EWS: XML: <RootFolder>\n");
199 if (sscanf(ne_xml_get_attr(ctx->
parser, atts, NULL,
"TotalItemsInView"),
"%u", &items) != 1) {
201 ne_xml_set_error(ctx->
parser,
"Could't read number of events.");
205 ast_debug(3,
"EWS: %u calendar items to load\n", items);
210 return NE_XML_DECLINE;
213 }
else if (!strcmp(name,
"CalendarItem")) {
215 ast_debug(3,
"EWS: XML: <CalendarItem>\n");
234 }
else if (!strcmp(name,
"ItemId")) {
249 ast_debug(3,
"EWS_GET: XML: UID: %s\n", ne_xml_get_attr(ctx->
parser, atts, NULL,
"Id"));
253 }
else if (!strcmp(name,
"Subject")) {
267 }
else if (!strcmp(name,
"Start")) {
270 }
else if (!strcmp(name,
"End")) {
273 }
else if (!strcmp(name,
"LegacyFreeBusyStatus")) {
276 }
else if (!strcmp(name,
"Organizer") ||
278 !strcmp(name,
"Name")))) {
285 }
else if (!strcmp(name,
"Location")) {
292 }
else if (!strcmp(name,
"Categories")) {
302 }
else if (!strcmp(name,
"Importance")) {
309 }
else if (!strcmp(name,
"RequiredAttendees") || !strcmp(name,
"OptionalAttendees")) {
323 return NE_XML_DECLINE;
328 struct xml_context *ctx = userdata;
337 ast_log(
LOG_ERROR,
"Parsing event data, but event object does not exist!\n");
356 if (!strcmp(data,
"Busy") || !strcmp(data,
"OOF")) {
360 else if (!strcmp(data,
"Tentative")) {
361 ast_debug(3,
"EWS: XML: Busy: tentative\n");
387 struct xml_context *ctx = userdata;
389 ast_debug(5,
"EWS: XML: End: %s\n", name);
391 return NE_XML_DECLINE;
394 if (!strcmp(name,
"Subject")) {
404 }
else if (!strcmp(name,
"Organizer")) {
409 }
else if (!strcmp(name,
"Location")) {
414 }
else if (!strcmp(name,
"Categories")) {
419 }
else if (!strcmp(name,
"Importance")) {
433 if (!(attendee =
ast_calloc(1,
sizeof(*attendee)))) {
446 }
else if (!strcmp(name,
"CalendarItem")) {
448 ast_debug(3,
"EWS: XML: </CalendarItem>\n");
454 ast_log(
LOG_ERROR,
"Event data ended in XML, but event object does not exist!\n");
457 }
else if (!strcmp(name,
"Envelope")) {
461 ast_debug(3,
"EWS: XML: All events has been parsed, merging…\n");
469 static const char *
mstime(time_t t,
char *buf,
size_t buflen)
471 struct timeval tv = {
479 return S_OR(buf,
"");
500 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/FindItem\"";
502 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/GetItem\"";
504 return "\"http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem\"";
514 ne_xml_parser *parser;
517 if (!(ctx && ctx->
pvt)) {
531 ne_set_request_flag(req, NE_REQFLAG_IDEMPOTENT, 0);
534 ne_add_request_header(req,
"Content-Type",
"text/xml; charset=utf-8");
541 parser = ne_xml_create();
546 ret = ne_xml_dispatch_request(req, parser);
549 ne_request_destroy(req);
550 ne_xml_destroy(parser);
555 ne_request_destroy(req);
556 ne_xml_destroy(parser);
565 char start[21], end[21];
566 struct xml_context ctx = {
571 char *category, *categories;
582 "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
583 "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
584 "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
585 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
587 "<CreateItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\" "
588 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
589 "SendMeetingInvitations=\"SendToNone\" >"
590 "<SavedItemFolderId>"
591 "<t:DistinguishedFolderId Id=\"calendar\"/>"
592 "</SavedItemFolderId>"
594 "<t:CalendarItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
595 "<Subject>%s</Subject>"
596 "<Body BodyType=\"Text\">%s</Body>"
597 "<ReminderIsSet>false</ReminderIsSet>"
600 "<IsAllDayEvent>false</IsAllDayEvent>"
601 "<LegacyFreeBusyStatus>%s</LegacyFreeBusyStatus>"
602 "<Location>%s</Location>",
632 category =
strsep(&categories,
",");
633 while (category != NULL) {
635 category =
strsep(&categories,
",");
640 ast_str_append(&request, 0,
"</t:CalendarItem></Items></CreateItem></soap:Body></soap:Envelope>");
651 char start[21], end[21];
655 struct xml_context ctx = {
660 ast_debug(5,
"EWS: get_ewscal_ids_for()\n");
681 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" "
682 "xmlns:ns1=\"http://schemas.microsoft.com/exchange/services/2006/types\" "
683 "xmlns:ns2=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
685 "<ns2:FindItem Traversal=\"Shallow\">"
687 "<ns1:BaseShape>IdOnly</ns1:BaseShape>"
689 "<ns2:CalendarView StartDate=\"%s\" EndDate=\"%s\"/>"
690 "<ns2:ParentFolderIds>"
691 "<ns1:DistinguishedFolderId Id=\"calendar\"/>"
692 "</ns2:ParentFolderIds>"
695 "</SOAP-ENV:Envelope>",
715 struct xml_context ctx = {
725 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
726 "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" "
727 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\">"
729 "<GetItem xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">"
731 "<t:BaseShape>AllProperties</t:BaseShape>"
734 "<t:ItemId Id=\"%s\"/>"
738 "</soap:Envelope>",
id
760 for (iter = id_head; iter; iter =
AST_LIST_NEXT(iter, next)) {
777 ast_debug(5,
"EWS: ewscal_load_calendar()\n");
780 ast_log(
LOG_ERROR,
"You must enable calendar support for res_ewscal to load\n");
803 ast_log(
LOG_ERROR,
"Could not allocate space for fetching events for calendar: %s\n", cal->
name);
819 if (!strcasecmp(v->
name,
"url")) {
821 }
else if (!strcasecmp(v->
name,
"user")) {
823 }
else if (!strcasecmp(v->
name,
"secret")) {
831 ast_log(
LOG_WARNING,
"No URL was specified for Exchange Web Service calendar '%s' - skipping.\n", cal->
name);
837 if (ne_uri_parse(pvt->
url, &pvt->
uri) || pvt->
uri.host == NULL || pvt->
uri.path == NULL) {
838 ast_log(
LOG_WARNING,
"Could not parse url '%s' for Exchange Web Service calendar '%s' - skipping.\n", pvt->
url, cal->
name);
844 if (pvt->
uri.scheme == NULL) {
845 pvt->
uri.scheme =
"http";
848 if (pvt->
uri.port == 0) {
849 pvt->
uri.port = ne_uri_defaultport(pvt->
uri.scheme);
859 pvt->
session = ne_session_create(pvt->
uri.scheme, pvt->
uri.host, pvt->
uri.port);
860 ne_redirect_register(pvt->
session);
862 ne_set_useragent(pvt->
session,
"Asterisk");
864 if (!strcasecmp(pvt->
uri.scheme,
"https")) {
865 ne_ssl_trust_default_ca(pvt->
session);
881 struct timespec ts = {0,};
894 ast_debug(10,
"Skipping refresh since we got a shutdown signal\n");
919 if (ne_version_match(0, 29) && ne_version_match(0, 30)) {
920 ast_log(
LOG_ERROR,
"Exchange Web Service calendar module require neon >= 0.29.1, but %s is installed.\n", ne_version_string());
static int unload_module(void)
struct xml_context::ids ids
static int ssl_verify(void *userdata, int failures, const ne_ssl_certificate *cert)
Asterisk locking-related definitions:
int ast_calendar_register(struct ast_calendar_tech *tech)
Register a new calendar technology.
static int auth_credentials(void *userdata, const char *realm, int attempts, char *username, char *secret)
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_link(arg1, arg2)
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
char * strsep(char **str, const char *delims)
static int startelm(void *userdata, int parent, const char *nspace, const char *name, const char **atts)
struct ao2_container * events
static int send_ews_request_and_parse(struct ast_str *request, struct xml_context *ctx)
static int ewscal_write_event(struct ast_calendar_event *event)
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
#define ao2_callback(c, flags, cb_fn, arg)
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Structure for variables, used for configurations and for channel variables.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
static struct ast_calendar_tech ewscal_tech
Configuration File Parser.
static int update_ewscal(struct ewscal_pvt *pvt)
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
static time_t mstime_to_time_t(char *mstime)
enum ast_calendar_busy_state busy_state
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
struct ast_str * ast_str_create(size_t init_len)
Create a malloc'ed dynamic length string.
#define ast_mutex_lock(a)
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
static void ewscal_destructor(void *obj)
const ast_string_field description
const ast_string_field organizer
struct ast_config * ast_calendar_config_acquire(void)
Grab and lock pointer to the calendar config (read only)
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
void ast_calendar_merge_events(struct ast_calendar *cal, struct ao2_container *new_events)
Add an event to the list of events for a calendar.
#define ast_debug(level,...)
Log a DEBUG message.
const ast_string_field location
static const char * get_soap_action(enum xml_op op)
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
struct ast_calendar * owner
struct ao2_container * ast_calendar_event_container_alloc(void)
Allocate an astobj2 container for ast_calendar_event objects.
#define AST_STRING_FIELD(name)
Declare a string field.
#define ao2_ref(o, delta)
static struct calendar_id * get_ewscal_ids_for(struct ewscal_pvt *pvt)
A general API for managing calendar events with Asterisk.
const ast_string_field name
char * ast_strptime(const char *s, const char *format, struct ast_tm *tm)
Special version of strptime(3) which places the answer in the common structure ast_tm. Also, unlike strptime(3), ast_strptime() initializes its memory prior to use.
struct calendar_id * next
const ast_string_field secret
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
#define ast_strdupa(s)
duplicate a string in memory from the stack
const ast_string_field user
struct ast_calendar_event * ast_calendar_event_alloc(struct ast_calendar *cal)
Allocate an astobj2 ast_calendar_event object.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
static ast_mutex_t refreshlock
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
#define ao2_alloc(data_size, destructor_fn)
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
static void * ewscal_load_calendar(void *data)
static const char * mstime(time_t t, char *buf, size_t buflen)
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
static int load_module(void)
struct ast_calendar_event * ast_calendar_unref_event(struct ast_calendar_event *event)
Unreference an ast_calendar_event.
structure to hold users read from users.conf
const ast_string_field categories
static int cdata(void *userdata, int state, const char *cdata, size_t len)
struct ast_calendar_event::attendees attendees
static int endelm(void *userdata, int state, const char *nspace, const char *name)
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
void ast_calendar_unregister(struct ast_calendar_tech *tech)
Unregister a new calendar technology.
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
static int parse_ewscal_id(struct ewscal_pvt *pvt, const char *id)
struct timeval ast_mktime(struct ast_tm *const tmp, const char *zone)
Timezone-independent version of mktime(3).
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Individual calendaring technology data.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
struct ast_variable * next
#define ast_mutex_init(pmutex)
Asterisk calendar structure.
void ast_calendar_config_release(void)
Release the calendar config.
static const char * msstatus(enum ast_calendar_busy_state state)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
const ast_string_field summary
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
#define ast_cond_timedwait(cond, mutex, time)
Structure for mutex and tracking information.
static void * unref_ewscal(void *obj)
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
const ast_string_field url
#define ast_mutex_unlock(a)
struct ast_calendar_event * event
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.