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