HUGE PATCH. This moves all of mime_parser.c and all
[citadel.git] / citadel / modules / pager / serv_pager.c
1 /*
2  * \file serv_pager.c
3  * @author Mathew McBride
4  * 
5  * This module implements an external pager hook for when notifcation
6  * of a new email is wanted.
7  * Based on bits of serv_funambol
8  * Contact: <matt@mcbridematt.dhs.org> / <matt@comalies>
9  */
10
11 #include "sysdep.h"
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <stdio.h>
15 #include <fcntl.h>
16 #include <signal.h>
17 #include <pwd.h>
18 #include <errno.h>
19 #include <sys/types.h>
20
21 #if TIME_WITH_SYS_TIME
22 # include <sys/time.h>
23 # include <time.h>
24 #else
25 # if HAVE_SYS_TIME_H
26 #  include <sys/time.h>
27 # else
28 #  include <time.h>
29 # endif
30 #endif
31
32 #include <sys/wait.h>
33 #include <string.h>
34 #include <limits.h>
35 #include <sys/socket.h>
36 #include <libcitadel.h>
37 #include "citadel.h"
38 #include "server.h"
39 #include "citserver.h"
40 #include "support.h"
41 #include "config.h"
42 #include "control.h"
43 #include "room_ops.h"
44 #include "user_ops.h"
45 #include "policy.h"
46 #include "database.h"
47 #include "msgbase.h"
48 #include "internet_addressing.h"
49 #include "domain.h"
50 #include "clientsocket.h"
51 #include "serv_pager.h"
52
53 #include "ctdl_module.h"
54
55 #define PAGER_CONFIG_MESSAGE "__ Push email settings __"
56 #define PAGER_CONFIG_TEXT  "textmessage"
57
58 /*! \brief Create the notify message queue. We use the exact same room 
59  *                      as the Funambol module. 
60  *
61  *      Run at server startup, creates FNBL_QUEUE_ROOM if it doesn't exist
62  *      and sets as system room. 
63  */
64 void create_pager_queue(void) {
65         struct ctdlroom qrbuf;
66
67         create_room(FNBL_QUEUE_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
68
69         /*
70          * Make sure it's set to be a "system room" so it doesn't show up
71          * in the <K>nown rooms list for Aides.
72          */
73         if (lgetroom(&qrbuf, FNBL_QUEUE_ROOM) == 0) {
74                 qrbuf.QRflags2 |= QR2_SYSTEM;
75                 lputroom(&qrbuf);
76         }
77 }
78 /*! 
79  * \brief Run through the pager room queue
80  */
81 void do_pager_queue(void) {
82         static int doing_queue = 0;
83
84         /*
85          * This is a simple concurrency check to make sure only one queue run
86          * is done at a time.  We could do this with a mutex, but since we
87          * don't really require extremely fine granularity here, we'll do it
88          * with a static variable instead.
89          */
90         if (doing_queue) return;
91         doing_queue = 1;
92
93         /* 
94          * Go ahead and run the queue
95          */
96         lprintf(CTDL_DEBUG, "serv_pager: processing notify queue\n");
97
98         if (getroom(&CC->room, FNBL_QUEUE_ROOM) != 0) {
99                 lprintf(CTDL_ERR, "Cannot find room <%s>\n", FNBL_QUEUE_ROOM);
100                 return;
101         }
102         CtdlForEachMessage(MSGS_ALL, 0L, NULL,
103                 SPOOLMIME, NULL, notify_pager, NULL);
104
105         lprintf(CTDL_DEBUG, "serv_pager: queue run completed\n");
106         doing_queue = 0;
107 }
108
109 /*!
110  * \brief Call the external pager tool as set by the administrator
111  * @param msgnum The message number of the 'hint' message passed from do_pager_queue
112  * @param userdata userdata struct as passed by CtdlForEachMessage
113  *
114  */
115 void notify_pager(long msgnum, void *userdata) {
116         struct CtdlMessage *msg;
117         
118         /* W means 'wireless', which contains the unix name */
119         msg = CtdlFetchMessage(msgnum, 1);
120         if ( msg->cm_fields['W'] == NULL) {
121                 goto nuke;
122         }
123         /* Are we allowed to push? */
124         if (IsEmptyStr(config.c_pager_program)) {
125                 return;
126         } else if (IsEmptyStr(config.c_pager_program) && IsEmptyStr(config.c_funambol_host)) {
127                 goto nuke;
128         } else {
129                 lprintf(CTDL_INFO, "Pager alerter enabled\n");  
130         }
131
132         /* Get the configuration. We might be allowed system wide but the user
133                 may have configured otherwise */
134         long configMsgNum = pager_getConfigMessage(msg->cm_fields['W']);
135         int allowed = pager_isPagerAllowedByPrefs(configMsgNum);
136         if (allowed != 0 && pager_doesUserWant(configMsgNum) == 0) {
137                 goto nuke;
138         } else if (allowed != 0) {
139                 return;
140         }
141         char *num = pager_getUserPhoneNumber(configMsgNum);
142         char command[SIZ];
143         snprintf(command, sizeof command, "%s %s -u %s", config.c_pager_program, num, msg->cm_fields['W']);
144         system(command);
145         
146         nuke:
147         CtdlFreeMessage(msg);
148         long todelete[1];
149         todelete[0] = msgnum;
150         CtdlDeleteMessages(FNBL_QUEUE_ROOM, todelete, 1, "");
151 }
152 /*! \brief Get configuration message for pager/funambol system from the 
153  *                      users "My Citadel Config" room
154  */
155 long pager_getConfigMessage(char *username) {
156         struct ctdlroom qrbuf; // scratch for room
157         struct ctdluser user; // ctdl user instance
158         char configRoomName[ROOMNAMELEN];
159         struct CtdlMessage *msg;
160         struct cdbdata *cdbfr;
161         long *msglist = NULL;
162         int num_msgs = 0;
163         long confMsgNum = -1;
164         // Get the user
165         getuser(&user, username);
166         
167         MailboxName(configRoomName, sizeof configRoomName, &user, USERCONFIGROOM);
168         // Fill qrbuf
169         getroom(&qrbuf, configRoomName);
170         /* Do something really, really stoopid here. Raid the room on ourselves,
171                 loop through the messages manually and find it. I don't want 
172                 to use a CtdlForEachMessage callback here, as we would be 
173                 already in one */
174         cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long));
175         if (cdbfr != NULL) {
176                 msglist = (long *) cdbfr->ptr;
177                 cdbfr->ptr = NULL;      /* CtdlForEachMessage() now owns this memory */
178                 num_msgs = cdbfr->len / sizeof(long);
179                 cdb_free(cdbfr);
180         } else {
181                 lprintf(CTDL_DEBUG, "pager_getConfigMessage: No config messages found\n");
182                 return -1;      /* No messages at all?  No further action. */
183         }
184         int a;
185         for (a = 0; a < num_msgs; ++a) {
186                                 msg = CtdlFetchMessage(msglist[a], 1);
187                                 if (msg != NULL) {
188                                         if (msg->cm_fields['U'] != NULL && strncasecmp(msg->cm_fields['U'], PAGER_CONFIG_MESSAGE, 
189                                                 strlen(PAGER_CONFIG_MESSAGE)) == 0) {
190                                                 confMsgNum = msglist[a];
191                                         }
192                                         CtdlFreeMessage(msg);
193                                 }
194         }
195         return confMsgNum;
196
197 }
198 int pager_isPagerAllowedByPrefs(long configMsgNum) {
199         // Do a simple string search to see if 'textmessage' is selected as the 
200         // type. This string would be at the very top of the message contents.
201         if (configMsgNum == -1) {
202                 return -1;
203         }
204         struct CtdlMessage *prefMsg;
205         prefMsg = CtdlFetchMessage(configMsgNum, 1);
206         char *msgContents = prefMsg->cm_fields['M'];
207         return strncasecmp(msgContents, PAGER_CONFIG_TEXT, strlen(PAGER_CONFIG_TEXT));
208 }
209 int pager_doesUserWant(long configMsgNum) {
210         if (configMsgNum == -1) {
211                 return -1;
212         }
213         struct CtdlMessage *prefMsg;
214         prefMsg = CtdlFetchMessage(configMsgNum, 1);
215         char *msgContents = prefMsg->cm_fields['M'];
216         return strncasecmp(msgContents, "none", 4);
217 }
218         /* warning: fetching twice gravely inefficient, will fix some time */
219 char *pager_getUserPhoneNumber(long configMsgNum) {
220         if (configMsgNum == -1) {
221                 return NULL;
222         }
223         struct CtdlMessage *prefMsg;
224         prefMsg = CtdlFetchMessage(configMsgNum, 1);
225         char *msgContents = prefMsg->cm_fields['M'];
226         char *lines = strtok(msgContents, "textmessage\n");
227         return lines;
228 }
229 CTDL_MODULE_INIT(pager)
230 {
231         create_pager_queue();
232         CtdlRegisterSessionHook(do_pager_queue, EVT_TIMER);
233
234         /* return our Subversion id for the Log */
235         return "$Id: serv_pager.c $";
236 }