Annotation of libwww/Library/src/HTFile.c, revision 1.80

1.72      frystyk     1: /*                                                                    HTFile.c
                      2: **     FILE ACCESS
                      3: **
1.79      frystyk     4: **     (c) COPYRIGHT MIT 1995.
1.72      frystyk     5: **     Please first read the full copyright statement in the file COPYRIGH.
1.1       timbl       6: **
                      7: **     This is unix-specific code in general, with some VMS bits.
1.8       timbl       8: **     These are routines for file access used by browsers.
1.1       timbl       9: **
                     10: ** History:
                     11: **        Feb 91       Written Tim Berners-Lee CERN/CN
                     12: **        Apr 91       vms-vms access included using DECnet syntax
                     13: **     26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
                     14: **                     Fixed access bug for relative names on VMS.
1.28      duns       15: **        Sep 93 (MD)  Access to VMS files allows sharing.
                     16: **     15 Nov 93 (MD)  Moved HTVMSname to HTVMSUTILS.C
1.52      duns       17: **     22 Feb 94 (MD)  Excluded two routines if we are not READING directories
1.61      frystyk    18: **     18 May 94 (HF)  Directory stuff removed and stream handling updated,
                     19: **                     error messages introduced etc.
1.1       timbl      20: **
                     21: ** Bugs:
1.2       timbl      22: **     FTP: Cannot access VMS files from a unix machine.
                     23: **      How can we know that the
1.1       timbl      24: **     target machine runs VMS?
                     25: */
                     26: 
1.67      frystyk    27: /* Library Includes */
1.75      frystyk    28: #include "tcp.h"
1.67      frystyk    29: #include "HTUtils.h"
1.75      frystyk    30: #include "HTString.h"
1.1       timbl      31: #include "HTParse.h"
                     32: #include "HTTCP.h"
                     33: #include "HTAnchor.h"
1.2       timbl      34: #include "HTAtom.h"
                     35: #include "HTWriter.h"
1.75      frystyk    36: #include "HTFWrite.h"
1.52      duns       37: #include "HTFormat.h"
1.54      luotonen   38: #include "HTMulti.h"
1.78      frystyk    39: #include "HTDirBrw.h"
                     40: #include "HTBind.h"
1.80    ! frystyk    41: #include "HTSocket.h"
        !            42: #include "HTThread.h"
1.61      frystyk    43: #include "HTError.h"
1.67      frystyk    44: #include "HTFile.h"            /* Implemented here */
                     45: 
1.80    ! frystyk    46: /* This is the local definition of HTRequest->net_info */
        !            47: typedef enum _FileState {
        !            48:     FILE_RETRY         = -4,
        !            49:     FILE_ERROR         = -3,
        !            50:     FILE_NO_DATA       = -2,
        !            51:     FILE_GOT_DATA      = -1,
        !            52:     FILE_BEGIN         = 0,
        !            53:     FILE_NEED_OPEN_FILE,
        !            54:     FILE_NEED_BODY,
        !            55:     FILE_PARSE_DIR,
        !            56:     FILE_TRY_FTP
        !            57: } FileState;
        !            58: 
1.77      frystyk    59: typedef struct _file_info {
                     60:     SOCKFD             sockfd;                         /* Socket descripter */
                     61:     SockA              sock_addr;              /* SockA is defined in tcp.h */
                     62:     HTInputSocket *    isoc;                                /* Input buffer */
                     63:     SocAction          action;                 /* Result of the select call */
                     64:     HTStream *         target;                             /* Target stream */
                     65:     int                addressCount;        /* Attempts if multi-homed host */
                     66:     time_t             connecttime;             /* Used on multihomed hosts */
                     67:     struct _HTRequest *        request;           /* Link back to request structure */
1.80    ! frystyk    68: 
        !            69:     FileState          state;            /* Current state of the connection */
        !            70:     char *             localname;      /* Local representation of file name */
        !            71:     FILE *             fp;        /* If we can't use sockets on local files */
1.77      frystyk    72: } file_info;
1.2       timbl      73: 
1.80    ! frystyk    74: /* Local definition */
        !            75: struct _HTStream {
        !            76:     CONST HTStreamClass *      isa;
        !            77:     HTStream *                 target;
        !            78: };
        !            79: 
