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