1 /* notify.c -- Module to notify of new mail
2  *
3  * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name "Carnegie Mellon University" must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission. For permission or any legal
20  *    details, please contact
21  *      Carnegie Mellon University
22  *      Center for Technology Transfer and Enterprise Creation
23  *      4615 Forbes Avenue
24  *      Suite 302
25  *      Pittsburgh, PA  15213
26  *      (412) 268-7393, fax: (412) 268-7395
27  *      innovation@andrew.cmu.edu
28  *
29  * 4. Redistributions of any form whatsoever must retain the following
30  *    acknowledgment:
31  *    "This product includes software developed by Computing Services
32  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33  *
34  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41  */
42 
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46 
47 #include <stdio.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <netinet/in.h>
53 #include <sys/un.h>
54 #ifdef HAVE_UNISTD_H
55 # include <unistd.h>
56 #endif
57 
58 #include "append.h"
59 #include "global.h"
60 #include "notify.h"
61 #include "xstrlcpy.h"
62 #include "xstrlcat.h"
63 #include "mailbox.h"
64 #include "util.h"
65 #include "times.h"
66 
67 /* generated headers are not necessarily in current directory */
68 #include "imap/imap_err.h"
69 
70 #define FNAME_NOTIFY_SOCK "/socket/notify"
71 
72 #define NOTIFY_MAXSIZE 8192
73 
add_arg(char * buf,int max_size,const char * arg,int * buflen)74 static int add_arg(char *buf, int max_size, const char *arg, int *buflen)
75 {
76     const char *myarg = (arg ? arg : "");
77     int len = strlen(myarg) + 1;
78 
79     if (*buflen + len > max_size) return -1;
80 
81     strcat(buf+*buflen, myarg);
82     *buflen += len;
83 
84     return 0;
85 }
86 
notify_dlist(const char * sockpath,const char * method,const char * class,const char * priority,const char * user,const char * mailbox,int nopt,const char ** options,const char * message,const char * fname)87 static void notify_dlist(const char *sockpath, const char *method,
88                          const char *class, const char *priority,
89                          const char *user, const char *mailbox,
90                          int nopt, const char **options,
91                          const char *message, const char *fname)
92 {
93     struct sockaddr_un sun_data;
94     struct protstream *in = NULL, *out = NULL;
95     struct dlist *dl = dlist_newkvlist(NULL, "NOTIFY");
96     struct dlist *res = NULL;
97     struct dlist *il;
98     int c;
99     int soc = -1;
100     int i;
101 
102     dlist_setatom(dl, "METHOD", method);
103     dlist_setatom(dl, "CLASS", class);
104     dlist_setatom(dl, "PRIORITY", priority);
105     dlist_setatom(dl, "USER", user);
106     dlist_setatom(dl, "MAILBOX", mailbox);
107     il = dlist_newlist(dl, "OPTIONS");
108     for (i = 0; i < nopt; i++)
109         dlist_setatom(il, NULL, options[i]);
110     dlist_setatom(dl, "MESSAGE", message);
111     dlist_setatom(dl, "FILEPATH", fname);
112 
113     memset((char *)&sun_data, 0, sizeof(sun_data));
114     sun_data.sun_family = AF_UNIX;
115     strlcpy(sun_data.sun_path, sockpath, sizeof(sun_data.sun_path));
116 
117     soc = socket(PF_UNIX, SOCK_STREAM, 0);
118     if (soc < 0) {
119         syslog(LOG_ERR, "NOTIFY: unable to create notify socket(): %m");
120         goto out;
121     }
122 
123     if (connect(soc, (struct sockaddr *)&sun_data, sizeof(sun_data)) < 0) {
124         syslog(LOG_ERR, "NOTIFY: failed to connect to %s: %m", sockpath);
125         goto out;
126     }
127 
128     in = prot_new(soc, 0);
129     out = prot_new(soc, 1);
130     /* Force use of LITERAL+ */
131     prot_setisclient(in, 1);
132     prot_setisclient(out, 1);
133 
134     dlist_print(dl, 1, out);
135     prot_printf(out, "\r\n");
136     prot_flush(out);
137 
138     c = dlist_parse(&res, 1, 0, in);
139     if (c == '\r') c = prot_getc(in);
140     /* XXX - do something with the response?  Like have NOTIFY answer */
141     if (c == '\n' && res && res->name) {
142         syslog(LOG_NOTICE, "NOTIFY: response %s to method %s", res->name, method);
143     }
144     else {
145         syslog(LOG_ERR, "NOTIFY: error sending %s to %s", method, sockpath);
146     }
147 
148 out:
149     if (in) prot_free(in);
150     if (out) prot_free(out);
151     if (soc >= 0) close(soc);
152     dlist_free(&dl);
153     dlist_free(&res);
154 }
155 
notify_at(time_t when,const char * method,const char * class,const char * priority,const char * user,const char * mboxname,int nopt,const char ** options,const char * message)156 EXPORTED int notify_at(time_t when, const char *method,
157             const char *class, const char *priority,
158             const char *user, const char *mboxname,
159             int nopt, const char **options,
160             const char *message)
161 {
162     struct mailbox *mailbox = NULL;
163     char datestr[RFC822_DATETIME_MAX+1];
164     int i;
165     int r = mailbox_open_iwl("#events", &mailbox);
166     struct buf buf = BUF_INITIALIZER;
167 
168     FILE *f;
169     quota_t qdiffs[QUOTA_NUMRESOURCES] = QUOTA_DIFFS_DONTCARE_INITIALIZER;
170     struct appendstate as;
171     struct stagemsg *stage = NULL;
172 
173     if (r == IMAP_MAILBOX_NONEXISTENT) {
174         r = mboxlist_createmailbox("#events", 0, config_defpartition, 1,
175                                    "cyrus", NULL, 0, 0, 0, 0, NULL);
176         if (!r) r = mailbox_open_iwl("#events", &mailbox);
177     }
178     if (r) goto done;
179 
180     /* Prepare to stage the message */
181     f = append_newstage(mailbox->name, when, 0, &stage);
182     if (!f) {
183         r = IMAP_IOERROR;
184         goto done;
185     }
186 
187     syslog(LOG_NOTICE, "APPENDING TO STAGE: %s (%u)", mailbox->name, (unsigned)when);
188     time_to_rfc822(when, datestr, sizeof(datestr));
189     fprintf(f, "Date: %s\r\n", datestr);
190     fprintf(f, "Method: %s\r\n", charset_encode_mimeheader(method, 0));
191     fprintf(f, "Class: %s\r\n", charset_encode_mimeheader(class, 0));
192     fprintf(f, "Priority: %s\r\n", charset_encode_mimeheader(priority, 0));
193     fprintf(f, "User: %s\r\n", charset_encode_mimeheader(user, 0));
194     fprintf(f, "Mailbox: %s\r\n", charset_encode_mimeheader(mboxname, 0));
195     for (i = 0; i < nopt; i++)
196         fprintf(f, "Option: %s\r\n", charset_encode_mimeheader(options[i], 0));
197     fprintf(f, "Message: %s\r\n", charset_encode_mimeheader(message, 0));
198     fprintf(f, "\r\n");
199 
200     fclose(f);
201     f = NULL;
202 
203     r = append_setup_mbox(&as, mailbox, "cyrus", NULL,
204                           0, qdiffs, 0, 0, 0);
205     if (r) goto done;
206 
207     struct body *body = NULL;
208     r = append_fromstage(&as, &body, stage, when, 0, 0, 0);
209     if (body) {
210         message_free_body(body);
211         free(body);
212     }
213     if (r) goto done;
214 
215     r = append_commit(&as);
216     if (r) goto done;
217 
218 done:
219     append_removestage(stage);
220     append_abort(&as);
221     mailbox_close(&mailbox);
222     buf_free(&buf);
223     if (f) fclose(f);
224 
225     return r;
226 }
227 
notify(const char * method,const char * class,const char * priority,const char * user,const char * mailbox,int nopt,const char ** options,const char * message,const char * fname)228 EXPORTED void notify(const char *method,
229             const char *class, const char *priority,
230             const char *user, const char *mailbox,
231             int nopt, const char **options,
232             const char *message, const char *fname)
233 {
234     const char *notify_sock = config_getstring(IMAPOPT_NOTIFYSOCKET);
235     int soc = -1;
236     struct sockaddr_un sun_data;
237     char buf[NOTIFY_MAXSIZE] = "", noptstr[20];
238     int buflen = 0;
239     int i, r = 0;
240 
241     if (!strncmp(notify_sock, "dlist:", 6)) {
242         notify_dlist(notify_sock+6, method, class, priority,
243                             user, mailbox, nopt, options,
244                             message, fname);
245         return;
246     }
247 
248     soc = socket(AF_UNIX, SOCK_DGRAM, 0);
249     if (soc == -1) {
250         syslog(LOG_ERR, "unable to create notify socket(): %m");
251         goto out;
252     }
253 
254     memset((char *)&sun_data, 0, sizeof(sun_data));
255     sun_data.sun_family = AF_UNIX;
256     if (notify_sock) {
257         strlcpy(sun_data.sun_path, notify_sock, sizeof(sun_data.sun_path));
258     }
259     else {
260         strlcpy(sun_data.sun_path, config_dir, sizeof(sun_data.sun_path));
261         strlcat(sun_data.sun_path,
262                 FNAME_NOTIFY_SOCK, sizeof(sun_data.sun_path));
263     }
264 
265     /*
266      * build request of the form:
267      *
268      * method NUL class NUL priority NUL user NUL mailbox NUL
269      *   nopt NUL N(option NUL) message NUL
270      */
271 
272     r = add_arg(buf, sizeof(buf), method, &buflen);
273     if (!r) r = add_arg(buf, sizeof(buf), class, &buflen);
274     if (!r) r = add_arg(buf, sizeof(buf), priority, &buflen);
275     if (!r) r = add_arg(buf, sizeof(buf), user, &buflen);
276     if (!r) r = add_arg(buf, sizeof(buf), mailbox, &buflen);
277 
278     snprintf(noptstr, sizeof(noptstr), "%d", nopt);
279     if (!r) r = add_arg(buf, sizeof(buf), noptstr, &buflen);
280 
281     for (i = 0; !r && i < nopt; i++) {
282         r = add_arg(buf, sizeof(buf), options[i], &buflen);
283     }
284 
285     if (!r) r = add_arg(buf, sizeof(buf), message, &buflen);
286     if (!r && fname) r = add_arg(buf, sizeof(buf), fname, &buflen);
287 
288     if (r) {
289         syslog(LOG_ERR, "notify datagram too large, %s, %s",
290                user, mailbox);
291         goto out;
292     }
293 
294     r = sendto(soc, buf, buflen, 0,
295                (struct sockaddr *)&sun_data, sizeof(sun_data));
296 
297     if (r < 0) {
298         syslog(LOG_ERR, "unable to sendto() notify socket: %m");
299         goto out;
300     }
301     if (r < buflen) {
302         syslog(LOG_ERR, "short write to notify socket");
303         goto out;
304     }
305 
306 out:
307     xclose(soc);
308 }
309