1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2007-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils 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
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include "pop3d.h"
18 
19 static char *bulletin_mbox_name;
20 static char *bulletin_db_name;
21 
22 void
set_bulletin_db(const char * file)23 set_bulletin_db (const char *file)
24 {
25   bulletin_db_name = mu_strdup (file);
26 }
27 
28 static void
close_bulletin_mailbox(mu_mailbox_t * pmbox)29 close_bulletin_mailbox (mu_mailbox_t *pmbox)
30 {
31   if (pmbox)
32     {
33       mu_mailbox_close (*pmbox);
34       mu_mailbox_destroy (pmbox);
35     }
36 }
37 
38 static int
open_bulletin_mailbox(mu_mailbox_t * pmbox)39 open_bulletin_mailbox (mu_mailbox_t *pmbox)
40 {
41   int status;
42   mu_mailbox_t tmbox;
43 
44   if ((status = mu_mailbox_create (&tmbox, bulletin_mbox_name)) != 0)
45     {
46       mu_error (_("cannot create bulletin mailbox `%s': %s"),
47 		bulletin_mbox_name, mu_strerror (status));
48       return 1;
49     }
50 
51   if ((status = mu_mailbox_open (tmbox, MU_STREAM_READ)) != 0)
52     {
53       mu_mailbox_destroy (&tmbox);
54       mu_error (_("cannot open bulletin mailbox `%s': %s"),
55 		bulletin_mbox_name, mu_strerror (status));
56       return 1;
57     }
58   if (!pmbox)
59     close_bulletin_mailbox (&tmbox);
60   else
61     *pmbox = tmbox;
62   return 0;
63 }
64 
65 int
set_bulletin_source(const char * source)66 set_bulletin_source (const char *source)
67 {
68   bulletin_mbox_name = mu_strdup (source);
69   return 0;
70 }
71 
72 static int
read_popbull_file(size_t * pnum)73 read_popbull_file (size_t *pnum)
74 {
75   int rc = 1;
76   FILE *fp;
77   char *filename = mu_tilde_expansion ("~/.popbull", MU_HIERARCHY_DELIMITER,
78 				       auth_data->dir);
79 
80   if (!filename)
81     return 1;
82   fp = fopen (filename, "r");
83   if (fp)
84     {
85       char buf[128];
86       char *p = fgets (buf, sizeof buf, fp);
87       if (p)
88 	{
89 	  *pnum = strtoul (buf, &p, 0);
90 	  rc = *p && !mu_isspace (*p);
91 	}
92       fclose (fp);
93     }
94   return rc;
95 }
96 
97 static int
write_popbull_file(size_t num)98 write_popbull_file (size_t num)
99 {
100   int rc = 1;
101   FILE *fp;
102   char *filename = mu_tilde_expansion ("~/.popbull", MU_HIERARCHY_DELIMITER,
103 				       auth_data->dir);
104 
105   if (!filename)
106     return 1;
107   fp = fopen (filename, "w");
108   if (fp)
109     {
110       fprintf (fp, "%s\n", mu_umaxtostr (0, num));
111       fclose (fp);
112       rc = 0;
113     }
114   return rc;
115 }
116 
117 #ifdef ENABLE_DBM
118 int
read_bulletin_db(size_t * pnum)119 read_bulletin_db (size_t *pnum)
120 {
121   mu_dbm_file_t db;
122   struct mu_dbm_datum key, data;
123   int rc;
124   char sbuf[128];
125   char *bufptr;
126   char *buf = NULL;
127   size_t s;
128   char *p;
129 
130   rc = mu_dbm_create (bulletin_db_name, &db, DEFAULT_GROUP_DB_SAFETY);
131   if (rc)
132     {
133       mu_diag_output (MU_DIAG_ERROR, _("unable to create bulletin db"));
134       return rc;
135     }
136 
137   rc = mu_dbm_safety_check (db);
138   if (rc)
139     {
140       mu_dbm_destroy (&db);
141       if (rc == ENOENT)
142 	{
143 	  *pnum = 0;
144 	  return 0;
145 	}
146       mu_diag_output (MU_DIAG_ERROR,
147 		      _("bulletin db %s fails safety check: %s"),
148 		      bulletin_db_name, mu_strerror (rc));
149       return 1;
150     }
151 
152   rc = mu_dbm_open (db, MU_STREAM_READ, 0660);
153   if (rc)
154     {
155       mu_error (_("unable to open bulletin db for reading: %s"),
156 		mu_strerror (rc));
157       return rc;
158     }
159 
160   memset (&key, 0, sizeof key);
161   memset (&data, 0, sizeof data);
162 
163   key.mu_dptr = username;
164   key.mu_dsize = strlen (username);
165 
166   rc = mu_dbm_fetch (db, &key, &data);
167 
168   if (rc == MU_ERR_NOENT)
169     {
170       mu_dbm_destroy (&db);
171       *pnum = 0;
172       return 0;
173     }
174   else if (rc)
175     {
176       mu_error (_("cannot fetch bulletin db data: %s"),
177 		mu_dbm_strerror (db));
178       mu_dbm_destroy (&db);
179       return 1;
180     }
181   mu_dbm_destroy (&db);
182 
183   s = data.mu_dsize;
184   if (s < sizeof sbuf)
185     bufptr = sbuf;
186   else
187     {
188       buf = malloc (s + 1);
189       if (!buf)
190 	{
191 	  mu_error ("%s", mu_strerror (errno));
192 	  return 1;
193 	}
194       bufptr = buf;
195     }
196 
197   memcpy (bufptr, data.mu_dptr, s);
198   bufptr[s] = 0;
199   mu_dbm_datum_free (&data);
200 
201   rc = 1;
202   *pnum = strtoul (bufptr, &p, 0);
203   if (*p == 0)
204     rc = 0;
205   else
206     {
207       mu_error (_("wrong bulletin database format for `%s'"),
208 		username);
209     }
210 
211   free (buf);
212   return rc;
213 }
214 
215 int
write_bulletin_db(size_t num)216 write_bulletin_db (size_t num)
217 {
218   mu_dbm_file_t db;
219   struct mu_dbm_datum key, data;
220   int rc;
221   const char *p;
222 
223   rc = mu_dbm_create (bulletin_db_name, &db, DEFAULT_GROUP_DB_SAFETY);
224   if (rc)
225     {
226       mu_diag_output (MU_DIAG_ERROR, _("unable to create bulletin db"));
227       return rc;
228     }
229 
230   rc = mu_dbm_safety_check (db);
231   if (rc && rc != ENOENT)
232     {
233       mu_diag_output (MU_DIAG_ERROR,
234 		      _("bulletin db %s fails safety check: %s"),
235 		      bulletin_db_name, mu_strerror (rc));
236       mu_dbm_destroy (&db);
237       return rc;
238     }
239 
240   rc = mu_dbm_open (db, MU_STREAM_RDWR, 0660);
241   if (rc)
242     {
243       mu_error (_("unable to open bulletin db for writing: %s"),
244 		mu_strerror (rc));
245       mu_dbm_destroy (&db);
246       return rc;
247     }
248 
249   memset (&key, 0, sizeof key);
250   memset (&data, 0, sizeof data);
251 
252   key.mu_dptr = username;
253   key.mu_dsize = strlen (username);
254   p = mu_umaxtostr (0, num);
255   data.mu_dptr = (char *) p;
256   data.mu_dsize = strlen (p);
257 
258   rc = mu_dbm_store (db, &key, &data, 1);
259   if (rc)
260     mu_error (_("cannot store datum in bulletin db: %s"),
261 	      mu_dbm_strerror (db));
262 
263   mu_dbm_destroy (&db);
264   return rc;
265 }
266 #endif /* ENABLE_DBM */
267 
268 int
get_last_delivered_num(size_t * pret)269 get_last_delivered_num (size_t *pret)
270 {
271 #ifdef ENABLE_DBM
272   if (bulletin_db_name && read_bulletin_db (pret) == 0)
273     return 0;
274 #endif
275   return read_popbull_file (pret);
276 }
277 
278 void
store_last_delivered_num(size_t num)279 store_last_delivered_num (size_t num)
280 {
281 #ifdef ENABLE_DBM
282   if (bulletin_db_name && write_bulletin_db (num) == 0)
283     return;
284 #endif
285   write_popbull_file (num);
286 }
287 
288 void
deliver_pending_bulletins()289 deliver_pending_bulletins ()
290 {
291   mu_mailbox_t bull;
292   int rc;
293   size_t lastnum, total;
294 
295   if (!bulletin_mbox_name)
296     return;
297 
298   rc = open_bulletin_mailbox (&bull);
299   if (rc || get_last_delivered_num (&lastnum))
300     return;
301 
302   rc = mu_mailbox_messages_count (bull, &total);
303   if (rc)
304     mu_error (_("cannot count bulletins: %s"), mu_strerror (rc));
305   else
306     {
307       mu_diag_output (MU_DIAG_DEBUG,
308 	      "user %s, last bulletin %lu, total bulletins %lu",
309 	      username, (unsigned long) lastnum, (unsigned long) total);
310 
311       if (lastnum < total)
312 	{
313 	  size_t i;
314 	  size_t count = total - lastnum;
315 
316 	  mu_diag_output (MU_DIAG_INFO,
317 		  ngettext ("user %s: delivering %lu pending bulletin",
318 			    "user %s: delivering %lu pending bulletins",
319 			    count),
320 		  username, (unsigned long) count);
321 
322 	  for (i = lastnum + 1; i <= total; i++)
323 	    {
324 	      int rc;
325 	      mu_message_t msg;
326 
327 	      if ((rc = mu_mailbox_get_message (bull, i, &msg)) != 0)
328 		{
329 		  mu_error (_("cannot read bulletin %lu: %s"),
330 			    (unsigned long) i, mu_strerror (rc));
331 		  break;
332 		}
333 
334 	      if ((rc = mu_mailbox_append_message (mbox, msg)) != 0)
335 		{
336 		  mu_error (_("cannot append message %lu: %s"),
337 			    (unsigned long) i, mu_strerror (rc));
338 		  break;
339 		}
340 	    }
341 	  store_last_delivered_num (i - 1);
342 	}
343     }
344 
345   close_bulletin_mailbox (&bull);
346 }
347 
348