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