Annotation of libwww/Library/src/HTFWrite.c, revision 2.7
2.1 frystyk 1: /* HTFWrite.c
2: ** FILE WRITER
3: **
2.6 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.1 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
7: ** This version of the stream object just writes to a C file.
8: ** The file is assumed open and left open.
9: **
10: ** Bugs:
11: ** strings written must be less than buffer size.
12: **
13: ** History:
14: ** HFN: wrote it
15: ** HWL: converted the caching scheme to be hierachical by taking
16: ** AL code from Deamon
17: **
18: */
19:
20: /* Library include files */
21: #include "tcp.h"
22: #include "HTUtils.h"
23: #include "HTString.h"
24: #include "HTFormat.h"
25: #include "HTAlert.h"
2.4 frystyk 26: #include "HTBind.h"
2.1 frystyk 27: #include "HTList.h"
2.3 frystyk 28: #include "HTParse.h"
29: #include "HTFWrite.h" /* Implemented here */
2.1 frystyk 30:
2.4 frystyk 31: #define HASH_SIZE 1001 /* Tunable */
32:
2.1 frystyk 33: #define CACHE_LIMIT 100 /* files */
34:
2.3 frystyk 35: #define CACHE_INFO ".cache_info"
36: #define INDEX_FILE ".cache_dirindex"
37: #define WELCOME_FILE ".cache_welcome"
38: #define TMP_SUFFIX ".cache_tmp"
39: #define LOCK_SUFFIX ".cache_lock"
2.1 frystyk 40:
2.3 frystyk 41: PUBLIC int HTCacheLimit = CACHE_LIMIT;
2.1 frystyk 42:
2.3 frystyk 43: struct _HTStream {
44: CONST HTStreamClass * isa;
45:
46: FILE * fp;
47: BOOL leave_open; /* Close file? HFN 08/02-94 */
48: char * end_command;
49: BOOL remove_on_close;
50: BOOL announce;
51: char * filename;
52: HTRequest * request; /* saved for callback */
53: BOOL (*callback) PARAMS((struct _HTRequest * req, void * filename));
54: HTCacheItem * cache;
55: };
2.1 frystyk 56:
2.3 frystyk 57: PRIVATE HTStream HTBlackHoleInstance; /* Made static */
2.1 frystyk 58:
2.3 frystyk 59: PRIVATE BOOL HTCacheEnable = NO;
60: PRIVATE char * HTCacheRoot = NULL; /* Destination for cache */
61: PRIVATE HTList * HTCache = NULL; /* List of cached elements */
2.1 frystyk 62:
2.3 frystyk 63: PRIVATE char * HTTmpRoot = NULL; /* Dest for tmp files */
2.1 frystyk 64:
2.3 frystyk 65: /* ------------------------------------------------------------------------- */
66: /* BASIC STREAM CLASSES */
67: /* ------------------------------------------------------------------------- */
2.1 frystyk 68:
2.3 frystyk 69: /*
2.1 frystyk 70: **
2.3 frystyk 71: ** B L A C K H O L E C L A S S
2.1 frystyk 72: **
2.3 frystyk 73: ** There is only one black hole instance shared by anyone
74: ** who wants a black hole. These black holes don't radiate,
75: ** they just absorb data.
76: */
77: PRIVATE int HTBlackHole_put_character ARGS2(HTStream *, me, char, c)
78: {
79: return HT_OK;
80: }
81:
82: PRIVATE int HTBlackHole_put_string ARGS2(HTStream *, me, CONST char*, s)
83: {
84: return HT_OK;
85: }
86:
87: PRIVATE int HTBlackHole_write ARGS3(HTStream *, me, CONST char*, s, int, l)
88: {
89: return HT_OK;
90: }
91:
92: PRIVATE int HTBlackHole_flush ARGS1(HTStream *, me)
93: {
94: return HT_OK;
95: }
96:
97: PRIVATE int HTBlackHole_free ARGS1(HTStream *, me)
98: {
99: return HT_OK;
100: }
101:
102: PRIVATE int HTBlackHole_abort ARGS2(HTStream *, me, HTError, e)
103: {
104: return HT_ERROR;
105: }
106:
107:
108: /* Black Hole stream
109: ** -----------------
110: */
111: PRIVATE CONST HTStreamClass HTBlackHoleClass =
112: {
113: "BlackHole",
114: HTBlackHole_flush,
115: HTBlackHole_free,
116: HTBlackHole_abort,
117: HTBlackHole_put_character,
118: HTBlackHole_put_string,
119: HTBlackHole_write
120: };
121:
122: PUBLIC HTStream * HTBlackHole NOARGS
123: {
124: if (TRACE)
125: fprintf(TDEST, "BlackHole... Created\n");
126: HTBlackHoleInstance.isa = &HTBlackHoleClass; /* The rest is random */
127: return &HTBlackHoleInstance;
128: }
129:
130:
131: /* HTThroughLine
132: ** -------------
2.1 frystyk 133: **
2.3 frystyk 134: ** This function is a dummy function that returns the same output stream
135: ** as given as a parameter. Henrik 01/03-94
2.1 frystyk 136: */
2.3 frystyk 137: PUBLIC HTStream* HTThroughLine ARGS5(
138: HTRequest *, request,
139: void *, param,
140: HTFormat, input_format,
141: HTFormat, output_format,
142: HTStream *, output_stream) /* Only one used */
143: {
144: return output_stream;
145: }
2.1 frystyk 146:
2.3 frystyk 147: /* ------------------------------------------------------------------------- */
148: /* SOCKET WRITER STREAM */
149: /* ------------------------------------------------------------------------- */
150:
151: PRIVATE int HTFWriter_put_character ARGS2(HTStream *, me, char, c)
152: {
153: return (fputc(c, me->fp) == EOF) ? HT_ERROR : HT_OK;
154: }
155:
156: PRIVATE int HTFWriter_put_string ARGS2(HTStream *, me, CONST char*, s)
157: {
158: if (*s) /* For vms :-( 10/04-94 */
159: return (fputs(s, me->fp) == EOF) ? HT_ERROR : HT_OK;
160: return HT_OK;
161: }
162:
2.7 ! frystyk 163: PRIVATE int HTFWriter_flush ARGS1(HTStream *, me)
2.3 frystyk 164: {
2.7 ! frystyk 165: return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
2.3 frystyk 166: }
167:
2.7 ! frystyk 168:
! 169: PRIVATE int HTFWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l)
2.3 frystyk 170: {
2.7 ! frystyk 171: int status ;
! 172: status = (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK ;
! 173: if (l > 1 && status == HT_OK)
! 174: (void)HTFWriter_flush( me) ;
! 175: return status ;
! 176:
! 177: /* return (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK; */
! 178:
2.3 frystyk 179: }
2.7 ! frystyk 180:
2.3 frystyk 181:
182: PRIVATE int HTFWriter_free ARGS1(HTStream *, me)
183: {
184: if (me->cache) {
185: time_t finish_time;
186: time(&finish_time);
187: me->cache->load_delay = finish_time - me->cache->load_time;
188: /* Actually, ought to use draft ANSI-C difftime() */
189: /* But that I bet is more portable in real life (@@?) */
190: }
191:
192: if (me->leave_open != YES) fclose(me->fp);
193:
194: if (me->end_command) { /* Temp file */
195: HTProgress(me->end_command); /* Tell user what's happening */
196: system(me->end_command); /* @@ Beware of security hole */
197: free (me->end_command);
198: if (me->remove_on_close) {
199: unlink(me->filename);
200: }
201: }
202: if (me->callback) {
203: (*me->callback)(me->request, me->filename);
204: }
205: if (me->filename) free(me->filename);
206: free(me);
207: return HT_OK;
208: }
209:
210: PRIVATE int HTFWriter_abort ARGS2(HTStream *, me, HTError, e)
211: {
212: if (me->leave_open != YES) fclose(me->fp);
213: if (me->end_command) { /* Temp file */
214: if (TRACE)
215: fprintf(TDEST,"FileWriter.. Aborting: file %s not executed.\n",
216: me->filename ? me->filename : "???" );
217: free (me->end_command);
218: if (me->remove_on_close) {
219: unlink(me->filename);
220: }
221: }
222:
223: if (me->filename) free(me->filename);
224: free(me);
225: return HT_ERROR;
226: }
227:
228: PRIVATE CONST HTStreamClass HTFWriter = /* As opposed to print etc */
229: {
230: "FileWriter",
231: HTFWriter_flush,
232: HTFWriter_free,
233: HTFWriter_abort,
234: HTFWriter_put_character,
235: HTFWriter_put_string,
236: HTFWriter_write
237: };
238:
239: PUBLIC HTStream* HTFWriter_new ARGS2(FILE *, fp, BOOL, leave_open)
240: {
241: HTStream* me;
242:
243: if (!fp) {
244: if (TRACE)
245: fprintf(TDEST, "FileWriter.. Bad file descriptor\n");
246: return NULL;
247: }
248: me = (HTStream*)calloc(sizeof(*me),1);
249: if (me == NULL) outofmem(__FILE__, "HTFWriter_new");
250: me->isa = &HTFWriter;
251:
252: me->fp = fp;
253: me->leave_open = leave_open; /* HENRIK 08/02-94 */
254: me->end_command = NULL;
255: me->remove_on_close = NO;
256: me->announce = NO;
257: me->callback = NULL;
258: return me;
259: }
260:
261: /* ------------------------------------------------------------------------- */
262: /* CACHE MANAGER */
263: /* ------------------------------------------------------------------------- */
2.1 frystyk 264:
265: /*
266: ** Check that the name we're about to generate doesn't
267: ** clash with anything used by the caching system.
268: */
269: PRIVATE BOOL reserved_name ARGS1(char *, url)
270: {
271: char * name = strrchr(url, '/');
272: char * suff = NULL;
273:
274: if (name) name++;
275: else name = url;
276:
277: if (!strcmp(name, CACHE_INFO) ||
278: !strcmp(name, INDEX_FILE) ||
279: !strcmp(name, WELCOME_FILE))
280: return YES;
281:
282: suff = strrchr(name, TMP_SUFFIX[0]);
283: if (suff && !strcmp(suff, TMP_SUFFIX))
284: return YES;
285:
286: suff = strrchr(name, LOCK_SUFFIX[0]);
287: if (suff && !strcmp(suff, LOCK_SUFFIX))
288: return YES;
289:
290: return NO;
291: }
292:
293:
294: /*
295: ** Map url to cache file name.
296: */
2.4 frystyk 297: PRIVATE char * cache_file_name ARGS1(char *, url)
2.1 frystyk 298: {
299: char * access = NULL;
300: char * host = NULL;
301: char * path = NULL;
302: char * cfn = NULL;
303: BOOL welcome = NO;
304: BOOL res = NO;
305:
306: if (!url || strchr(url, '?') || (res = reserved_name(url)) ||
307: !(access = HTParse(url, "", PARSE_ACCESS)) ||
308: (0 != strcmp(access, "http") &&
309: 0 != strcmp(access, "ftp") &&
310: 0 != strcmp(access, "gopher"))) {
311:
312: if (access) free(access);
313:
2.3 frystyk 314: if (res && CACHE_TRACE)
2.1 frystyk 315: fprintf(TDEST,
316: "Cache....... Clash with reserved name (\"%s\")\n",url);
317:
318: return NULL;
319: }
320:
321: host = HTParse(url, "", PARSE_HOST);
322: path = HTParse(url, "", PARSE_PATH | PARSE_PUNCTUATION);
323: if (path && path[strlen(path)-1] == '/')
324: welcome = YES;
325:
2.4 frystyk 326: cfn = (char*)malloc(strlen(HTCacheRoot) +
2.1 frystyk 327: strlen(access) +
328: (host ? strlen(host) : 0) +
329: (path ? strlen(path) : 0) +
330: (welcome ? strlen(WELCOME_FILE) : 0) + 3);
331: if (!cfn) outofmem(__FILE__, "cache_file_name");
2.3 frystyk 332:
333: /* Removed extra slash - HF May2,95 */
2.4 frystyk 334: sprintf(cfn, "%s%s/%s%s%s", HTCacheRoot, access, host, path,
2.1 frystyk 335: (welcome ? WELCOME_FILE : ""));
336:
337: FREE(access); FREE(host); FREE(path);
338:
339: /*
340: ** This checks that the last component is not too long.
341: ** It could check all the components, but the last one
342: ** is most important because it could later blow up the
343: ** whole gc when reading cache info files.
344: ** Operating system handles other cases.
345: ** 64 = 42 + 22 and 22 = 42 - 20 :-)
346: ** In other words I just picked some number, it doesn't
347: ** really matter that much.
348: */
349: {
350: char * last = strrchr(cfn, '/');
351: if (!last) last = cfn;
352: if ((int)strlen(last) > 64) {
2.3 frystyk 353: if (CACHE_TRACE)
2.1 frystyk 354: fprintf(TDEST, "Too long.... cache file name \"%s\"\n", cfn);
355: free(cfn);
356: cfn = NULL;
357: }
358: }
359: return cfn;
360: }
361:
362:
363: /*
364: ** Create directory path for cache file
365: **
366: ** On exit:
367: ** return YES
368: ** if directories created -- after that caller
369: ** can rely on fopen(cfn,"w") succeeding.
370: **
371: */
2.4 frystyk 372: PRIVATE BOOL create_cache_place ARGS1(char *, cfn)
2.1 frystyk 373: {
374: struct stat stat_info;
375: char * cur = NULL;
376: BOOL create = NO;
377:
2.4 frystyk 378: if (!cfn || (int)strlen(cfn) <= (int)strlen(HTCacheRoot) + 1)
2.1 frystyk 379: return NO;
380:
2.4 frystyk 381: cur = cfn + strlen(HTCacheRoot) + 1;
2.1 frystyk 382:
383: while ((cur = strchr(cur, '/'))) {
384: *cur = 0;
385: if (create || HTStat(cfn, &stat_info) == -1) {
386: create = YES; /* To avoid doing stat()s in vain */
2.3 frystyk 387: if (CACHE_TRACE)
2.1 frystyk 388: fprintf(TDEST,"Cache....... creating cache dir \"%s\"\n",cfn);
2.3 frystyk 389: if (MKDIR(cfn, 0777) < 0) {
390: if (CACHE_TRACE)
391: fprintf(TDEST,"Cache....... can't create dir `%s\'\n",cfn);
2.1 frystyk 392: return NO;
393: }
2.3 frystyk 394: } else {
2.1 frystyk 395: if (S_ISREG(stat_info.st_mode)) {
396: int len = strlen(cfn);
397: char * tmp1 = (char*)malloc(len + strlen(TMP_SUFFIX) + 1);
398: char * tmp2 = (char*)malloc(len + strlen(INDEX_FILE) + 2);
399: /* time_t t1,t2,t3,t4,t5; */
400:
401:
402: sprintf(tmp1, "%s%s", cfn, TMP_SUFFIX);
403: sprintf(tmp2, "%s/%s", cfn, INDEX_FILE);
404:
2.3 frystyk 405: if (CACHE_TRACE) {
2.1 frystyk 406: fprintf(TDEST,"Cache....... moving \"%s\" to \"%s\"\n",
407: cfn,tmp1);
408: fprintf(TDEST,"and......... creating dir \"%s\"\n",
409: cfn);
410: fprintf(TDEST,"and......... moving \"%s\" to \"%s\"\n",
411: tmp1,tmp2);
412: }
413: rename(cfn,tmp1);
2.3 frystyk 414: (void) MKDIR(cfn, 0777);
2.1 frystyk 415: rename(tmp1,tmp2);
416:
417: /* if (HTCacheInfo_for(cfn,&t1,&t2,&t3,&t4,&t5)) {
2.3 frystyk 418: if (CACHE_TRACE)
2.1 frystyk 419: fprintf(TDEST,"Adding...... info entry for %s\n",tmp2);
420: if (!HTCacheInfo_writeEntryFor(tmp2,t1,t2,t3,t4,t5))
421: HTLog_error2("Can't write cache info entry for",tmp2);
422: }
423: */
424: free(tmp1);
425: free(tmp2);
426: }
427: else {
2.3 frystyk 428: if (CACHE_TRACE)
2.1 frystyk 429: fprintf(TDEST,"Cache....... dir \"%s\" already exists\n",
430: cfn);
431: }
432: }
433: *cur = '/';
434: cur++;
435: }
436: return YES;
437: }
438:
439:
2.4 frystyk 440: /* Create a cache path
441: ** -------------------
442: ** Find a full path name for the cache file and create the path if it
443: ** does not already exist. Returns name or NULL
444: ** HWL 22/9/94
445: ** HWL added support for hierachical structure
446: */
447: PRIVATE char *HTCache_getName ARGS1(char *, url)
448: {
449: char *filename = cache_file_name(url);
450: if (!filename)
451: return NULL;
452: if (create_cache_place(filename))
453: return(filename);
454: return NULL;
2.1 frystyk 455: }
456:
457:
2.3 frystyk 458: PRIVATE void HTCache_remove ARGS2(HTList *, list, HTCacheItem *, item)
459: {
460: if (CACHE_TRACE)
461: fprintf(TDEST, "Cache: Removing %s\n", item->filename);
462: HTList_removeObject(list, item);
463: HTList_removeObject(item->anchor->cacheItems, item);
464: unlink(item->filename);
465:
466: /* HWL 22/9/94: since we added a hierachical file structure, we should also clean up */
467: {
468: char * p;
2.1 frystyk 469:
2.3 frystyk 470: while ((p = strrchr(item->filename,'/')) && (p != NULL)){
471: item->filename[p - item->filename] = 0; /* this will be freed in a sec */
472: if (strcmp(item->filename, HTCacheRoot) != 0) {
473: if (CACHE_TRACE)
474: fprintf(TDEST, "rmdir %s\n", item->filename);
475: rmdir(item->filename); /* this will luckily fail if directory is not empty */
476: }
477: }
478: }
479:
480: free(item->filename);
481: free(item);
482: }
483:
484: /*
485: ** This can be called for the main list or an anchor's list
2.1 frystyk 486: */
2.3 frystyk 487: PUBLIC void HTCacheClear ARGS1(HTList *, list)
2.1 frystyk 488: {
2.3 frystyk 489: HTCacheItem * item;
490: while ((item = (HTCacheItem *) HTList_objectAt(list, 0)) != NULL) {
491: HTCache_remove(list, item);
492: }
2.1 frystyk 493: }
494:
495:
2.3 frystyk 496: /*
497: ** This function removes ALL cache files known to this session. The anchors
498: ** ARE updated, but normally this function is for exiting purposes.
499: */
500: PUBLIC void HTCacheDeleteAll NOARGS
501: {
502: HTCacheItem * item;
503: while ((item = (HTCacheItem *) HTList_objectAt(HTCache, 0)) != NULL) {
504: HTCache_remove(HTCache, item);
505: }
506: }
2.1 frystyk 507:
2.3 frystyk 508: /* Remove a file from the cache to prevent too many files from being cached
2.1 frystyk 509: */
2.3 frystyk 510: PRIVATE void limit_cache ARGS1(HTList * , list)
2.1 frystyk 511: {
2.3 frystyk 512: HTList * cur = list;
513: HTCacheItem * item;
514: time_t best_delay = 0; /* time_t in principle can be any arith type */
515: HTCacheItem* best_item = NULL;
516:
517: if (HTList_count(list) < HTCacheLimit) return; /* Limit not reached */
518:
519: while (NULL != (item = (HTCacheItem*)HTList_nextObject(cur))) {
520: if (best_delay == 0 || item->load_delay < best_delay) {
521: best_delay = item->load_delay;
522: best_item = item;
523: }
524: }
525:
526: if (best_item) HTCache_remove(list, best_item);
2.1 frystyk 527: }
528:
2.3 frystyk 529: /* Enable Cache
2.1 frystyk 530: ** ------------
2.3 frystyk 531: ** If `cache_root' is NULL then reuse old value or use HT_CACHE_ROOT.
532: ** An empty string will make '/' as cache root
2.1 frystyk 533: */
2.3 frystyk 534: PUBLIC BOOL HTCache_enable ARGS1(CONST char *, cache_root)
2.1 frystyk 535: {
2.3 frystyk 536: if (cache_root)
537: HTCache_setRoot(cache_root);
538: HTCacheEnable = YES;
539: return YES;
2.1 frystyk 540: }
541:
542:
2.3 frystyk 543: /* Disable Cache
544: ** ------------
545: ** Turns off the cache. Note that the cache can be disabled and enabled
546: ** at any time. The cache root is kept and can be reused during the
547: ** execution.
2.1 frystyk 548: */
2.3 frystyk 549: PUBLIC BOOL HTCache_disable NOARGS
2.1 frystyk 550: {
2.3 frystyk 551: HTCacheEnable = NO;
552: return YES;
553: }
2.1 frystyk 554:
2.3 frystyk 555: /* Is Cache Enabled
556: ** ----------------
557: ** Returns YES or NO. Also makes sure that we have a root value
558: ** (even though it might be invalid)
559: */
560: PUBLIC BOOL HTCache_isEnabled NOARGS
561: {
562: if (HTCacheEnable) {
563: if (!HTCacheRoot)
564: HTCache_setRoot(NULL);
565: return YES;
2.1 frystyk 566: }
2.3 frystyk 567: return NO;
2.1 frystyk 568: }
569:
2.3 frystyk 570:
571: /* Set Cache Root
572: ** --------------
573: ** If `cache_root' is NULL then the current value (might be a define)
574: ** Should we check if the cache_root is actually OK? I think not!
2.1 frystyk 575: */
2.3 frystyk 576: PUBLIC BOOL HTCache_setRoot ARGS1(CONST char *, cache_root)
577: {
578: StrAllocCopy(HTCacheRoot, cache_root ? cache_root : HT_CACHE_ROOT);
579: if (*(HTCacheRoot+strlen(HTCacheRoot)-1) != '/')
580: StrAllocCat(HTCacheRoot, "/");
581: if (CACHE_TRACE)
582: fprintf(TDEST, "Cache Root.. Root set to `%s\'\n", HTCacheRoot);
583: return YES;
584: }
2.1 frystyk 585:
2.3 frystyk 586:
587: /* Get Cache Root
588: ** --------------
589: */
590: PUBLIC CONST char * HTCache_getRoot NOARGS
2.1 frystyk 591: {
2.3 frystyk 592: return HTCacheRoot;
2.1 frystyk 593: }
594:
595:
2.3 frystyk 596: /* Free Cache Root
597: ** --------------
598: ** For clean up memory
2.1 frystyk 599: */
2.3 frystyk 600: PUBLIC void HTCache_freeRoot NOARGS
601: {
602: FREE(HTCacheRoot);
603: }
2.1 frystyk 604:
605:
2.3 frystyk 606: /* Cache Writer
607: ** ------------------
608: **
2.1 frystyk 609: */
2.3 frystyk 610: PUBLIC HTStream* HTCacheWriter ARGS5(
611: HTRequest *, request,
612: void *, param,
613: HTFormat, input_format,
614: HTFormat, output_format,
615: HTStream *, output_stream)
616:
2.1 frystyk 617: {
2.3 frystyk 618: char *fnam;
2.1 frystyk 619: HTStream* me;
620:
2.3 frystyk 621: if (HTClientHost) {
622: if (CACHE_TRACE)
623: fprintf(TDEST, "Only caching if WWW is run locally.\n");
624: return HTBlackHole();
625: }
2.4 frystyk 626:
627: /* Get a file name */
628: if ((fnam = HTCache_getName(HTAnchor_physical(request->anchor))) == NULL)
629: return HTBlackHole();
630:
2.1 frystyk 631: me = (HTStream*)calloc(sizeof(*me),1);
2.3 frystyk 632: if (me == NULL) outofmem(__FILE__, "CacheWriter");
633: me->isa = &HTFWriter;
2.1 frystyk 634: me->end_command = NULL;
2.3 frystyk 635: me->remove_on_close = NO; /* If needed, put into end_command */
2.1 frystyk 636: me->announce = NO;
2.3 frystyk 637: me->filename = NULL;
2.4 frystyk 638: if ((me->fp = fopen (fnam, "w")) == NULL) {
2.3 frystyk 639: HTAlert("Can't open local file for writing.");
640: if (CACHE_TRACE)
641: fprintf(TDEST, "HTStream: can't open %s for writing\n",fnam);
642: free(fnam);
643: free(me);
644: return HTBlackHole();
645: }
646:
647: /* Set up a cache record */
648: if (CACHE_TRACE)
649: fprintf(TDEST, "Cache....... Creating file %s\n", fnam);
2.4 frystyk 650: if ((me->cache = (HTCacheItem*)calloc(sizeof(*me->cache),1)) == NULL)
651: outofmem(__FILE__, "cache");
2.3 frystyk 652: time(&me->cache->load_time);
653: StrAllocCopy(me->cache->filename, fnam);
2.4 frystyk 654: me->cache->format = input_format;
655: me->callback = request->callback;
656: me->request = request; /* won't be freed */
657: me->filename = fnam; /* will be freed */
658:
659: /* Make binding to he anchor structure */
2.3 frystyk 660: me->cache->anchor = request->anchor;
661: if (!request->anchor->cacheItems)
662: request->anchor->cacheItems = HTList_new();
663: HTList_addObject(request->anchor->cacheItems, me->cache);
2.4 frystyk 664:
665: /* Keep a global list of all cache items */
2.3 frystyk 666: if (!HTCache) HTCache = HTList_new();
667: HTList_addObject(HTCache, me->cache);
2.4 frystyk 668: limit_cache(HTCache); /* Limit number (not size) of files */
2.3 frystyk 669:
2.1 frystyk 670: return me;
671: }
672:
2.3 frystyk 673: /* ------------------------------------------------------------------------- */
674: /* FILE WRITER ROUTINES */
675: /* ------------------------------------------------------------------------- */
676:
677: /* Set TMP Root
678: ** --------------
679: ** If `tmp_root' is NULL use the current value (might be a define)
680: */
681: PUBLIC BOOL HTTmp_setRoot ARGS1(CONST char *, tmp_root)
682: {
683: StrAllocCopy(HTTmpRoot, tmp_root ? tmp_root : HT_TMP_ROOT);
684: if (*(HTTmpRoot+strlen(HTTmpRoot)-1) != '/')
685: StrAllocCat(HTTmpRoot, "/");
686: if (TRACE)
687: fprintf(TDEST, "Tmp Root.... Root set to `%s\'\n", HTTmpRoot);
688: return YES;
689: }
690:
691:
692: /* Get Tmp Root
693: ** --------------
2.1 frystyk 694: */
2.3 frystyk 695: PUBLIC CONST char * HTTmp_getRoot NOARGS
696: {
697: return HTTmpRoot;
698: }
2.1 frystyk 699:
700:
2.3 frystyk 701: /* Free Tmp Root
702: ** --------------
703: ** For clean up memory
704: */
705: PUBLIC void HTTmp_freeRoot NOARGS
706: {
707: FREE(HTTmpRoot);
708: }
2.1 frystyk 709:
2.4 frystyk 710: /*
711: ** This function tries really hard to find a non-existent filename relative
712: ** to the path given. Returns a string that must be freed by the caller or
713: ** NULL on error. The base must be '/' terminated which!
714: */
715: PRIVATE char *get_filename ARGS3(char *, base, CONST char *, url,
716: CONST char *, suffix)
717: {
718: char *path=NULL;
719: char filename[40];
720: int hash=0;
721:
722: /* Do this until we find a name that doesn't exist */
723: while (1)
724: {
725: CONST char *ptr=url;
726: for( ; *ptr; ptr++)
727: hash = (int) ((hash * 31 + (* (unsigned char *) ptr)) % HASH_SIZE);
728:
729: #ifndef NO_GETPID
730: sprintf(filename, "%d-%d", hash, (int) getpid());
731: #else
732: sprintf(filename, "%d-%d", hash, time(NULL));
733: #endif
734: StrAllocCopy(path, base);
735: StrAllocCat(path, filename);
736: if (suffix) StrAllocCat(path, suffix);
737:
738: {
739: FILE *fp = fopen(path, "r");
740: if (fp) /* This file does already exist */
741: fclose(fp);
742: else
743: break; /* Got the file name */
744: }
745: }
746: return path;
747: }
748:
749:
2.1 frystyk 750: /* Take action using a system command
751: ** ----------------------------------
752: **
753: ** Creates temporary file, writes to it, executes system command
754: ** on end-document. The suffix of the temp file can be given
755: ** in case the application is fussy, or so that a generic opener can
756: ** be used.
757: */
758: PUBLIC HTStream* HTSaveAndExecute ARGS5(
759: HTRequest *, request,
760: void *, param,
761: HTFormat, input_format,
762: HTFormat, output_format,
763: HTStream *, output_stream)
764:
765: #ifdef REMOVE_FILE
766: {
767: char *fnam;
768: HTStream* me;
769:
770: if (HTSecure) {
771: HTAlert("Can't save data to file -- please run WWW locally");
772: return HTBlackHole();
773: }
774:
2.3 frystyk 775: if (!HTTmpRoot) {
2.1 frystyk 776: if (TRACE) fprintf(TDEST, "Save and execute turned off");
777: return HTBlackHole();
778: }
779:
2.4 frystyk 780: /* Let's find a hash name for this file */
781: {
2.5 frystyk 782: char *suffix = HTBind_getSuffix(request->anchor);
2.4 frystyk 783: fnam = get_filename(HTTmpRoot, HTAnchor_physical(request->anchor),
784: suffix);
2.5 frystyk 785: FREE(suffix);
2.4 frystyk 786: if (!fnam) {
787: HTAlert("Can't find a suitable file name");
788: return HTBlackHole();
789: }
790: }
791:
2.1 frystyk 792: me = (HTStream*)calloc(sizeof(*me), 1);
793: if (me == NULL) outofmem(__FILE__, "Save and execute");
794: me->isa = &HTFWriter;
795: me->request = request; /* won't be freed */
796: me->fp = fopen (fnam, "w");
797: if (!me->fp) {
798: HTAlert("Can't open temporary file!");
799: free(fnam);
800: free(me);
2.4 frystyk 801: return HTBlackHole();
2.1 frystyk 802: }
803: StrAllocCopy(me->filename, fnam);
804:
2.4 frystyk 805: /* Make command to process file */
2.1 frystyk 806: me->end_command = (char *) malloc ((strlen((char *) param) + 10 +
807: 3*strlen(fnam)) * sizeof (char));
808: if (me == NULL) outofmem(__FILE__, "SaveAndExecute");
809:
810: sprintf (me->end_command, (char *) param, fnam, fnam, fnam);
811: me->remove_on_close = NO;
812: me->announce = NO;
813: free (fnam);
814: return me;
815: }
816:
817: #else /* can't do remove */
2.4 frystyk 818: {
819: return HTBlackHole();
820: }
2.1 frystyk 821: #endif
822:
823:
824: /* Save Locally
825: ** ------------
826: **
827: ** Bugs:
828: ** GUI Apps should open local Save panel here really.
829: **
830: */
831: PUBLIC HTStream* HTSaveLocally ARGS5(
832: HTRequest *, request,
833: void *, param,
834: HTFormat, input_format,
835: HTFormat, output_format,
836: HTStream *, output_stream) /* Not used */
837:
838: {
839: char *fnam = NULL;
840: char *answer = NULL;
841: HTStream* me;
842:
843: if (HTClientHost) {
844: HTAlert("Can't save data to file -- please run WWW locally");
845: return HTBlackHole();
846: }
847:
2.3 frystyk 848: if (!HTTmpRoot) {
2.1 frystyk 849: if (TRACE) fprintf(TDEST, "Save locally turned off");
850: return HTBlackHole();
851: }
852:
2.4 frystyk 853: /* Let's find a file name for this file */
854: {
2.5 frystyk 855: char *suffix = HTBind_getSuffix(request->anchor);
2.4 frystyk 856: fnam = get_filename(HTTmpRoot, HTAnchor_physical(request->anchor),
857: suffix);
858: answer = HTPrompt("Give name of file to save in", fnam ? fnam : "");
859: if (!answer) {
860: FREE(fnam);
861: return HTBlackHole();
862: }
2.5 frystyk 863: FREE(suffix);
2.4 frystyk 864: FREE(fnam);
865: }
866:
2.1 frystyk 867: me = (HTStream*)calloc(sizeof(*me),1);
868: if (me == NULL) outofmem(__FILE__, "SaveLocally");
869: me->isa = &HTFWriter;
870: me->announce = YES;
871:
872: me->fp = fopen (answer, "w");
873: if (!me->fp) {
874: HTAlert("Can't open local file to write into.");
875: FREE(answer);
876: free(me);
2.4 frystyk 877: return HTBlackHole();
2.1 frystyk 878: }
879: me->callback = NULL;
880: me->request = request; /* won't be freed */
881: me->filename = answer; /* Will be freed */
882: return me;
883: }
884:
885:
886: /* Save and Call Back
887: ** ------------------
888: **
889: **
890: ** The special case is a kludge. Better is everything uses streams
891: ** and nothing uses files. Then this routine will go too. :-))
892: */
893: PUBLIC HTStream* HTSaveAndCallBack ARGS5(
894: HTRequest *, request,
895: void *, param,
896: HTFormat, input_format,
897: HTFormat, output_format,
898: HTStream *, output_stream)
899: {
900: HTStream * me;
901:
902: if (request->using_cache) { /* Special case! file wanted && cache hit */
903: (*request->callback)(request,
904: ((HTCacheItem*)request->using_cache)->filename);
2.3 frystyk 905: return HTBlackHole();
2.1 frystyk 906: } else {
907: me = HTCacheWriter(request, param,
908: input_format, output_format, output_stream);
909: if (me) {
910: me->callback = request->callback;
911: }
912: }
913: return me;
914: }
915:
Webmaster