1.77      frystyk    80: /* Controlling globals */
1.33      timbl      81: PUBLIC BOOL HTTakeBackup = YES;
1.1       timbl      82: 
                     83: PRIVATE char *HTMountRoot = "/Net/";           /* Where to find mounts */
1.2       timbl      84: 
1.61      frystyk    85: /* ------------------------------------------------------------------------- */
1.11      timbl      86: 
1.80    ! frystyk    87: /*     Convert file URLs into a local representation
        !            88: **     ---------------------------------------------
        !            89: **     The URL has already been translated through the rules in get_physical
        !            90: **     in HTAccess.c and all we need to do now is to map the path to a local
        !            91: **     representation, for example if must translate '/' to the ones that
        !            92: **     turn the wrong way ;-)
        !            93: **
        !            94: ** On Entry
        !            95: **     The full URL that's is going to be translated
        !            96: ** On Exit,
        !            97: **     -1      Error
        !            98: **      0      URL is not pointing to the local file system
        !            99: **      1      A local name is returned in `local' which must be freed
        !           100: **             by caller
1.1       timbl     101: */
1.80    ! frystyk   102: PRIVATE int HTLocalName ARGS2(CONST char *, url, char **, local)
1.1       timbl     103: {
1.80    ! frystyk   104:     if (url) {
        !           105:        char * access = HTParse(url, "", PARSE_ACCESS);
        !           106:        char * host = HTParse(url, "", PARSE_HOST);
        !           107:        char * path = HTParse(url, "", PARSE_PATH+PARSE_PUNCTUATION);
        !           108:        CONST char *myhost;
        !           109:        
        !           110:        /* Find out if this is a reference to the local file system */
        !           111:        if ((*access && strcmp(access, "file")) ||
        !           112:            (*host && strcasecomp(host, "localhost") &&
        !           113:             (myhost=HTGetHostName()) && strcmp(host, myhost))) {
        !           114:            if (PROT_TRACE)
        !           115:                fprintf(TDEST, "LocalName... Not on local file system\n");
        !           116:            free(access);
        !           117:            free(host);
        !           118:            free(path);
        !           119:            return 0;
        !           120:        } else {
        !           121:            /*
        !           122:             ** Do whatever translation is required here in order to fit your
        !           123:             ** platform _before_ the path is unescaped.
        !           124:             */
1.65      duns      125: #if VMS
1.80    ! frystyk   126:            HTVMS_checkDecnet(path);
        !           127: #endif
1.77      frystyk   128: #ifdef _WINDOWS
                    129:            /* an absolute pathname with logical drive */
                    130:            if (*path == '/' && path[2] == ':')    
                    131:                /* NB. need memmove because overlaps */
1.80    ! frystyk   132:                memmove( path, path+1, strlen(path) + 1);
1.77      frystyk   133: #endif
1.80    ! frystyk   134:            
        !           135:            HTUnEscape(path);             /* Take out the escaped characters */
        !           136:            if (PROT_TRACE)
        !           137:                fprintf(TDEST, "Node........ `%s' means path `%s'\n",url,path);
        !           138:            free(access);
1.1       timbl     139:            free(host);
1.80    ! frystyk   140:            *local = path;
        !           141:            return 1;
1.1       timbl     142:        }
                    143:     }
1.80    ! frystyk   144:     return -1;
1.1       timbl     145: }
                    146: 
                    147: 
                    148: /*     Make a WWW name from a full local path name
                    149: **
                    150: ** Bugs:
                    151: **     At present, only the names of two network root nodes are hand-coded
                    152: **     in and valid for the NeXT only. This should be configurable in
                    153: **     the general case.
                    154: */
                    155: 
                    156: PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
                    157: {
                    158:     char * result;
                    159: #ifdef NeXT
                    160:     if (0==strncmp("/private/Net/", name, 13)) {
                    161:        result = (char *)malloc(7+strlen(name+13)+1);
                    162:        if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
                    163:        sprintf(result, "file://%s", name+13);
                    164:     } else
                    165: #endif
                    166:     if (0==strncmp(HTMountRoot, name, 5)) {
                    167:        result = (char *)malloc(7+strlen(name+5)+1);
                    168:        if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
                    169:        sprintf(result, "file://%s", name+5);
                    170:     } else {
1.63      frystyk   171:         result = (char *)malloc(7+strlen(HTGetHostName())+strlen(name)+1);
1.1       timbl     172:        if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
1.63      frystyk   173:        sprintf(result, "file://%s%s", HTGetHostName(), name);
1.1       timbl     174:     }
1.75      frystyk   175:     if (TRACE) fprintf(TDEST, "File `%s'\n\tmeans node `%s'\n", name, result);
1.1       timbl     176:     return result;
                    177: }
                    178: 
                    179: 
                    180: /*     Determine write access to a file
1.2       timbl     181: **     --------------------------------
1.80    ! frystyk   182: **     If stat_info is NULL then the function calls stat() on it's own,
        !           183: **     otherwise it uses the information found in stat_info
1.2       timbl     184: ** On exit,
                    185: **     return value    YES if file can be accessed and can be written to.
                    186: **
                    187: ** Bugs:
                    188: **     1.      No code for non-unix systems.
                    189: **     2.      Isn't there a quicker way?
1.1       timbl     190: */
1.80    ! frystyk   191: PUBLIC BOOL HTEditable ARGS2(CONST char *, filename, struct stat *, stat_info)
        !           192: {
1.75      frystyk   193: #ifndef NO_UNIX_IO
                    194: #ifdef NO_GROUPS
1.80    ! frystyk   195:     return NO;            /* Safe answer till we find the correct algorithm */
1.1       timbl     196: #else
1.75      frystyk   197: #ifdef NeXT
1.80    ! frystyk   198:     int groups[NGROUPS];
1.75      frystyk   199: #else
1.80    ! frystyk   200:     gid_t groups[NGROUPS];
1.75      frystyk   201: #endif 
1.80    ! frystyk   202:     int i;
        !           203:     uid_t myUid;
        !           204:     int        ngroups;                        /* The number of groups  */
1.1       timbl     205:     struct stat        fileStatus;
1.80    ! frystyk   206:     struct stat *fileptr = stat_info ? stat_info : &fileStatus;
1.1       timbl     207:         
1.80    ! frystyk   208:     if (!stat_info) {
        !           209:        if (HTStat(filename, &fileStatus))
        !           210:            return NO;                            /* Can't even access file! */
        !           211:     }
1.1       timbl     212:     ngroups = getgroups(NGROUPS, groups);      /* Groups to which I belong  */
                    213:     myUid = geteuid();                         /* Get my user identifier */
                    214: 
                    215:     if (TRACE) {
                    216:         int i;
1.75      frystyk   217:        fprintf(TDEST, 
1.19      timbl     218:            "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
1.80    ! frystyk   219:            (unsigned int) fileptr->st_mode, (int) fileptr->st_uid,
        !           220:            (int) fileptr->st_gid, (int) myUid, ngroups);
1.75      frystyk   221:        for (i=0; i<ngroups; i++) fprintf(TDEST, " %d", (int) groups[i]);
                    222:        fprintf(TDEST, ")\n");
1.1       timbl     223:     }
                    224:     
1.80    ! frystyk   225:     if (fileptr->st_mode & 0002)               /* I can write anyway? */
1.1       timbl     226:        return YES;
                    227:        
1.80    ! frystyk   228:     if ((fileptr->st_mode & 0200)              /* I can write my own file? */
        !           229:      && (fileptr->st_uid == myUid))
1.1       timbl     230:        return YES;
                    231: 
1.80    ! frystyk   232:     if (fileptr->st_mode & 0020)               /* Group I am in can write? */
1.1       timbl     233:     {
                    234:        for (i=0; i<ngroups; i++) {
1.80    ! frystyk   235:             if (groups[i] == fileptr->st_gid)
1.1       timbl     236:                return YES;
                    237:        }
                    238:     }
1.75      frystyk   239:     if (TRACE) fprintf(TDEST, "\tFile is not editable.\n");
1.1       timbl     240:     return NO;                                 /* If no excuse, can't do */
                    241: #endif
1.80    ! frystyk   242: #else
        !           243:     /*
        !           244:     ** We don't know and can't find out. Can we be sure that when opening
        !           245:     ** a file in mode "a" that the file is not modified?
        !           246:     */
        !           247:     return NO;
        !           248: #endif /* !NO_UNIX_IO */
1.1       timbl     249: }
1.80    ! frystyk   250: 
1.1       timbl     251: 
1.2       timbl     252: /*     Make a save stream
                    253: **     ------------------
                    254: **
                    255: **     The stream must be used for writing back the file.
                    256: **     @@@ no backup done
                    257: */
