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