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