1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2007-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library; If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21 #include <sys/select.h>
22 #include <sys/time.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <mailutils/server.h>
28 #include <mailutils/errno.h>
29 #include <mailutils/acl.h>
30
31
32 struct _mu_connection
33 {
34 struct _mu_connection *next, *prev;
35 int fd;
36 mu_conn_loop_fp f_loop;
37 mu_conn_free_fp f_free;
38 void *data;
39 };
40
41 #define MU_SERVER_TIMEOUT 0x1
42
43 struct _mu_server
44 {
45 int nfd;
46 fd_set fdset;
47 int flags;
48 struct timeval timeout;
49 struct _mu_connection *head, *tail;
50 mu_server_idle_fp f_idle;
51 mu_server_free_fp f_free;
52 void *server_data;
53 };
54
55 void
recompute_nfd(mu_server_t srv)56 recompute_nfd (mu_server_t srv)
57 {
58 struct _mu_connection *p;
59 int nfd = 0;
60 for (p = srv->head; p; p = p->next)
61 if (p->fd > nfd)
62 nfd = p->fd;
63 srv->nfd = nfd + 1;
64 }
65
66 void
destroy_connection(mu_server_t srv,struct _mu_connection * conn)67 destroy_connection (mu_server_t srv, struct _mu_connection *conn)
68 {
69 if (conn->f_free)
70 conn->f_free (conn->data, srv->server_data);
71 free (conn);
72 }
73
74 void
remove_connection(mu_server_t srv,struct _mu_connection * conn)75 remove_connection (mu_server_t srv, struct _mu_connection *conn)
76 {
77 struct _mu_connection *p;
78
79 close (conn->fd);
80 FD_CLR (conn->fd, &srv->fdset);
81
82 p = conn->prev;
83 if (p)
84 p->next = conn->next;
85 else /* we're at head */
86 srv->head = conn->next;
87
88 p = conn->next;
89 if (p)
90 p->prev = conn->prev;
91 else /* we're at tail */
92 srv->tail = conn->prev;
93
94 if (conn->fd == srv->nfd - 1)
95 recompute_nfd (srv);
96
97 destroy_connection (srv, conn);
98 }
99
100 int
connection_loop(mu_server_t srv,fd_set * fdset)101 connection_loop (mu_server_t srv, fd_set *fdset)
102 {
103 struct _mu_connection *conn;
104 for (conn = srv->head; conn;)
105 {
106 struct _mu_connection *next = conn->next;
107 if (FD_ISSET (conn->fd, fdset))
108 {
109 int rc;
110
111 rc = conn->f_loop (conn->fd, conn->data, srv->server_data);
112 switch (rc)
113 {
114 case 0:
115 break;
116
117 case MU_SERVER_CLOSE_CONN:
118 default:
119 remove_connection (srv, conn);
120 break;
121
122 case MU_SERVER_SHUTDOWN:
123 return 1;
124 }
125 }
126 conn = next;
127 }
128 return 0;
129 }
130
131 void
make_fdset(mu_server_t srv)132 make_fdset (mu_server_t srv)
133 {
134 struct _mu_connection *p;
135 int nfd = 0;
136
137 FD_ZERO (&srv->fdset);
138 for (p = srv->head; p; p = p->next)
139 {
140 FD_SET (p->fd, &srv->fdset);
141 if (p->fd > nfd)
142 nfd = p->fd;
143 }
144 srv->nfd = nfd + 1;
145 }
146
147 int
mu_server_run(mu_server_t srv)148 mu_server_run (mu_server_t srv)
149 {
150 int status = 0;
151
152 if (!srv)
153 return EINVAL;
154 if (!srv->head)
155 return MU_ERR_NOENT;
156
157 make_fdset (srv);
158
159 while (1)
160 {
161 int rc;
162 fd_set rdset;
163 struct timeval *to;
164
165 rdset = srv->fdset;
166 to = (srv->flags & MU_SERVER_TIMEOUT) ? &srv->timeout : NULL;
167 rc = select (srv->nfd, &rdset, NULL, NULL, to);
168 if (rc == -1 && errno == EINTR)
169 {
170 if (srv->f_idle && srv->f_idle (srv->server_data))
171 break;
172 continue;
173 }
174 if (rc < 0)
175 return errno;
176
177 if (connection_loop (srv, &rdset))
178 {
179 status = MU_ERR_FAILURE;
180 break;
181 }
182 }
183 return status;
184 }
185
186 int
mu_server_create(mu_server_t * psrv)187 mu_server_create (mu_server_t *psrv)
188 {
189 mu_server_t srv = calloc (1, sizeof (*srv));
190 if (!srv)
191 return ENOMEM;
192 *psrv = srv;
193 return 0;
194 }
195
196 int
mu_server_destroy(mu_server_t * psrv)197 mu_server_destroy (mu_server_t *psrv)
198 {
199 mu_server_t srv;
200 struct _mu_connection *p;
201
202 if (!psrv)
203 return EINVAL;
204 srv = *psrv;
205 if (!srv)
206 return 0;
207
208 for (p = srv->head; p; )
209 {
210 struct _mu_connection *next = p->next;
211 destroy_connection (srv, p);
212 p = next;
213 }
214
215 if (srv->f_free)
216 srv->f_free (srv->server_data);
217
218 free (srv);
219 *psrv = NULL;
220 return 0;
221 }
222
223 int
mu_server_count(mu_server_t srv,size_t * pcount)224 mu_server_count (mu_server_t srv, size_t *pcount)
225 {
226 size_t n = 0;
227 struct _mu_connection *p;
228
229 if (!srv)
230 return EINVAL;
231 for (p = srv->head; p; p = p->next)
232 n++;
233 *pcount = n;
234 return 0;
235 }
236
237 int
mu_server_set_idle(mu_server_t srv,mu_server_idle_fp fp)238 mu_server_set_idle (mu_server_t srv, mu_server_idle_fp fp)
239 {
240 if (!srv)
241 return EINVAL;
242 srv->f_idle = fp;
243 return 0;
244 }
245
246 int
mu_server_set_data(mu_server_t srv,void * data,mu_server_free_fp fp)247 mu_server_set_data (mu_server_t srv, void *data, mu_server_free_fp fp)
248 {
249 if (!srv)
250 return EINVAL;
251 srv->server_data = data;
252 srv->f_free = fp;
253 return 0;
254 }
255
256 int
mu_server_set_timeout(mu_server_t srv,struct timeval * to)257 mu_server_set_timeout (mu_server_t srv, struct timeval *to)
258 {
259 if (!srv)
260 return EINVAL;
261 if (!to)
262 srv->flags &= ~MU_SERVER_TIMEOUT;
263 else
264 {
265 srv->timeout = *to;
266 srv->flags |= MU_SERVER_TIMEOUT;
267 }
268 return 0;
269 }
270
271 int
mu_server_add_connection(mu_server_t srv,int fd,void * data,mu_conn_loop_fp loop,mu_conn_free_fp free)272 mu_server_add_connection (mu_server_t srv,
273 int fd, void *data,
274 mu_conn_loop_fp loop, mu_conn_free_fp free)
275 {
276 struct _mu_connection *p;
277
278 if (!srv || !loop)
279 return EINVAL;
280
281 p = malloc (sizeof (*p));
282 if (!p)
283 return ENOMEM;
284 p->fd = fd;
285 p->f_loop = loop;
286 p->f_free = free;
287 p->data = data;
288
289 p->next = NULL;
290 p->prev = srv->tail;
291 if (srv->tail)
292 srv->tail->next = p;
293 else
294 srv->head = p;
295 srv->tail = p;
296 return 0;
297 }
298
299
300