]> Pileus Git - lackey/blob - cals/ews.c
Use CURL simple API for EWS calendars
[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
22 #include "util.h"
23 #include "conf.h"
24 #include "date.h"
25 #include "cal.h"
26
27 /* Local Types */
28 typedef struct ews_t {
29         cal_t         cal;
30         struct ews_t *next;
31
32         // Config
33         char         *username;
34         char         *password;
35         char         *location;
36         char         *domain;
37
38         // Parsing
39         CURL         *curl;
40         buf_t         buf;
41
42         // Debugging
43         int           debug;
44         int           indent;
45 } ews_t;
46
47 /* Static data */
48 static ews_t *calendars;
49 static CURLM *curlm;
50
51 /* SOAP Requests */
52 static char *req_calendar =
53         "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
54         "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
55         "               xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n"
56         "               xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"\n"
57         "               xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\"\n"
58         "               xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\">\n"
59         "    <soap:Body>\n"
60         "        <m:FindItem Traversal=\"Shallow\">\n"
61         "            <m:ItemShape>\n"
62         "                <t:BaseShape>IdOnly</t:BaseShape>\n"
63         "                <t:AdditionalProperties>\n"
64         "                    <t:FieldURI FieldURI=\"calendar:CalendarItemType\" />\n"
65         "                    <t:FieldURI FieldURI=\"calendar:Start\" />\n"
66         "                    <t:FieldURI FieldURI=\"calendar:End\" />\n"
67         "                    <t:FieldURI FieldURI=\"item:ItemClass\" />\n"
68         "                    <t:FieldURI FieldURI=\"item:Subject\" />\n"
69         "                </t:AdditionalProperties>\n"
70         "            </m:ItemShape>\n"
71         "            <m:CalendarView StartDate=\"2016-01-01T00:00:00-00:00\"\n"
72         "                            EndDate=\"2018-01-01T00:00:00-00:00\"\n"
73         "                            MaxEntriesReturned=\"1000\" />\n"
74         "            <m:ParentFolderIds>\n"
75         "                <t:DistinguishedFolderId Id=\"calendar\" />\n"
76         "            </m:ParentFolderIds>\n"
77         "        </m:FindItem>\n"
78         "    </soap:Body>\n"
79         "</soap:Envelope>\n"
80 ;
81
82 /* Local functions */
83 static void dump_xml(const char *buf, int len)
84 {
85         int indent = 0;
86         for (int i = 0; i < len; i++) {
87                 const char prev = i>0 ? buf[i-1] : '\0';
88                 const char this = buf[i];
89                 const char next = i<len ? buf[i+1] : '\0';
90                 if (this == '<' && next == '/')
91                         indent--;
92                 if (this == '<' && prev == '>') {
93                         putchar('\n');
94                         for (int j = 0; j < indent*4; j++)
95                                 putchar(' ');
96                 }
97                 if (this == '>' && prev == '/')
98                         indent--;
99                 if (this == '<' && next != '/' && next != '?')
100                         indent++;
101                 putchar(buf[i]);
102         }
103         putchar('\n');
104 }
105
106 static size_t on_write(void *buf, size_t size, size_t n, ews_t *cal)
107 {
108         int len = size * n;
109         append(&cal->buf, buf, len);
110         return len;
111 }
112
113 static void sync_ews(ews_t *cal)
114 {
115         CURLcode err;
116         long status;
117         struct curl_slist *hdr = NULL;
118
119         /* Debug output */
120         debug("Loading EWS:");
121         debug("  type     = %s", cal->cal.type);
122         debug("  name     = %s", cal->cal.name);
123         debug("  desc     = %s", cal->cal.desc);
124         debug("  username = %s", cal->username);
125         debug("  password = %s", cal->password);
126         debug("  location = %s", cal->location);
127         debug("  domain   = %s", cal->domain);
128
129         /* Setup HTTP request */
130         if (!(cal->curl = curl_easy_init()))
131                 error("Curl easy init failed");
132         if (curl_easy_setopt(cal->curl, CURLOPT_URL, cal->location))
133                 error("Curl easy setopt failed");
134         if (curl_easy_setopt(cal->curl, CURLOPT_WRITEFUNCTION, on_write))
135                 error("Curl easy set write function failed");
136         if (curl_easy_setopt(cal->curl, CURLOPT_WRITEDATA, cal))
137                 error("Curl easy set write data failed");
138
139         /* Skip SSL checks */
140         if (curl_easy_setopt(cal->curl, CURLOPT_SSL_VERIFYPEER, 0L))
141                 error("Curl easy set no verify peer failed");
142         if (curl_easy_setopt(cal->curl, CURLOPT_SSL_VERIFYHOST, 0L))
143                 error("Curl easy set no verify hosts failed");
144
145         /* Set login info */
146         if (curl_easy_setopt(cal->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY))
147                 error("Curl easy set http auth failed");
148         if (curl_easy_setopt(cal->curl, CURLOPT_USERNAME, cal->username))
149                 error("Curl easy set username failed");
150         if (curl_easy_setopt(cal->curl, CURLOPT_PASSWORD, cal->password))
151                 error("Curl easy set password failed");
152
153         /* Setup SOAP request */
154         hdr = curl_slist_append(hdr, "Accept: text/xml");
155         hdr = curl_slist_append(hdr, "Content-Type: text/xml");
156         if (curl_easy_setopt(cal->curl, CURLOPT_HTTPHEADER, hdr))
157                 error("Curl easy set http header failed");
158         if (curl_easy_setopt(cal->curl, CURLOPT_POST, 1L))
159                 error("Curl easy set post failed");
160         if (curl_easy_setopt(cal->curl, CURLOPT_POSTFIELDS, req_calendar))
161                 error("Curl easy set post failed");
162
163         /* Curl Easy */
164         if ((err = curl_easy_perform(cal->curl)))
165                 error("Curl easy perform failed: %s", curl_easy_strerror(err));
166         if ((err = curl_easy_getinfo(cal->curl, CURLINFO_RESPONSE_CODE, &status)))
167                 error("Curl easy get info failed: %s", curl_easy_strerror(err));
168
169         /* Output response */
170         if (cal->debug)
171                 printf("EWS -- HTTP Status %ld\n", status);
172         if (cal->debug && !cal->buf.data)
173                 printf("EWS -- No Response Data\n");
174         if (cal->debug)
175                 dump_xml(cal->buf.data, cal->buf.len);
176
177         /* Cleanup */
178         curl_easy_cleanup(cal->curl);
179         release(&cal->buf);
180 }
181
182 /* Config parser */
183 void ews_config(const char *group, const char *name, const char *key, const char *value)
184 {
185         ews_t *cal = NULL, *last = NULL;
186
187         /* Make sure it's valid */
188         if (!match(group, "ews") || !name)
189                 return;
190
191         /* Find existing calendar */
192         for (cal = calendars; cal; last = cal, cal = cal->next)
193                 if (match(cal->cal.name, name))
194                         break;
195
196         /* Create new calendar */
197         if (!cal) {
198                 cal = new0(ews_t);
199                 cal->cal.type = "ews";
200                 cal->cal.name = get_name(name);
201                 if (last)
202                         last->next = cal;
203                 else
204                         calendars = cal;
205         }
206
207         /* Set calendar values */
208         if (match(key, "location"))
209                 cal->location = get_string(value);
210         else if (match(key, "username"))
211                 cal->username = get_string(value);
212         else if (match(key, "password"))
213                 cal->password = get_string(value);
214         else if (match(key, "domain"))
215                 cal->domain = get_string(value);
216 }
217
218 /* Cal functions */
219 cal_t *ews_cals(void)
220 {
221         for (ews_t *cal = calendars; cal; cal = cal->next) {
222                 sync_ews(cal);
223                 cal->cal.next = &cal->next->cal;
224         }
225
226         return &calendars->cal;
227 }
228
229 /* Event functions */
230 event_t *ews_events(date_t start, date_t end)
231 {
232         return NULL;
233 }
234
235 /* Todo functions */
236 todo_t *ews_todos(date_t start, date_t end)
237 {
238         return NULL;
239 }
240
241 /* Test functions */
242 void ews_test(char *url, char *user, char *pass)
243 {
244         ews_t cal = {
245                 .location = url,
246                 .username = user && user[0] ? user : NULL,
247                 .password = pass && pass[0] ? pass : NULL,
248                 .debug    = 1,
249         };
250
251         /* Setup CURL */
252         printf("EWS -- test start\n");
253         if (curl_global_init(CURL_GLOBAL_DEFAULT))
254                 error("Curl global init failed");
255         if (!(curlm = curl_multi_init()))
256                 error("Curl multi init failed");
257
258         /* Sync the calendar */
259         sync_ews(&cal);
260
261         /* Cleanup */
262         curl_multi_cleanup(curlm);
263         curl_global_cleanup();
264         printf("EWS -- test end\n");
265 }