1.29      timbl     258: PUBLIC HTStream * HTFileSaveStream ARGS1(HTRequest *, request)
1.2       timbl     259: {
                    260: 
1.29      timbl     261:     CONST char * addr = HTAnchor_address((HTAnchor*)request->anchor);
1.80    ! frystyk   262:     char *  filename;
1.33      timbl     263:     FILE* fp;
1.80    ! frystyk   264:     HTLocalName(addr, &filename);
1.33      timbl     265: 
                    266: /*     @ Introduce CVS handling here one day
                    267: */
                    268: /*     Take a backup before we do anything silly   931205
                    269: */        
                    270:     if (HTTakeBackup) {
1.69      frystyk   271:        char * p;
                    272:        char * backup_filename = (char *) malloc(strlen(filename)+2);
1.33      timbl     273:        if (!backup_filename) outofmem(__FILE__, "taking backup");
                    274:        strcpy(backup_filename, filename);
1.34      timbl     275:        for(p=backup_filename+strlen(backup_filename);; p--) {
1.33      timbl     276:            if ((*p=='/') || (p<backup_filename)) {
                    277:                p[1]=',';               /* Insert comma after slash */
                    278:                break;
                    279:            }
                    280:            p[1] = p[0];        /* Move up everything to the right of it */
                    281:        }
                    282:        
1.65      duns      283: 
                    284: #ifdef VMS
                    285:        if ((fp=fopen(filename, "r", "shr=put", "shr=upd"))) {  /* File exists */
                    286: #else /* not VMS */
1.35      luotonen  287:        if ((fp=fopen(filename, "r"))) {                /* File exists */
1.65      duns      288: #endif /* not VMS */
1.33      timbl     289:            fclose(fp);
1.75      frystyk   290:            if (TRACE) fprintf(TDEST, "File `%s' exists\n", filename);
1.77      frystyk   291:            if (REMOVE_FILE(backup_filename)) {
1.75      frystyk   292:                if (TRACE) fprintf(TDEST, "Backup file `%s' removed\n",
1.56      frystyk   293:                                   backup_filename);
1.33      timbl     294:            }
                    295:            if (rename(filename, backup_filename)) {    /* != 0 => Failure */
1.75      frystyk   296:                if (TRACE) fprintf(TDEST, "Rename `%s' to `%s' FAILED!\n",
1.56      frystyk   297:                                   filename, backup_filename);
1.33      timbl     298:            } else {                                    /* Success */
1.75      frystyk   299:                if (TRACE) fprintf(TDEST, "Renamed `%s' to `%s'\n", filename,
1.56      frystyk   300:                                   backup_filename);
1.33      timbl     301:            }
                    302:        }
                    303:        free(backup_filename);
                    304:     } /* if take backup */    
1.2       timbl     305:     
1.61      frystyk   306:     if ((fp = fopen(filename, "w")) == NULL) {
1.75      frystyk   307:        HTErrorSysAdd(request, ERR_FATAL, errno, NO, "fopen");
1.61      frystyk   308:        return NULL;
                    309:     } else
1.40      frystyk   310:        return HTFWriter_new(fp, NO);
1.2       timbl     311: }
                    312: 
1.10      secret    313: 
1.80    ! frystyk   314: /*                                                                  FileCleanup
        !           315: **
        !           316: **      This function closes the connection and frees memory.
        !           317: **
        !           318: **      Returns 0 on OK, else -1
        !           319: */
        !           320: PRIVATE int FileCleanup ARGS2(HTRequest *, req, BOOL, abort)
        !           321: {
        !           322:     file_info *file;
        !           323:     int status = 0;
        !           324:     if (!req || !req->net_info) {
        !           325:        if (PROT_TRACE) fprintf(TDEST, "FileCleanup. Bad argument!\n");
        !           326:        status = -1;
        !           327:     } else {
        !           328:        file = (file_info *) req->net_info;
        !           329:        if (file->sockfd != INVSOC) {
        !           330: 
        !           331:            /* Free stream with data FROM local file system */
        !           332:            if (file->target) {
        !           333:                if (abort)
        !           334:                    (*file->target->isa->abort)(file->target, NULL);
        !           335:                else
        !           336:                    (*file->target->isa->_free)(file->target);
        !           337:            }
        !           338: 
        !           339:            if (PROT_TRACE)
        !           340:                fprintf(TDEST,"FILE........ Closing socket %d\n",file->sockfd);
        !           341:            if ((status = NETCLOSE(file->sockfd)) < 0)
        !           342:                HTErrorSysAdd(file->request, ERR_FATAL, socerrno, NO,
        !           343:                              "NETCLOSE");
        !           344:            HTThreadState(file->sockfd, THD_CLOSE);
        !           345:            file->sockfd = INVSOC;
        !           346:            HTThread_clear((HTNetInfo *) file);
        !           347:        }
        !           348:        if (file->isoc)
        !           349:            HTInputSocket_free(file->isoc);
        !           350:        if (file->fp)
        !           351:            fclose(file->fp);
        !           352:        FREE(file->localname);
        !           353:        free(file);
        !           354:        req->net_info = NULL;
        !           355:     }
        !           356:     return status;
        !           357: }
        !           358: 
        !           359: 
1.1       timbl     360: /*     Load a document
                    361: **     ---------------
                    362: **
                    363: ** On entry,
1.80    ! frystyk   364: **     request         This is the request structure
1.1       timbl     365: ** On exit,
1.77      frystyk   366: **     returns         HT_ERROR        Error has occured or interrupted
                    367: **                     HT_WOULD_BLOCK  We are using blocking I/O so this
                    368: **                                     indicates that we have a read a chunk
                    369: **                                     of data and now want to see what's up
                    370: **                                     in the eventloop.
1.80    ! frystyk   371: **                     HT_LOADED       if file has been loaded successfully
        !           372: **                     HT_NO_DATA      if file is empty
        !           373: **                     HT_RETRY        if file service is unavailable
1.1       timbl     374: */
1.36      luotonen  375: PUBLIC int HTLoadFile ARGS1 (HTRequest *, request)
1.1       timbl     376: {
1.80    ! frystyk   377:     int status = HT_ERROR;
        !           378:     file_info *file;                         /* Specific access information */
1.45      luotonen  379: 
1.61      frystyk   380:     if (!request || !request->anchor) {
1.75      frystyk   381:        if (TRACE) fprintf(TDEST, "HTLoadFile.. Called with bad argument\n");
1.77      frystyk   382:        return HT_ERROR;
1.50      frystyk   383:     }
1.1       timbl     384: 
1.80    ! frystyk   385:     /* Only do the setup first time through. This is actually state FILE_BEGIN
        !           386:        but it can't be in the state machine as we need the structure first */
        !           387:     if (!request->net_info) {    
        !           388:        if (PROT_TRACE) {
        !           389:            char *url = HTAnchor_physical(request->anchor);
        !           390:            fprintf(TDEST, "LoadFile.... Looking for `%s\'\n", url);
        !           391:        }
        !           392:        if ((file = (file_info *) calloc(1, sizeof(file_info))) == NULL)
        !           393:            outofmem(__FILE__, "HTLoadFILE");
        !           394:        file->sockfd = INVSOC;                      /* Invalid socket number */
        !           395:        file->request = request;
        !           396:        request->net_info = (HTNetInfo *) file;
        !           397:        file->state = FILE_BEGIN;
        !           398:        HTThread_new((HTNetInfo *) file);
        !           399:     } else {
        !           400:        file = (file_info *) request->net_info;         /* Get existing copy */
1.78      frystyk   401: 
1.80    ! frystyk   402:        /* @@@ NEED ALSO TO CHECK FOR ANSI FILE DESCRIPTORS @@@ */
        !           403:        if (file->sockfd != INVSOC && HTThreadIntr(file->sockfd))
        !           404:            file->state = FILE_ERROR;
        !           405:     }
1.36      luotonen  406: 
1.80    ! frystyk   407:     /* Now jump into the machine. We know the state from the previous run */
        !           408:     while (1) {
        !           409:        switch (file->state) {
        !           410:          case FILE_BEGIN:
        !           411:            if (HTSecure) {
        !           412:                if (PROT_TRACE)
        !           413:                    fprintf(TDEST, "LoadFile.... No access to local file system\n");
        !           414:                file->state = FILE_TRY_FTP;
        !           415:                break;
        !           416:            }
        !           417:            if ((status = HTLocalName(HTAnchor_physical(request->anchor),
        !           418:                                      &file->localname)) == -1) {
        !           419:                file->state = FILE_ERROR;
        !           420:                break;
        !           421:            } else if (status == 0) {
        !           422:                file->state = FILE_TRY_FTP;
        !           423:                break;
1.2       timbl     424:            }
1.80    ! frystyk   425:            /*
        !           426:            ** If we have to do content negotiation then find the object that
        !           427:            ** fits best into either what the client has indicated in the
        !           428:            ** accept headers or what the client has registered on it own.
        !           429:            ** The object chosen can in fact be a directory! However, content
        !           430:            ** negotiation only makes sense it we can read the directory!
        !           431:            ** We stat the file in order to find the size and to see it if
        !           432:            ** exists.
        !           433:            */
        !           434:            {
        !           435:                struct stat stat_info;        /* Contains actual file chosen */
        !           436:                if (request->ContentNegotiation) {
        !           437:                    char *new_path=HTMulti(request,file->localname,&stat_info);
        !           438:                    if (new_path) {
        !           439:                        FREE(file->localname);
        !           440:                        file->localname = new_path;
        !           441:                        HTAnchor_setPhysical(request->anchor, new_path);
        !           442:                    } else {
        !           443:                        file->state = FILE_ERROR;
        !           444:                        break;
        !           445:                    }
        !           446:                } else {
        !           447:                    if (HTStat(file->localname, &stat_info) == -1) {
        !           448:                        if (PROT_TRACE)
        !           449:                            fprintf(TDEST, "HTLoadFile.. Can't stat %s\n",
        !           450:                                    file->localname);
        !           451:                        file->state = FILE_ERROR;
        !           452:                        break;
        !           453:                    }
        !           454:                }
        !           455:                /* Check to see if the 'localname' is in fact a directory */
        !           456:                if (((stat_info.st_mode) & S_IFMT) == S_IFDIR)
        !           457:                    file->state = FILE_PARSE_DIR;
        !           458:                else {
        !           459:                    /*
        !           460:                    ** If empty file then only serve it if it is editable
        !           461:                    */
        !           462:                    BOOL editable = HTEditable(file->localname, &stat_info);
        !           463:                    HTBind_getBindings(request->anchor);
        !           464:                    if (editable)
        !           465:                        HTAnchor_appendMethods(request->anchor, METHOD_PUT);
        !           466:                    if (stat_info.st_size)
        !           467:                        HTAnchor_setLength(request->anchor, stat_info.st_size);
        !           468: 
        !           469:                    /* Put all relevant metainformation into the anchor */
        !           470:                    request->anchor->header_parsed = YES;
        !           471: 
        !           472:                    if (!editable && !stat_info.st_size) {
        !           473:                        HTErrorAdd(request, ERR_FATAL, NO, HTERR_NO_RESPONSE,
        !           474:                                   NULL, 0, "HTLoadFile");
        !           475:                        file->state = FILE_NO_DATA;
        !           476:                    } else
        !           477:                        file->state = FILE_NEED_OPEN_FILE;
        !           478:                }
1.2       timbl     479:            }
1.80    ! frystyk   480:            break;
1.61      frystyk   481: 
1.80    ! frystyk   482:          case FILE_NEED_OPEN_FILE:
        !           483:            /*
        !           484:            ** If we have unix file descriptors then use this otherwise use
        !           485:            ** the ANSI C file descriptors
        !           486:            */
        !           487: #ifndef NO_UNIX_IO
        !           488:            if ((file->sockfd = open(file->localname, O_RDONLY)) == -1) {
        !           489:                HTErrorSysAdd(request, ERR_FATAL, errno, NO, "open");
        !           490:                file->state = FILE_ERROR;
        !           491:            } else {
        !           492:                if (PROT_TRACE)
        !           493:                    fprintf(TDEST,"HTLoadFile.. `%s' opened using fd %d \n",
        !           494:                            file->localname, file->sockfd);
        !           495: 
        !           496:                /* If non-blocking protocol then change socket status
        !           497:                ** I use FCNTL so that I can ask the status before I set it.
        !           498:                ** See W. Richard Stevens (Advan. Prog. in UNIX env, p.364)
        !           499:                ** Be CAREFULL with the old `O_NDELAY' - it wont work as read()
        !           500:                ** returns 0 when blocking and NOT -1. FNDELAY is ONLY for BSD
        !           501:                ** and does NOT work on SVR4 systems. O_NONBLOCK is POSIX.
        !           502:                */
        !           503:                if (!HTProtocolBlocking(request)) {
        !           504:                    if ((status = FCNTL(file->sockfd, F_GETFL, 0)) != -1) {
        !           505:                        status |= O_NONBLOCK;                       /* POSIX */
        !           506:                        status = FCNTL(file->sockfd, F_SETFL, status);
        !           507:                    }
        !           508:                    if (PROT_TRACE) {
        !           509:                        if (status == -1)
        !           510:                            fprintf(TDEST, "HTLoadFile.. Can't make socket non-blocking\n");
        !           511:                        else
        !           512:                            fprintf(TDEST, "HTLoadFile.. Using NON_BLOCKING I/O\n");
        !           513:                    }
        !           514:                }
        !           515:            
        !           516:                /* Set up read buffer and streams */
        !           517:                file->isoc = HTInputSocket_new(file->sockfd);
        !           518:                file->target = HTStreamStack(HTAnchor_format(request->anchor),
        !           519:                                             request->output_format,
        !           520:                                             request->output_stream,
        !           521:                                             request, YES);
        !           522:                HTThreadState(file->sockfd, THD_SET_READ);
        !           523:                file->state = file->target ? FILE_NEED_BODY : FILE_ERROR;
1.61      frystyk   524:            }
1.80    ! frystyk   525: #else
1.65      duns      526: #ifdef VMS
1.80    ! frystyk   527:            if ((file->fp = fopen(file->localname,"r","shr=put","shr=upd")) == NULL) {
        !           528: #else
        !           529:            if ((file->fp = fopen(file->localname,"r")) == NULL){
1.65      duns      530: #endif /* not VMS */
1.80    ! frystyk   531:                HTErrorSysAdd(request, ERR_FATAL, errno, NO, "fopen");
        !           532:                file->state = FILE_ERROR;
        !           533:            } else {
        !           534:                if (PROT_TRACE)
        !           535:                    fprintf(TDEST,"HTLoadFile.. Open `%s'\n",file->localname);
        !           536:                file->state = FILE_NEED_BODY;
        !           537:            }
        !           538: #endif /* !NO_UNIX_IO */
        !           539:            
        !           540:            break;
1.65      duns      541: 
1.80    ! frystyk   542:          case FILE_NEED_BODY:
        !           543: #ifndef NO_UNIX_IO
        !           544:            status = HTSocketRead(request, file->target);
        !           545:            if (status == HT_WOULD_BLOCK)
        !           546:                return HT_WOULD_BLOCK;
        !           547:            else if (status == HT_INTERRUPTED)
        !           548:                file->state = FILE_ERROR;
        !           549:            else if (status == HT_LOADED) {
        !           550:                file->state = FILE_GOT_DATA;
1.61      frystyk   551:            } else
1.80    ! frystyk   552:                file->state = FILE_ERROR;
        !           553: #else
        !           554:            if (HTParseFile(HTAnchor_format(request->anchor), file->fp,
        !           555:                            request) < 0)
        !           556:                file->state = FILE_ERROR;
        !           557:            else
        !           558:                file->state = FILE_GOT_DATA;
        !           559: #endif
        !           560:            break;
1.1       timbl     561: 
1.80    ! frystyk   562:          case FILE_PARSE_DIR:
        !           563: #ifdef GOT_READ_DIR
        !           564:            file->state = HTBrowseDirectory(request, file->localname) < 0 ?
        !           565:                FILE_ERROR : FILE_GOT_DATA;
        !           566: #else
        !           567:            file->state = FILE_ERROR;
1.1       timbl     568: #endif
1.80    ! frystyk   569:            break;
        !           570: 
        !           571:          case FILE_TRY_FTP:
        !           572:            {
        !           573:                char *url = HTAnchor_physical(request->anchor);
        !           574:                HTAnchor *anchor;
        !           575:                char *newname = NULL;
        !           576:                StrAllocCopy(newname, "ftp:");
        !           577:                if (!strncmp(url, "file:", 5))
        !           578:                    StrAllocCat(newname, url+5);
        !           579:                else
        !           580:                    StrAllocCat(newname, url);
        !           581:                anchor = HTAnchor_findAddress(newname);
        !           582:                free(newname);
        !           583:                FileCleanup(request, NO);
        !           584:                return HTLoadAnchorRecursive(anchor, request);
        !           585:            }
        !           586:            break;
1.1       timbl     587: 
1.80    ! frystyk   588:          case FILE_GOT_DATA:
        !           589:            FileCleanup(request, NO);
        !           590:            return HT_LOADED;
        !           591:            break;
        !           592: 
        !           593:          case FILE_NO_DATA:
        !           594:            FileCleanup(request, NO);
        !           595:            return HT_NO_DATA;
        !           596:            break;
        !           597: 
        !           598:          case FILE_RETRY:
        !           599:            FileCleanup(request, YES);
        !           600:            return HT_RETRY;
        !           601:            break;
        !           602: 
        !           603:          case FILE_ERROR:
        !           604:            FileCleanup(request, YES);
        !           605:            return HT_ERROR;
        !           606:            break;
        !           607:        }
        !           608:     } /* End of while(1) */
1.1       timbl     609: }
                    610: 
1.80    ! frystyk   611: 
        !           612: /*
        !           613: **  Protocol descriptor
        !           614: **  Make this non-blocking is possible
        !           615: */
        !           616: #ifndef NO_UNIX_IO
        !           617: GLOBALDEF PUBLIC HTProtocol HTFile = {
        !           618:     "file", SOC_NON_BLOCK, HTLoadFile, HTFileSaveStream, NULL
        !           619: };
        !           620: #else
1.71      frystyk   621: GLOBALDEF PUBLIC HTProtocol HTFile = {
                    622:     "file", SOC_BLOCK, HTLoadFile, HTFileSaveStream, NULL
                    623: };
1.80    ! frystyk   624: #endif

Webmaster