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