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