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/mman.h>
51 #include <sys/types.h>
52 #if defined(__FreeBSD__)
53 #include <sys/sysctl.h>
54 #endif
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <sys/un.h>
58 #ifdef HAVE_UNISTD_H
59 # include <unistd.h>
60 #endif
61
62 #include "append.h"
63 #include "global.h"
64 #include "notify.h"
65 #include "xstrlcpy.h"
66 #include "xstrlcat.h"
67 #include "mailbox.h"
68 #include "util.h"
69 #include "times.h"
70
71 /* generated headers are not necessarily in current directory */
72 #include "imap/imap_err.h"
73
74 #define FNAME_NOTIFY_SOCK "/socket/notify"
75
add_arg(char * buf,int max_size,const char * arg,int * buflen)76 static int add_arg(char *buf, int max_size, const char *arg, int *buflen)
77 {
78 const char *myarg = (arg ? arg : "");
79 int len = strlen(myarg) + 1;
80
81 if (*buflen + len > max_size) return -1;
82
83 strcat(buf+*buflen, myarg);
84 *buflen += len;
85
86 return 0;
87 }
88
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)89 static void notify_dlist(const char *sockpath, const char *method,
90 const char *class, const char *priority,
91 const char *user, const char *mailbox,
92 int nopt, const char **options,
93 const char *message, const char *fname)
94 {
95 struct sockaddr_un sun_data;
96 struct protstream *in = NULL, *out = NULL;
97 struct dlist *dl = dlist_newkvlist(NULL, "NOTIFY");
98 struct dlist *res = NULL;
99 struct dlist *il;
100 int c;
101 int soc = -1;
102 int i;
103
104 dlist_setatom(dl, "METHOD", method);
105 dlist_setatom(dl, "CLASS", class);
106 dlist_setatom(dl, "PRIORITY", priority);
107 dlist_setatom(dl, "USER", user);
108 dlist_setatom(dl, "MAILBOX", mailbox);
109 il = dlist_newlist(dl, "OPTIONS");
110 for (i = 0; i < nopt; i++)
111 dlist_setatom(il, NULL, options[i]);
112 dlist_setatom(dl, "MESSAGE", message);
113 dlist_setatom(dl, "FILEPATH", fname);
114
115 memset((char *)&sun_data, 0, sizeof(sun_data));
116 sun_data.sun_family = AF_UNIX;
117 strlcpy(sun_data.sun_path, sockpath, sizeof(sun_data.sun_path));
118
119 soc = socket(PF_UNIX, SOCK_STREAM, 0);
120 if (soc < 0) {
121 syslog(LOG_ERR, "NOTIFY: unable to create notify socket(): %m");
122 goto out;
123 }
124
125 if (connect(soc, (struct sockaddr *)&sun_data, sizeof(sun_data)) < 0) {
126 syslog(LOG_ERR, "NOTIFY: failed to connect to %s: %m", sockpath);
127 goto out;
128 }
129
130 in = prot_new(soc, 0);
131 out = prot_new(soc, 1);
132 /* Force use of LITERAL+ */
133 prot_setisclient(in, 1);
134 prot_setisclient(out, 1);
135
136 dlist_print(dl, 1, out);
137 prot_printf(out, "\r\n");
138 prot_flush(out);
139
140 c = dlist_parse(&res, 1, 0, in);
141 if (c == '\r') c = prot_getc(in);
142 /* XXX - do something with the response? Like have NOTIFY answer */
143 if (c == '\n' && res && res->name) {
144 syslog(LOG_NOTICE, "NOTIFY: response %s to method %s", res->name, method);
145 }
146 else {
147 syslog(LOG_ERR, "NOTIFY: error sending %s to %s", method, sockpath);
148 }
149
150 out:
151 if (in) prot_free(in);
152 if (out) prot_free(out);
153 if (soc >= 0) close(soc);
154 dlist_free(&dl);
155 dlist_free(&res);
156 }
157
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)158 EXPORTED void notify(const char *method,
159 const char *class, const char *priority,
160 const char *user, const char *mailbox,
161 int nopt, const char **options,
162 const char *message, const char *fname)
163 {
164 const char *notify_sock = config_getstring(IMAPOPT_NOTIFYSOCKET);
165 int soc = -1;
166 struct sockaddr_un sun_data;
167 char buf[NOTIFY_MAXSIZE] = "", noptstr[20];
168 int buflen = 0;
169 int i, r = 0;
170 unsigned bufsiz;
171 socklen_t optlen;
172
173 if (!strncmp(notify_sock, "dlist:", 6)) {
174 notify_dlist(notify_sock+6, method, class, priority,
175 user, mailbox, nopt, options,
176 message, fname);
177 return;
178 }
179
180 soc = socket(AF_UNIX, SOCK_DGRAM, 0);
181 if (soc == -1) {
182 syslog(LOG_ERR, "unable to create notify socket(): %m");
183 goto out;
184 }
185
186 memset((char *)&sun_data, 0, sizeof(sun_data));
187 sun_data.sun_family = AF_UNIX;
188 if (notify_sock) {
189 strlcpy(sun_data.sun_path, notify_sock, sizeof(sun_data.sun_path));
190 }
191 else {
192 strlcpy(sun_data.sun_path, config_dir, sizeof(sun_data.sun_path));
193 strlcat(sun_data.sun_path,
194 FNAME_NOTIFY_SOCK, sizeof(sun_data.sun_path));
195 }
196
197 #if defined(__FreeBSD__)
198 size_t maxsockbuf;
199 size_t len = sizeof(maxsockbuf);
200 r = sysctlbyname("kern.ipc.maxsockbuf", &maxsockbuf, &len, NULL, 0);
201 if (r == 0) {
202 bufsiz = MIN(maxsockbuf, NOTIFY_MAXSIZE);
203 } else {
204 syslog(LOG_WARNING, "unable to sysctlbyname(kern.ipc.maxsockbuf): %m");
205 bufsiz = NOTIFY_MAXSIZE;
206 }
207
208 optlen = sizeof(bufsiz);
209 r = setsockopt(soc, SOL_SOCKET, SO_SNDBUF, &bufsiz, optlen);
210 if (r == -1) {
211 syslog(LOG_WARNING,
212 "unable to setsockopt(SO_SNDBUF) on notify socket: %m");
213 }
214 #else
215 /* Get send buffer size */
216 optlen = sizeof(bufsiz);
217 r = getsockopt(soc, SOL_SOCKET, SO_SNDBUF, &bufsiz, &optlen);
218 if (r == -1) {
219 syslog(LOG_ERR, "unable to getsockopt(SO_SNDBUF) on notify socket: %m");
220 goto out;
221 }
222
223 /* Use minimum of 1/10 of send buffer size (-overhead) NOTIFY_MAXSIZE */
224 bufsiz = MIN(bufsiz / 10 - 32, NOTIFY_MAXSIZE);
225 #endif
226
227 /*
228 * build request of the form:
229 *
230 * method NUL class NUL priority NUL user NUL mailbox NUL
231 * nopt NUL N(option NUL) message NUL
232 */
233
234 r = add_arg(buf, bufsiz, method, &buflen);
235 if (!r) r = add_arg(buf, bufsiz, class, &buflen);
236 if (!r) r = add_arg(buf, bufsiz, priority, &buflen);
237 if (!r) r = add_arg(buf, bufsiz, user, &buflen);
238 if (!r) r = add_arg(buf, bufsiz, mailbox, &buflen);
239
240 snprintf(noptstr, sizeof(noptstr), "%d", nopt);
241 if (!r) r = add_arg(buf, bufsiz, noptstr, &buflen);
242
243 for (i = 0; !r && i < nopt; i++) {
244 r = add_arg(buf, bufsiz, options[i], &buflen);
245 }
246
247 if (!r) r = add_arg(buf, bufsiz, message, &buflen);
248 if (!r && fname) r = add_arg(buf, bufsiz, fname, &buflen);
249
250 if (r) {
251 syslog(LOG_ERR, "notify datagram too large, %s, %s",
252 user, mailbox);
253 goto out;
254 }
255
256 r = sendto(soc, buf, buflen, 0,
257 (struct sockaddr *)&sun_data, sizeof(sun_data));
258
259 if (r < 0) {
260 syslog(LOG_ERR, "unable to sendto() notify socket: %m");
261 goto out;
262 }
263 if (r < buflen) {
264 syslog(LOG_ERR, "short write to notify socket");
265 goto out;
266 }
267
268 out:
269 xclose(soc);
270 }
271