]> Pileus Git - lackey/blob - cals/ews.c
62776d2471058095503f8d239d71dc8039f87d40
[lackey] / cals / ews.c
1 /*
2  * Copyright (C) 2016 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdio.h>
19
20 #include <curl/curl.h>
21 #include <expat.h>
22
23 #include "util.h"
24 #include "conf.h"
25 #include "date.h"
26 #include "cal.h"
27
28 /* Local Types */
29 typedef struct ews_t {
30         cal_t         cal;
31         struct ews_t *next;
32
33         // Config
34         char         *username;
35         char         *password;
36         char         *location;
37         char         *domain;
38
39         // Parsing
40         CURL         *curl;
41         XML_Parser    expat;
42         buf_t         buf;
43
44         // Debugging
45         int           debug;
46         int           indent;
47 } ews_t;
48
49 /* Static data */
50 static ews_t *calendars;
51 static CURLM *curlm;
52
53 /* SOAP Requests */
54 static char *req_calendar =
55         "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
56         "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
57         "               xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n"
58         "               xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
59         "               xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\"\n"
60         "               xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\">\n"
61         "    <soap:Body>\n"
62         "        <m:FindItem Traversal=\"Shallow\">\n"
63         "            <m:ItemShape>\n"
64         "                <t:BaseShape>IdOnly</t:BaseShape>\n"
65         "                <t:AdditionalProperties>\n"
66         "                    <t:FieldURI FieldURI=\"calendar:CalendarItemType\" />\n"
67         "                    <t:FieldURI FieldURI=\"calendar:Start\" />\n"
68         "                    <t:FieldURI FieldURI=\"calendar:End\" />\n"
69         "                    <t:FieldURI FieldURI=\"item:ItemClass\" />\n"
70         "                    <t:FieldURI FieldURI=\"item:Subject\" />\n"
71         "                </t:AdditionalProperties>\n"
72         "            </m:ItemShape>\n"
73         "            <m:CalendarView StartDate=\"2016-01-01T00:00:00-00:00\"\n"
74         "                            EndDate=\"2018-01-01T00:00:00-00:00\"\n"
75         "                            MaxEntriesReturned=\"1000\" />\n"
76         "            <m:ParentFolderIds>\n"
77         "                <t:DistinguishedFolderId Id=\"calendar\" />\n"
78         "            </m:ParentFolderIds>\n"
79         "        </m:FindItem>\n"
80         "    </soap:Body>\n"
81         "</soap:Envelope>\n"
82 ;
83
84 /* Local functions */
85 static void on_start(void *_cal, const char *tag, const char **attrs)
86 {
87         ews_t *cal = _cal;
88
89         /* Debug print */
90         if (cal->debug) {
91                 printf("%*s%s\n",
92                         cal->indent*4, "", tag);
93                 for (int i = 0; attrs[i]; i += 2) {
94                         printf("%*s %s=\"%s\"\n",
95                                 cal->indent*4+6, "--",
96                                 attrs[i+0], attrs[i+1]);
97                         attrs+=2;
98                 }
99                 cal->indent++;
100         }
101 }
102
103 static void on_end(void *_cal, const char *tag)
104 {
105         ews_t *cal = _cal;
106
107         /* Debug print */
108         if (cal->debug) {
109                 if (cal->buf.len)
110                         printf("%*s \"%s\"\n",
111                                 cal->indent*4+2, "--",
112                                 (char*)cal->buf.data);
113                 cal->indent--;
114         }
115
116         /* Parse items */
117         cal->buf.len = 0;
118 }
119
120 static void on_data(void *_cal, const char *data, int len)
121 {
122         ews_t *cal = _cal;
123         if (cal->debug)
124                 append(&cal->buf, data, len);
125 }
126
127 static size_t on_write(void *buf, size_t size, size_t n, ews_t *cal)
128 {
129         int len = size * n;
130         XML_Parse(cal->expat, buf, len, 0);
131         return len;
132 }
133
134 static void sync_ews(ews_t *cal)
135 {
136         CURLcode err;
137         long status;
138         struct curl_slist *hdr = NULL;
139
140         /* Debug output */
141         debug("Loading EWS:");
142         debug("  type     = %s", cal->cal.type);
143         debug("  name     = %s", cal->cal.name);
144         debug("  desc     = %s", cal->cal.desc);
145         debug("  username = %s", cal->username);
146         debug("  password = %s", cal->password);
147         debug("  location = %s", cal->location);
148         debug("  domain   = %s", cal->domain);
149
150         /* Setup Expat */
151         if (!(cal->expat = XML_ParserCreate(NULL)))
152                 error("XML Parser Create");
153         XML_SetUserData(cal->expat, cal);
154         XML_SetStartElementHandler(cal->expat, on_start);
155         XML_SetEndElementHandler(cal->expat, on_end);
156         XML_SetCharacterDataHandler(cal->expat, on_data);
157
158         /* Setup HTTP request */
159         if (!(cal->curl = curl_easy_init()))
160                 error("Curl easy init failed");
161         if (curl_easy_setopt(cal->curl, CURLOPT_URL, cal->location))
162                 error("Curl easy setopt failed");
163         if (curl_easy_setopt(cal->curl, CURLOPT_WRITEFUNCTION, on_write))
164                 error("Curl easy set write function failed");
165         if (curl_easy_setopt(cal->curl, CURLOPT_WRITEDATA, cal))
166                 error("Curl easy set write data failed");
167
168         /* Skip SSL checks */
169         if (curl_easy_setopt(cal->curl, CURLOPT_SSL_VERIFYPEER, 0L))
170                 error("Curl easy set no verify peer failed");
171         if (curl_easy_setopt(cal->curl, CURLOPT_SSL_VERIFYHOST, 0L))
172                 error("Curl easy set no verify hosts failed");
173
174         /* Set login info */
175         if (curl_easy_setopt(cal->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY))
176                 error("Curl easy set http auth failed");
177         if (curl_easy_setopt(cal->curl, CURLOPT_USERNAME, cal->username))
178                 error("Curl easy set username failed");
179         if (curl_easy_setopt(cal->curl, CURLOPT_PASSWORD, cal->password))
180                 error("Curl easy set password failed");
181
182         /* Setup SOAP request */
183         hdr = curl_slist_append(hdr, "Accept: text/xml");
184         hdr = curl_slist_append(hdr, "Content-Type: text/xml");
185         if (curl_easy_setopt(cal->curl, CURLOPT_HTTPHEADER, hdr))
186                 error("Curl easy set http header failed");
187         if (curl_easy_setopt(cal->curl, CURLOPT_POST, 1L))
188                 error("Curl easy set post failed");
189         if (curl_easy_setopt(cal->curl, CURLOPT_POSTFIELDS, req_calendar))
190                 error("Curl easy set post failed");
191
192         /* Curl Easy */
193         if ((err = curl_easy_perform(cal->curl)))
194                 error("Curl easy perform failed: %s", curl_easy_strerror(err));
195         if ((err = curl_easy_getinfo(cal->curl, CURLINFO_RESPONSE_CODE, &status)))
196                 error("Curl easy get info failed: %s", curl_easy_strerror(err));
197
198         /* Output response */
199         if (cal->debug)
200                 printf("EWS -- HTTP Status %ld\n", status);
201         if (cal->debug && !cal->buf.data)
202                 printf("EWS -- No Response Data\n");
203
204         /* Cleanup */
205         curl_easy_cleanup(cal->curl);
206         XML_ParserFree(cal->expat);
207 }
208
209 /* Config parser */
210 void ews_config(const char *group, const char *name, const char *key, const char *value)
211 {
212         ews_t *cal = NULL, *last = NULL;
213
214         /* Make sure it's valid */
215         if (!match(group, "ews") || !name)
216                 return;
217
218         /* Find existing calendar */
219         for (cal = calendars; cal; last = cal, cal = cal->next)
220                 if (match(cal->cal.name, name))
221                         break;
222
223         /* Create new calendar */
224         if (!cal) {
225                 cal = new0(ews_t);
226                 cal->cal.type = "ews";
227                 cal->cal.name = get_name(name);
228                 if (last)
229                         last->next = cal;
230                 else
231                         calendars = cal;
232         }
233
234         /* Set calendar values */
235         if (match(key, "location"))
236                 cal->location = get_string(value);
237         else if (match(key, "username"))
238                 cal->username = get_string(value);
239         else if (match(key, "password"))
240                 cal->password = get_string(value);
241         else if (match(key, "domain"))
242                 cal->domain = get_string(value);
243 }
244
245 /* Cal functions */
246 cal_t *ews_cals(void)
247 {
248         for (ews_t *cal = calendars; cal; cal = cal->next) {
249                 sync_ews(cal);
250                 cal->cal.next = &cal->next->cal;
251         }
252
253         return &calendars->cal;
254 }
255
256 /* Event functions */
257 event_t *ews_events(date_t start, date_t end)
258 {
259         return NULL;
260 }
261
262 /* Todo functions */
263 todo_t *ews_todos(date_t start, date_t end)
264 {
265         return NULL;
266 }
267
268 /* Test functions */
269 void ews_test(char *url, char *user, char *pass)
270 {
271         ews_t cal = {
272                 .location = url,
273                 .username = user && user[0] ? user : NULL,
274                 .password = pass && pass[0] ? pass : NULL,
275                 .debug    = 1,
276         };
277
278         /* Setup CURL */
279         printf("EWS -- test start\n");
280         if (curl_global_init(CURL_GLOBAL_DEFAULT))
281                 error("Curl global init failed");
282         if (!(curlm = curl_multi_init()))
283                 error("Curl multi init failed");
284
285         /* Sync the calendar */
286         sync_ews(&cal);
287
288         /* Cleanup */
289         curl_multi_cleanup(curlm);
290         curl_global_cleanup();
291         printf("EWS -- test end\n");
292 }