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