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