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