Annotation of libwww/Library/src/HTFWrite.c, revision 2.6.2.1
2.6.2.1 ! cbrooks 1: /* HTFWrite.c
! 2: ** FILE WRITER
! 3: **
! 4: ** (c) COPYRIGHT MIT 1995.
! 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"
! 26: #include "HTBind.h"
! 27: #include "HTList.h"
! 28: #include "HTParse.h"
! 29: #include "HTFWrite.h" /* Implemented here */
! 30:
! 31: #define HASH_SIZE 1001 /* Tunable */
! 32:
! 33: #define CACHE_LIMIT 100 /* files */
! 34:
! 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"
! 40:
! 41: PUBLIC int HTCacheLimit = CACHE_LIMIT;
! 42:
! 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: };
! 56:
! 57: PRIVATE HTStream HTBlackHoleInstance; /* Made static */
! 58:
! 59: PRIVATE BOOL HTCacheEnable = NO;
! 60: PRIVATE char * HTCacheRoot = NULL; /* Destination for cache */
! 61: PRIVATE HTList * HTCache = NULL; /* List of cached elements */
! 62:
! 63: PRIVATE char * HTTmpRoot = NULL; /* Dest for tmp files */
! 64:
! 65: /* ------------------------------------------------------------------------- */
! 66: /* BASIC STREAM CLASSES */
! 67: /* ------------------------------------------------------------------------- */
! 68:
! 69: /*
! 70: **
! 71: ** B L A C K H O L E C L A S S
! 72: **
! 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: ** -------------
! 133: **
! 134: ** This function is a dummy function that returns the same output stream
! 135: ** as given as a parameter. Henrik 01/03-94
! 136: */
! 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: }
! 146:
! 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_flush ARGS1(HTStream *, me)
! 164: {
! 165: return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
! 166: }
! 167:
! 168:
! 169: PRIVATE int HTFWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l)
! 170: {
! 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:
! 179: }
! 180:
! 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: /* ------------------------------------------------------------------------- */
! 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: */
! 297: PRIVATE char * cache_file_name ARGS1(char *, url)
! 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:
! 314: if (res && CACHE_TRACE)
! 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:
! 326: cfn = (char*)malloc(strlen(HTCacheRoot) +
! 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");
! 332:
! 333: /* Removed extra slash - HF May2,95 */
! 334: sprintf(cfn, "%s%s/%s%s%s", HTCacheRoot, access, host, path,
! 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) {
! 353: if (CACHE_TRACE)
! 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: */
! 372: PRIVATE BOOL create_cache_place ARGS1(char *, cfn)
! 373: {
! 374: struct stat stat_info;
! 375: char * cur = NULL;
! 376: BOOL create = NO;
! 377:
! 378: if (!cfn || (int)strlen(cfn) <= (int)strlen(HTCacheRoot) + 1)
! 379: return NO;
! 380:
! 381: cur = cfn + strlen(HTCacheRoot) + 1;
! 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 */
! 387: if (CACHE_TRACE)
! 388: fprintf(TDEST,"Cache....... creating cache dir \"%s\"\n",cfn);
! 389: if (MKDIR(cfn, 0777) < 0) {
! 390: if (CACHE_TRACE)
! 391: fprintf(TDEST,"Cache....... can't create dir `%s\'\n",cfn);
! 392: return NO;
! 393: }
! 394: } else {
! 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:
! 405: if (CACHE_TRACE) {
! 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);
! 414: (void) MKDIR(cfn, 0777);
! 415: rename(tmp1,tmp2);
! 416:
! 417: /* if (HTCacheInfo_for(cfn,&t1,&t2,&t3,&t4,&t5)) {
! 418: if (CACHE_TRACE)
! 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 {
! 428: if (CACHE_TRACE)
! 429: fprintf(TDEST,"Cache....... dir \"%s\" already exists\n",
! 430: cfn);
! 431: }
! 432: }
! 433: *cur = '/';
! 434: cur++;
! 435: }
! 436: return YES;
! 437: }
! 438:
! 439:
! 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;
! 455: }
! 456:
! 457:
! 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;
! 469:
! 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
! 486: */
! 487: PUBLIC void HTCacheClear ARGS1(HTList *, list)
! 488: {
! 489: HTCacheItem * item;
! 490: while ((item = (HTCacheItem *) HTList_objectAt(list, 0)) != NULL) {
! 491: HTCache_remove(list, item);
! 492: }
! 493: }
! 494:
! 495:
! 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: }
! 507:
! 508: /* Remove a file from the cache to prevent too many files from being cached
! 509: */
! 510: PRIVATE void limit_cache ARGS1(HTList * , list)
! 511: {
! 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);
! 527: }
! 528:
! 529: /* Enable Cache
! 530: ** ------------
! 531: ** If `cache_root' is NULL then reuse old value or use HT_CACHE_ROOT.
! 532: ** An empty string will make '/' as cache root
! 533: */
! 534: PUBLIC BOOL HTCache_enable ARGS1(CONST char *, cache_root)
! 535: {
! 536: if (cache_root)
! 537: HTCache_setRoot(cache_root);
! 538: HTCacheEnable = YES;
! 539: return YES;
! 540: }
! 541:
! 542:
! 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.
! 548: */
! 549: PUBLIC BOOL HTCache_disable NOARGS
! 550: {
! 551: HTCacheEnable = NO;
! 552: return YES;
! 553: }
! 554:
! 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;
! 566: }
! 567: return NO;
! 568: }
! 569:
! 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!
! 575: */
! 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: }
! 585:
! 586:
! 587: /* Get Cache Root
! 588: ** --------------
! 589: */
! 590: PUBLIC CONST char * HTCache_getRoot NOARGS
! 591: {
! 592: return HTCacheRoot;
! 593: }
! 594:
! 595:
! 596: /* Free Cache Root
! 597: ** --------------
! 598: ** For clean up memory
! 599: */
! 600: PUBLIC void HTCache_freeRoot NOARGS
! 601: {
! 602: FREE(HTCacheRoot);
! 603: }
! 604:
! 605:
! 606: /* Cache Writer
! 607: ** ------------------
! 608: **
! 609: */
! 610: PUBLIC HTStream* HTCacheWriter ARGS5(
! 611: HTRequest *, request,
! 612: void *, param,
! 613: HTFormat, input_format,
! 614: HTFormat, output_format,
! 615: HTStream *, output_stream)
! 616:
! 617: {
! 618: char *fnam;
! 619: HTStream* me;
! 620:
! 621: if (HTClientHost) {
! 622: if (CACHE_TRACE)
! 623: fprintf(TDEST, "Only caching if WWW is run locally.\n");
! 624: return HTBlackHole();
! 625: }
! 626:
! 627: /* Get a file name */
! 628: if ((fnam = HTCache_getName(HTAnchor_physical(request->anchor))) == NULL)
! 629: return HTBlackHole();
! 630:
! 631: me = (HTStream*)calloc(sizeof(*me),1);
! 632: if (me == NULL) outofmem(__FILE__, "CacheWriter");
! 633: me->isa = &HTFWriter;
! 634: me->end_command = NULL;
! 635: me->remove_on_close = NO; /* If needed, put into end_command */
! 636: me->announce = NO;
! 637: me->filename = NULL;
! 638: if ((me->fp = fopen (fnam, "w")) == NULL) {
! 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);
! 650: if ((me->cache = (HTCacheItem*)calloc(sizeof(*me->cache),1)) == NULL)
! 651: outofmem(__FILE__, "cache");
! 652: time(&me->cache->load_time);
! 653: StrAllocCopy(me->cache->filename, fnam);
! 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 */
! 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);
! 664:
! 665: /* Keep a global list of all cache items */
! 666: if (!HTCache) HTCache = HTList_new();
! 667: HTList_addObject(HTCache, me->cache);
! 668: limit_cache(HTCache); /* Limit number (not size) of files */
! 669:
! 670: return me;
! 671: }
! 672:
! 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: ** --------------
! 694: */
! 695: PUBLIC CONST char * HTTmp_getRoot NOARGS
! 696: {
! 697: return HTTmpRoot;
! 698: }
! 699:
! 700:
! 701: /* Free Tmp Root
! 702: ** --------------
! 703: ** For clean up memory
! 704: */
! 705: PUBLIC void HTTmp_freeRoot NOARGS
! 706: {
! 707: FREE(HTTmpRoot);
! 708: }
! 709:
! 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:
! 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:
! 775: if (!HTTmpRoot) {
! 776: if (TRACE) fprintf(TDEST, "Save and execute turned off");
! 777: return HTBlackHole();
! 778: }
! 779:
! 780: /* Let's find a hash name for this file */
! 781: {
! 782: char *suffix = HTBind_getSuffix(request->anchor);
! 783: fnam = get_filename(HTTmpRoot, HTAnchor_physical(request->anchor),
! 784: suffix);
! 785: FREE(suffix);
! 786: if (!fnam) {
! 787: HTAlert("Can't find a suitable file name");
! 788: return HTBlackHole();
! 789: }
! 790: }
! 791:
! 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);
! 801: return HTBlackHole();
! 802: }
! 803: StrAllocCopy(me->filename, fnam);
! 804:
! 805: /* Make command to process file */
! 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 */
! 818: {
! 819: return HTBlackHole();
! 820: }
! 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:
! 848: if (!HTTmpRoot) {
! 849: if (TRACE) fprintf(TDEST, "Save locally turned off");
! 850: return HTBlackHole();
! 851: }
! 852:
! 853: /* Let's find a file name for this file */
! 854: {
! 855: char *suffix = HTBind_getSuffix(request->anchor);
! 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: }
! 863: FREE(suffix);
! 864: FREE(fnam);
! 865: }
! 866:
! 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);
! 877: return HTBlackHole();
! 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);
! 905: return HTBlackHole();
! 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