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