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

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

Webmaster