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