]> diplodocus.org Git - nmh/blob - uip/mhcachesbr.c
fdcompare.c: Move interface to own file.
[nmh] / uip / mhcachesbr.c
1 /* mhcachesbr.c -- routines to manipulate the MIME content cache
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "sbr/error.h"
10 #include <fcntl.h>
11 #include "h/md5.h"
12 #include "h/mts.h"
13 #include "h/tws.h"
14 #include "h/mime.h"
15 #include "h/mhparse.h"
16 #include "h/mhcachesbr.h"
17 #include "h/utils.h"
18 #include "mhmisc.h"
19 #include "sbr/lock_file.h"
20 #include "sbr/m_mktemp.h"
21
22 #ifdef HAVE_SYS_TIME_H
23 # include <sys/time.h>
24 #endif
25 #include <time.h>
26
27 #define X(sw, minchars, id) { sw, minchars, id },
28 DEFINE_SWITCH_ARRAY(CACHE, caches);
29 #undef X
30 struct swit *cache_policy = caches;
31
32 extern int debugsw;
33
34 /* cache policies */
35 int rcachesw = CACHE_ASK;
36 int wcachesw = CACHE_ASK;
37
38 /*
39 * Location of public and private cache. These must
40 * be set before these routines are called.
41 */
42 char *cache_public;
43 char *cache_private;
44
45 /*
46 * static prototypes
47 */
48 static void cache_content (CT);
49 static int find_cache_aux (int, char *, char *, char *, int);
50 static int find_cache_aux2 (char *, char *, char *, int);
51
52
53 /*
54 * Top level entry point to cache content
55 * from a group of messages
56 */
57
58 void
59 cache_all_messages (CT *cts)
60 {
61 CT ct, *ctp;
62
63 for (ctp = cts; *ctp; ctp++) {
64 ct = *ctp;
65 if (type_ok (ct, 1)) {
66 cache_content (ct);
67 if (ct->c_fp) {
68 fclose (ct->c_fp);
69 ct->c_fp = NULL;
70 }
71 if (ct->c_ceclosefnx)
72 (*ct->c_ceclosefnx) (ct);
73 }
74 }
75 flush_errors ();
76 }
77
78
79 /*
80 * Entry point to cache content from external sources.
81 */
82
83 static void
84 cache_content (CT ct)
85 {
86 int cachetype;
87 char *file, cachefile[BUFSIZ];
88 CE ce = &ct->c_cefile;
89
90 if (!ct->c_id) {
91 inform("no %s: field in %s", ID_FIELD, ct->c_file);
92 return;
93 }
94
95 if (!ce) {
96 inform("unable to decode %s", ct->c_file);
97 return;
98 }
99
100 if (find_cache (NULL, wcachesw != CACHE_NEVER ? wcachesw : CACHE_ASK,
101 &cachetype, ct->c_id, cachefile, sizeof(cachefile))
102 == NOTOK) {
103 inform("unable to cache %s's contents", ct->c_file);
104 return;
105 }
106 if (wcachesw != CACHE_NEVER && wcachesw != CACHE_ASK) {
107 fflush (stdout);
108 fprintf (stderr, "caching message %s as file %s\n", ct->c_file,
109 cachefile);
110 }
111
112 if (ce->ce_file) {
113 int mask = umask (cachetype ? ~m_gmprot () : 0222);
114 FILE *fp;
115
116 if (debugsw)
117 fprintf (stderr, "caching by copying %s...\n", ce->ce_file);
118
119 file = NULL;
120 if ((*ct->c_ceopenfnx) (ct, &file) == NOTOK)
121 goto reset_umask;
122
123 if ((fp = fopen (cachefile, "w"))) {
124 int cc;
125 char buffer[BUFSIZ];
126 FILE *gp = ce->ce_fp;
127
128 fseek (gp, 0L, SEEK_SET);
129
130 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
131 > 0)
132 if ((int) fwrite (buffer, sizeof(*buffer), cc, fp) < cc) {
133 advise ("cache_content", "fwrite");
134 }
135 fflush (fp);
136
137 if (ferror (gp)) {
138 admonish (ce->ce_file, "error reading");
139 (void) m_unlink (cachefile);
140 } else {
141 if (ferror (fp)) {
142 admonish (cachefile, "error writing");
143 (void) m_unlink (cachefile);
144 }
145 }
146 fclose (fp);
147 } else
148 content_error (cachefile, ct, "unable to fopen for writing");
149 reset_umask:
150 umask (mask);
151 } else {
152 if (debugsw)
153 fprintf (stderr, "in place caching...\n");
154
155 file = cachefile;
156 if ((*ct->c_ceopenfnx) (ct, &file) != NOTOK)
157 chmod (cachefile, cachetype ? m_gmprot () : 0444);
158 }
159 }
160
161
162 int
163 find_cache (CT ct, int policy, int *writing, char *id,
164 char *buffer, int buflen)
165 {
166 int status = NOTOK;
167
168 if (id == NULL)
169 return NOTOK;
170 id = trimcpy (id);
171
172 if (debugsw)
173 fprintf (stderr, "find_cache %s(%d) %s %s\n", caches[policy].sw,
174 policy, writing ? "writing" : "reading", id);
175
176 switch (policy) {
177 case CACHE_NEVER:
178 default:
179 break;
180
181 case CACHE_ASK:
182 case CACHE_PUBLIC:
183 if (cache_private
184 && !writing
185 && find_cache_aux (0, cache_private, id,
186 buffer, buflen) == OK) {
187 if (access (buffer, R_OK) != NOTOK) {
188 got_private:
189 if (writing)
190 *writing = 1;
191 got_it:
192 status = OK;
193 break;
194 }
195 }
196 if (cache_public
197 && find_cache_aux (writing ? 1 : 0, cache_public, id,
198 buffer, buflen) == OK) {
199 if (writing || access (buffer, R_OK) != NOTOK) {
200 if (writing)
201 *writing = 0;
202 goto got_it;
203 }
204 }
205 break;
206
207 case CACHE_PRIVATE:
208 if (cache_private
209 && find_cache_aux (writing ? 2 : 0, cache_private, id,
210 buffer, buflen) == OK) {
211 if (writing || access (buffer, R_OK) != NOTOK)
212 goto got_private;
213 }
214 break;
215
216 }
217
218 if (status == OK && policy == CACHE_ASK) {
219 int len, buflen;
220 char *bp, query[BUFSIZ];
221
222 /* Get buffer ready to go */
223 bp = query;
224 buflen = sizeof(query);
225
226 /* Now, construct query */
227 if (writing) {
228 snprintf (bp, buflen, "Make cached, publicly-accessible copy");
229 } else {
230 struct stat st;
231
232 snprintf (bp, buflen, "Use cached copy");
233 len = strlen (bp);
234 bp += len;
235 buflen -= len;
236
237 if (ct->c_partno) {
238 snprintf (bp, buflen, " of content %s", ct->c_partno);
239 len = strlen (bp);
240 bp += len;
241 buflen -= len;
242 }
243
244 stat (buffer, &st);
245 snprintf (bp, buflen, " (size %lu octets)",
246 (unsigned long) st.st_size);
247 }
248 len = strlen (bp);
249 bp += len;
250 buflen -= len;
251
252 snprintf (bp, buflen, "\n in file %s? ", buffer);
253
254 /* Now, check answer */
255 if (!read_yes_or_no_if_tty (query))
256 status = NOTOK;
257 }
258
259 if (status == OK && writing) {
260 if (*writing && strchr(buffer, '/'))
261 make_intermediates (buffer);
262 (void) m_unlink (buffer);
263 }
264
265 free (id);
266 return status;
267 }
268
269
270 static int
271 find_cache_aux (int writing, char *directory, char *id,
272 char *buffer, int buflen)
273 {
274 int mask;
275 bool usemap;
276 char mapfile[BUFSIZ], mapname[BUFSIZ];
277 FILE *fp;
278 int failed_to_lock = 0;
279 static int partno, pid;
280 static time_t clock = 0;
281
282 usemap = true;
283
284 if (debugsw)
285 fprintf (stderr, "find_cache_aux %s usemap=%d\n", directory, usemap);
286
287 snprintf (mapfile, sizeof(mapfile), "%s/cache.map", directory);
288 if (find_cache_aux2 (mapfile, id, mapname, sizeof(mapname)) == OK)
289 goto done_map;
290
291 if (!writing) {
292 if (usemap)
293 return NOTOK;
294
295 use_raw:
296 snprintf (buffer, buflen, "%s/%s", directory, id);
297 return OK;
298 }
299
300 if (!usemap && access (mapfile, W_OK) == NOTOK)
301 goto use_raw;
302
303 if (clock != 0) {
304 time_t now;
305
306 time (&now);
307 if (now > clock)
308 clock = 0;
309 } else {
310 pid = getpid ();
311 }
312
313 if (clock == 0) {
314 time (&clock);
315 partno = 0;
316 } else {
317 if (partno > 0xff) {
318 clock++;
319 partno = 0;
320 }
321 }
322
323 snprintf (mapname, sizeof(mapname), "%08x%04x%02x",
324 (unsigned int) (clock & 0xffffffff),
325 (unsigned int) (pid & 0xffff),
326 (unsigned int) (partno++ & 0xff));
327
328 if (debugsw)
329 fprintf (stderr, "creating mapping %s->%s\n", mapname, id);
330
331 make_intermediates (mapfile);
332 mask = umask (writing == 2 ? 0077 : 0);
333 if (!(fp = lkfopendata (mapfile, "a", &failed_to_lock)) && errno == ENOENT) {
334 int fd;
335
336 if ((fd = creat (mapfile, 0666)) != NOTOK) {
337 close (fd);
338 fp = lkfopendata (mapfile, "a", &failed_to_lock);
339 if (failed_to_lock) {
340 adios (mapfile, "failed to lock");
341 }
342 }
343 }
344 umask (mask);
345 if (!fp)
346 return NOTOK;
347 fprintf (fp, "%s: %s\n", mapname, id);
348 lkfclosedata (fp, mapfile);
349
350 done_map:
351 if (*mapname == '/')
352 strncpy (buffer, mapname, buflen);
353 else
354 snprintf (buffer, buflen, "%s/%s", directory, mapname);
355 if (debugsw)
356 fprintf (stderr, "use %s\n", buffer);
357
358 return OK;
359 }
360
361
362 static int
363 find_cache_aux2 (char *mapfile, char *id, char *mapname, int namelen)
364 {
365 int state;
366 char buf[NMH_BUFSIZ], name[NAMESZ];
367 FILE *fp;
368 m_getfld_state_t gstate;
369 int failed_to_lock = 0;
370
371 if (!(fp = lkfopendata (mapfile, "r", &failed_to_lock)))
372 return NOTOK;
373
374 gstate = m_getfld_state_init(fp);
375 for (;;) {
376 int result;
377 char *cp, *dp;
378 int bufsz = sizeof buf;
379
380 switch (state = m_getfld2(&gstate, name, buf, &bufsz)) {
381 case FLD:
382 case FLDPLUS:
383 strncpy (mapname, name, namelen);
384 if (state != FLDPLUS)
385 cp = buf;
386 else {
387 cp = mh_xstrdup(buf);
388 while (state == FLDPLUS) {
389 bufsz = sizeof buf;
390 state = m_getfld2(&gstate, name, buf, &bufsz);
391 cp = add (buf, cp);
392 }
393 }
394 dp = trimcpy (cp);
395 if (cp != buf)
396 free (cp);
397 if (debugsw)
398 fprintf (stderr, "compare %s to %s <- %s\n", id, dp,
399 mapname);
400 result = strcmp (id, dp);
401 free (dp);
402 if (result == 0) {
403 lkfclosedata (fp, mapfile);
404 return OK;
405 }
406 continue;
407
408 case BODY:
409 case FILEEOF:
410 default:
411 break;
412 }
413 break;
414 }
415 m_getfld_state_destroy (&gstate);
416
417 lkfclosedata (fp, mapfile);
418 return NOTOK;
419 }