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

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

Webmaster