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

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

Webmaster