1a109971988185f4b4ebeb44a1a3604fbdc5042c
[citadel.git] / citadel / modules / extnotify / funambol65.c
1 /*
2  * funambol65.c
3  * Author: Mathew McBride
4  *
5  * This module facilitates notifications to a Funambol server
6  * for push email
7  *
8  * Based on bits of the previous serv_funambol
9  * Contact: <matt@mcbridematt.dhs.org> / <matt@comalies>
10  *
11  * Copyright (c) 2008-2015
12  *
13  * This program is open source software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License version 3.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <sys/socket.h>
26 #include <time.h>
27 #include <libcitadel.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <curl/curl.h>
31
32 #include "citadel.h"
33 #include "citserver.h"
34 #include "citadel_dirs.h"
35 #include "clientsocket.h"
36 #include "sysdep.h"
37 #include "config.h"
38 #include "sysdep_decls.h"
39 #include "msgbase.h"
40 #include "ctdl_module.h"
41
42 #include "event_client.h"
43 #include "extnotify.h"
44
45 eNextState EvaluateResult(AsyncIO *IO);
46 eNextState ExtNotifyTerminate(AsyncIO *IO);
47 eNextState ExtNotifyTerminateDB(AsyncIO *IO);
48 eNextState ExtNotifyShutdownAbort(AsyncIO *IO);
49
50 /*
51 * \brief Sends a message to the Funambol server notifying
52 * of new mail for a user
53 * Returns 0 if unsuccessful
54 */
55 int notify_http_server(char *remoteurl,
56                        const char* template, long tlen,
57                        char *user,
58                        char *msgid,
59                        long MsgNum,
60                        NotifyContext *Ctx)
61 {
62         CURLcode sta;
63         char msgnumstr[128];
64         char *buf = NULL;
65         char *SOAPMessage = NULL;
66         char *contenttype = NULL;
67         StrBuf *ReplyBuf;
68         StrBuf *Buf;
69         CURL *chnd;
70         AsyncIO *IO;
71
72         IO = (AsyncIO*) malloc(sizeof(AsyncIO));
73         memset(IO, 0, sizeof(AsyncIO));
74
75         if (! InitcURLIOStruct(IO,
76                                NULL, /* we don't have personal data anymore. */
77                                "Citadel ExtNotify",
78                                EvaluateResult,
79                                ExtNotifyTerminate,
80                                ExtNotifyTerminateDB,
81                                ExtNotifyShutdownAbort))
82         {
83                 syslog(LOG_ALERT, "Unable to initialize libcurl.\n");
84                 goto abort;
85         }
86
87         snprintf(msgnumstr, 128, "%ld", MsgNum);
88
89         if (tlen > 0) {
90                 /* Load the template message. Get mallocs done too */
91                 int fd;
92                 struct stat statbuf;
93                 const char *mimetype;
94                 const char *Err = NULL;
95
96                 fd = open(template, O_RDONLY);
97                 if ((fd < 0) ||
98                     (fstat(fd, &statbuf) == -1))
99                 {
100                         char buf[SIZ];
101
102                         snprintf(buf, SIZ,
103                                  "Cannot load template file %s [%s] "
104                                  "won't send notification\r\n",
105                                  file_funambol_msg,
106                                  strerror(errno));
107                         syslog(LOG_ERR, "%s", buf);
108                         // TODO: once an hour!
109                         CtdlAideMessage(
110                                 buf,
111                                 "External notifier: "
112                                 "unable to find/stat message template!");
113                         goto abort;
114                 }
115
116                 Buf = NewStrBufPlain(NULL, statbuf.st_size + 1);
117                 if (StrBufReadBLOB(Buf, &fd, 1, statbuf.st_size, &Err) < 0) {
118                         char buf[SIZ];
119
120                         close(fd);
121
122                         snprintf(buf, SIZ,
123                                  "Cannot load template file %s [%s] "
124                                  "won't send notification\r\n",
125                                  file_funambol_msg,
126                                  Err);
127                         syslog(LOG_ERR, "%s", buf);
128                         // TODO: once an hour!
129                         CtdlAideMessage(
130                                 buf,
131                                 "External notifier: "
132                                 "unable to load message template!");
133                         goto abort;
134                 }
135                 close(fd);
136
137                 mimetype = GuessMimeByFilename(template, tlen);
138
139                 SOAPMessage = SmashStrBuf(&Buf);
140
141                 // Do substitutions
142                 help_subst(SOAPMessage, "^notifyuser", user);
143                 help_subst(SOAPMessage, "^syncsource", CtdlGetConfigStr("c_funambol_source"));
144                 help_subst(SOAPMessage, "^msgid", msgid);
145                 help_subst(SOAPMessage, "^msgnum", msgnumstr);
146
147                 /* pass our list of custom made headers */
148
149                 contenttype=(char*) malloc(40+strlen(mimetype));
150                 sprintf(contenttype,
151                         "Content-Type: %s; charset=utf-8",
152                         mimetype);
153
154                 IO->HttpReq.headers = curl_slist_append(
155                         IO->HttpReq.headers,
156                         "SOAPAction: \"\"");
157
158                 IO->HttpReq.headers = curl_slist_append(
159                         IO->HttpReq.headers,
160                         contenttype);
161                 free(contenttype);
162                 contenttype = NULL;
163                 IO->HttpReq.headers = curl_slist_append(
164                         IO->HttpReq.headers,
165                         "Accept: application/soap+xml, "
166                         "application/mime, multipart/related, text/*");
167
168                 IO->HttpReq.headers = curl_slist_append(
169                         IO->HttpReq.headers,
170                         "Pragma: no-cache");
171
172                 /* Now specify the POST binary data */
173                 IO->HttpReq.PlainPostData = SOAPMessage;
174                 IO->HttpReq.PlainPostDataLen = strlen(SOAPMessage);
175         }
176         else {
177                 help_subst(remoteurl, "^notifyuser", user);
178                 help_subst(remoteurl, "^syncsource", CtdlGetConfigStr("c_funambol_source"));
179                 help_subst(remoteurl, "^msgid", msgid);
180                 help_subst(remoteurl, "^msgnum", msgnumstr);
181
182                 IO->HttpReq.headers = curl_slist_append(
183                         IO->HttpReq.headers,
184                         "Accept: application/soap+xml, "
185                         "application/mime, multipart/related, text/*");
186
187                 IO->HttpReq.headers = curl_slist_append(
188                         IO->HttpReq.headers,
189                         "Pragma: no-cache");
190         }
191
192         Buf = NewStrBufPlain (remoteurl, -1);
193         ParseURL(&IO->ConnectMe, Buf, 80);
194         FreeStrBuf(&Buf); /* TODO: this is uncool... */
195         CurlPrepareURL(IO->ConnectMe);
196
197         chnd = IO->HttpReq.chnd;
198         OPT(SSL_VERIFYPEER, 0);
199         OPT(SSL_VERIFYHOST, 0);
200
201         QueueCurlContext(IO);
202
203         return 0;
204 abort:
205
206         if (contenttype) free(contenttype);
207         if (SOAPMessage != NULL) free(SOAPMessage);
208         if (buf != NULL) free(buf);
209         FreeStrBuf (&ReplyBuf);
210         return 1;
211 }
212
213
214 eNextState EvaluateResult(AsyncIO *IO)
215 {
216
217         if (IO->HttpReq.httpcode != 200) {
218                 StrBuf *ErrMsg;
219
220                 syslog(LOG_ALERT, "libcurl error %ld: %s\n",
221                               IO->HttpReq.httpcode,
222                               IO->HttpReq.errdesc);
223
224                 ErrMsg = NewStrBufPlain(
225                         HKEY("Error sending your Notification\n"));
226                 StrBufAppendPrintf(ErrMsg, "\nlibcurl error %ld: \n\t\t%s\n",
227                                    IO->HttpReq.httpcode,
228                                    IO->HttpReq.errdesc);
229
230                 StrBufAppendBufPlain(ErrMsg,
231                                      HKEY("\nWas Trying to send: \n"),
232                                      0);
233
234                 StrBufAppendBufPlain(ErrMsg, IO->ConnectMe->PlainUrl, -1, 0);
235                 if (IO->HttpReq.PlainPostDataLen > 0) {
236                         StrBufAppendBufPlain(
237                                 ErrMsg,
238                                 HKEY("\nThe Post document was: \n"),
239                                 0);
240                         StrBufAppendBufPlain(ErrMsg,
241                                              IO->HttpReq.PlainPostData,
242                                              IO->HttpReq.PlainPostDataLen, 0);
243                         StrBufAppendBufPlain(ErrMsg, HKEY("\n\n"), 0);
244                 }
245                 if (StrLength(IO->HttpReq.ReplyData) > 0) {
246                         StrBufAppendBufPlain(
247                                 ErrMsg,
248                                 HKEY("\n\nThe Serverreply was: \n\n"),
249                                 0);
250                         StrBufAppendBuf(ErrMsg, IO->HttpReq.ReplyData, 0);
251                 }
252                 else
253                         StrBufAppendBufPlain(
254                                 ErrMsg,
255                                 HKEY("\n\nThere was no Serverreply.\n\n"),
256                                 0);
257                 ///ExtNotify_PutErrorMessage(Ctx, ErrMsg);
258                 CtdlAideMessage(ChrPtr(ErrMsg),
259                                 "External notifier: "
260                                 "unable to contact notification host!");
261                 FreeStrBuf(&ErrMsg);
262         }
263
264         syslog(LOG_DEBUG, "Funambol notified\n");
265 /*
266         while ((Ctx.NotifyHostList != NULL) && (Ctx.NotifyHostList[i] != NULL))
267                 FreeStrBuf(&Ctx.NotifyHostList[i]);
268
269         if (Ctx.NotifyErrors != NULL)
270         {
271                 long len;
272                 const char *Key;
273                 HashPos *It;
274                 void *vErr;
275                 StrBuf *ErrMsg;
276
277                 It = GetNewHashPos(Ctx.NotifyErrors, 0);
278                 while (GetNextHashPos(Ctx.NotifyErrors,
279                 It, &len, &Key, &vErr) &&
280                        (vErr != NULL)) {
281                         ErrMsg = (StrBuf*) vErr;
282                         quickie_message("Citadel", NULL, NULL,
283                         AIDEROOM, ChrPtr(ErrMsg), FMT_FIXED,
284                         "Failed to notify external service about inbound mail");
285                 }
286
287                 DeleteHashPos(&It);
288                 DeleteHash(&Ctx.NotifyErrors);
289         }
290 */
291
292 ////    curl_slist_free_all (headers);
293 ///     curl_easy_cleanup(curl);
294         ///if (contenttype) free(contenttype);
295         ///if (SOAPMessage != NULL) free(SOAPMessage);
296         ///if (buf != NULL) free(buf);
297         ///FreeStrBuf (&ReplyBuf);
298         return eTerminateConnection;
299 }
300
301 eNextState ExtNotifyTerminateDB(AsyncIO *IO)
302 {
303         free(IO);
304         return eAbort;
305 }
306 eNextState ExtNotifyTerminate(AsyncIO *IO)
307 {
308         free(IO);
309         return eAbort;
310 }
311 eNextState ExtNotifyShutdownAbort(AsyncIO *IO)
312 {
313         free(IO);
314         return eAbort;
315 }