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

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.159   ! vbancrof    6: **     @(#) $Id: HTFile.c,v 1.158 2000/08/10 16:05:39 kahan Exp $
1.1       timbl       7: **
                      8: **     This is unix-specific code in general, with some VMS bits.
1.8       timbl       9: **     These are routines for file access used by browsers.
1.1       timbl      10: **
                     11: ** History:
                     12: **        Feb 91       Written Tim Berners-Lee CERN/CN
                     13: **        Apr 91       vms-vms access included using DECnet syntax
                     14: **     26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
                     15: **                     Fixed access bug for relative names on VMS.
1.28      duns       16: **        Sep 93 (MD)  Access to VMS files allows sharing.
                     17: **     15 Nov 93 (MD)  Moved HTVMSname to HTVMSUTILS.C
1.52      duns       18: **     22 Feb 94 (MD)  Excluded two routines if we are not READING directories
1.61      frystyk    19: **     18 May 94 (HF)  Directory stuff removed and stream handling updated,
                     20: **                     error messages introduced etc.
1.120     frystyk    21: **               HFN   Separated transport
1.1       timbl      22: **
                     23: ** Bugs:
1.2       timbl      24: **     FTP: Cannot access VMS files from a unix machine.
                     25: **      How can we know that the
1.1       timbl      26: **     target machine runs VMS?
                     27: */
                     28: 
1.67      frystyk    29: /* Library Includes */
1.143     frystyk    30: #include "wwwsys.h"
1.120     frystyk    31: #include "WWWUtil.h"
                     32: #include "WWWCore.h"
1.124     frystyk    33: #include "WWWDir.h"
                     34: #include "WWWTrans.h"
1.120     frystyk    35: #include "HTReqMan.h"
1.152     frystyk    36: #include "HTBind.h"
1.124     frystyk    37: #include "HTMulti.h"
1.67      frystyk    38: #include "HTFile.h"            /* Implemented here */
                     39: 
1.91      frystyk    40: /* Final states have negative value */
1.80      frystyk    41: typedef enum _FileState {
1.95      frystyk    42:     FS_RETRY           = -4,
                     43:     FS_ERROR           = -3,
                     44:     FS_NO_DATA         = -2,
                     45:     FS_GOT_DATA                = -1,
                     46:     FS_BEGIN           = 0,
1.158     kahan      47:     FS_PENDING,
1.95      frystyk    48:     FS_DO_CN,
                     49:     FS_NEED_OPEN_FILE,
                     50:     FS_NEED_BODY,
                     51:     FS_PARSE_DIR,
                     52:     FS_TRY_FTP
1.80      frystyk    53: } FileState;
                     54: 
1.91      frystyk    55: /* This is the context structure for the this module */
1.77      frystyk    56: typedef struct _file_info {
1.80      frystyk    57:     FileState          state;            /* Current state of the connection */
1.95      frystyk    58:     char *             local;          /* Local representation of file name */
1.125     frystyk    59:     struct stat                stat_info;            /* Contains actual file chosen */
1.134     frystyk    60:     HTNet *            net;
1.140     frystyk    61:     HTTimer *          timer;
1.77      frystyk    62: } file_info;
1.2       timbl      63: 
1.80      frystyk    64: struct _HTStream {
1.112     frystyk    65:     const HTStreamClass *      isa;
1.80      frystyk    66: };
                     67: 
1.120     frystyk    68: struct _HTInputStream {
                     69:     const HTInputStreamClass * isa;
                     70: };
                     71: 
1.95      frystyk    72: PRIVATE HTDirReadme    dir_readme = HT_DIR_README_TOP;
                     73: PRIVATE HTDirAccess    dir_access = HT_DIR_OK;
                     74: PRIVATE HTDirShow      dir_show = HT_DS_SIZE+HT_DS_DATE+HT_DS_DES+HT_DS_ICON;
                     75: PRIVATE HTDirKey       dir_key = HT_DK_CINS;
1.143     frystyk    76: PRIVATE BOOL           file_suffix_binding = YES;
1.2       timbl      77: 
1.61      frystyk    78: /* ------------------------------------------------------------------------- */
1.11      timbl      79: 
1.95      frystyk    80: /*     Directory Access
                     81: **     ----------------
                     82: */
                     83: PUBLIC BOOL HTFile_setDirAccess (HTDirAccess mode)
                     84: {
                     85:     dir_access = mode;
                     86:     return YES;
                     87: }
                     88: 
                     89: PUBLIC HTDirAccess HTFile_dirAccess (void)
                     90: {
                     91:     return dir_access;
                     92: }
                     93: 
                     94: /*     Directory Readme
                     95: **     ----------------
                     96: */
                     97: PUBLIC BOOL HTFile_setDirReadme (HTDirReadme mode)
                     98: {
                     99:     dir_readme = mode;
                    100:     return YES;
                    101: }
                    102: 
                    103: PUBLIC HTDirReadme HTFile_dirReadme (void)
                    104: {
                    105:     return dir_readme;
                    106: }
                    107: 
1.143     frystyk   108: /*
                    109: **  Should we find the bindings between file suffixes and media types
                    110: **  here or not?
                    111: */
                    112: PUBLIC BOOL HTFile_doFileSuffixBinding (BOOL mode)
                    113: {
                    114:     file_suffix_binding = mode;
                    115:     return YES;
                    116: }
                    117: 
                    118: PUBLIC BOOL HTFile_fileSuffixBinding (void)
                    119: {
                    120:     return file_suffix_binding;
                    121: }
                    122: 
1.95      frystyk   123: /*     HTFile_readDir
                    124: **     --------------
                    125: **     Reads the directory "path"
                    126: **     Returns:
                    127: **             HT_ERROR        Error
1.107     frystyk   128: **             HT_FORBIDDEN    Directory reading not allowed
1.95      frystyk   129: **             HT_LOADED       Successfully read the directory
1.1       timbl     130: */
1.95      frystyk   131: PRIVATE int HTFile_readDir (HTRequest * request, file_info *file)
1.1       timbl     132: {
1.112     frystyk   133: #ifdef HAVE_READDIR
                    134:     DIR * dp;
1.95      frystyk   135:     struct stat file_info;
1.124     frystyk   136:     HTParentAnchor * anchor = HTRequest_anchor(request);
                    137:     char *url = HTAnchor_physical(anchor);
1.95      frystyk   138:     char fullname[HT_MAX_PATH+1];
                    139:     char *name;
1.153     frystyk   140:     HTTRACE(PROT_TRACE, "Reading..... directory\n");
1.95      frystyk   141:     if (dir_access == HT_DIR_FORBID) {
1.100     frystyk   142:        HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN,
1.95      frystyk   143:                   NULL, 0, "HTFile_readDir");
1.107     frystyk   144:        return HT_FORBIDDEN;
1.95      frystyk   145:     }
                    146:     
                    147:     /* Initialize path name for stat() */
                    148:     if (*(name = (url+strlen(url)-1)) != '/') {
                    149:        char *newurl = NULL;
                    150:        StrAllocCopy(newurl, url);
                    151:        StrAllocCat(newurl, "/");
1.110     frystyk   152:        HT_FREE(file->local);
1.123     frystyk   153:        file->local = HTWWWToLocal(newurl, "", HTRequest_userProfile(request));
1.110     frystyk   154:        HT_FREE(newurl);
1.95      frystyk   155:     }
                    156:     strcpy(fullname, file->local);
                    157:     name = fullname+strlen(fullname);           /* Point to end of fullname */
                    158: 
                    159:     /* Check if access is enabled */
                    160:     if (dir_access == HT_DIR_SELECTIVE) {
                    161:        strcpy(name, DEFAULT_DIR_FILE);
                    162:        if (HT_STAT(fullname, &file_info)) {
1.153     frystyk   163:            HTTRACE(PROT_TRACE, "Read dir.... `%s\' not found\n" _ DEFAULT_DIR_FILE);
1.100     frystyk   164:            HTRequest_addError(request, ERR_FATAL, NO, HTERR_FORBIDDEN,
1.95      frystyk   165:                       NULL, 0, "HTFile_readDir");
1.107     frystyk   166:            return HT_FORBIDDEN;
1.1       timbl     167:        }
                    168:     }
                    169: 
1.95      frystyk   170:     if ((dp = opendir(file->local))) {
1.112     frystyk   171:        struct dirent * dirbuf;
1.95      frystyk   172:        HTDir *dir = HTDir_new(request, dir_show, dir_key);
                    173:        char datestr[20];
                    174:        char sizestr[10];
                    175:        HTFileMode mode;
                    176: #ifdef HT_REENTRANT
1.130     frystyk   177:        struct dirent result;                               /* For readdir_r */
1.155     kahan     178: #endif
                    179: 
                    180: #ifdef HAVE_READDIR_R_2
                    181:         while ((dirbuf = (struct dirent *) readdir_r(dp, &result)))
                    182: #elif defined(HAVE_READDIR_R_3)
                    183:         while (readdir_r(dp, &result, &dirbuf) == 0)
1.95      frystyk   184: #else
                    185:        while ((dirbuf = readdir(dp)))
1.155     kahan     186: #endif /* HAVE_READDIR_R_2 */
1.95      frystyk   187:        {
                    188:            /* Current and parent directories are never shown in list */
1.112     frystyk   189: #ifdef HAVE_DIRENT_INO
1.95      frystyk   190:            if (!dirbuf->d_ino ||
                    191:                !strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, ".."))
1.112     frystyk   192: #else
                    193:            if (!strcmp(dirbuf->d_name, ".") || !strcmp(dirbuf->d_name, ".."))
                    194: #endif
1.95      frystyk   195:                continue;
                    196: 
                    197:            /* Make a lstat on the file */
                    198:            strcpy(name, dirbuf->d_name);
                    199:            if (HT_LSTAT(fullname, &file_info)) {
1.153     frystyk   200:                HTTRACE(PROT_TRACE, "Read dir.... lstat failed: %s\n" _ fullname);
1.95      frystyk   201:                continue;
                    202:            }
1.1       timbl     203: 
1.95      frystyk   204:            /* Convert stat info to fit our setup */
1.103     frystyk   205:            if (((mode_t) file_info.st_mode & S_IFMT) == S_IFDIR) {
1.95      frystyk   206: #ifdef VMS
                    207:                char *dot = strstr(name, ".DIR");      /* strip .DIR part... */
                    208:                if (dot) *dot = '\0';
                    209: #endif /* VMS */
                    210:                mode = HT_IS_DIR;
                    211:                if (dir_show & HT_DS_SIZE) strcpy(sizestr, "-");
                    212:            } else {
                    213:                mode = HT_IS_FILE;
                    214:                if (dir_show & HT_DS_SIZE)
                    215:                    HTNumToStr(file_info.st_size, sizestr, 10);
                    216:            }
                    217:            if (dir_show & HT_DS_DATE)
                    218:                HTDateDirStr(&file_info.st_mtime, datestr, 20);
1.1       timbl     219: 
1.95      frystyk   220:            /* Add to the list */
                    221:            if (HTDir_addElement(dir, name, datestr, sizestr, mode) != YES)
                    222:                break;
                    223:        }
                    224:        closedir(dp);
                    225:        HTDir_free(dir);
1.127     frystyk   226:        return HT_LOADED;
                    227:     } else {
1.100     frystyk   228:        HTRequest_addSystemError(request,  ERR_FATAL, errno, NO, "opendir");
1.127     frystyk   229:        return HT_ERROR;
                    230:     }
1.112     frystyk   231: #else
1.114     eric      232:     return HT_ERROR;   /* needed for WWW_MSWINDOWS */
1.112     frystyk   233: #endif /* HAVE_READDIR */
1.1       timbl     234: }
                    235: 
                    236: /*     Determine write access to a file
1.2       timbl     237: **     --------------------------------
1.80      frystyk   238: **     If stat_info is NULL then the function calls stat() on it's own,
                    239: **     otherwise it uses the information found in stat_info
1.2       timbl     240: ** On exit,
                    241: **     return value    YES if file can be accessed and can be written to.
                    242: **
                    243: ** Bugs:
                    244: **     1.      No code for non-unix systems.
                    245: **     2.      Isn't there a quicker way?
1.1       timbl     246: */
1.112     frystyk   247: PRIVATE BOOL HTEditable (const char * filename, struct stat * stat_info)
1.80      frystyk   248: {
1.112     frystyk   249: #ifdef GETGROUPS_T
1.80      frystyk   250:     int i;
                    251:     uid_t myUid;
                    252:     int        ngroups;                        /* The number of groups  */
1.1       timbl     253:     struct stat        fileStatus;
1.80      frystyk   254:     struct stat *fileptr = stat_info ? stat_info : &fileStatus;
1.112     frystyk   255:     GETGROUPS_T groups[NGROUPS];
1.80      frystyk   256:     if (!stat_info) {
1.90      frystyk   257:        if (HT_STAT(filename, &fileStatus))
1.80      frystyk   258:            return NO;                            /* Can't even access file! */
                    259:     }
1.1       timbl     260:     ngroups = getgroups(NGROUPS, groups);      /* Groups to which I belong  */
                    261:     myUid = geteuid();                         /* Get my user identifier */
                    262: 
1.153     frystyk   263: #ifdef HTDEBUG
1.95      frystyk   264:     if (PROT_TRACE) {
1.1       timbl     265:         int i;
1.153     frystyk   266:        HTTRACE(PROT_TRACE,
                    267:                "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (" _
                    268:                (unsigned int) fileptr->st_mode _
                    269:                (int) fileptr->st_uid _ (int) fileptr->st_gid _
                    270:                (int) myUid _ ngroups);
                    271:        for (i=0; i<ngroups; i++) HTTRACE(PROT_TRACE, " %d" _ (int) groups[i]);
                    272:        HTTRACE(PROT_TRACE, ")\n");
1.1       timbl     273:     }
1.153     frystyk   274: #endif /* HTDEBUG */
1.1       timbl     275:     
1.80      frystyk   276:     if (fileptr->st_mode & 0002)               /* I can write anyway? */
1.1       timbl     277:        return YES;
                    278:        
1.80      frystyk   279:     if ((fileptr->st_mode & 0200)              /* I can write my own file? */
                    280:      && (fileptr->st_uid == myUid))
1.1       timbl     281:        return YES;
                    282: 
1.80      frystyk   283:     if (fileptr->st_mode & 0020)               /* Group I am in can write? */
1.1       timbl     284:     {
                    285:        for (i=0; i<ngroups; i++) {
1.80      frystyk   286:             if (groups[i] == fileptr->st_gid)
1.1       timbl     287:                return YES;
                    288:        }
                    289:     }
1.153     frystyk   290:     HTTRACE(PROT_TRACE, "\tFile is not editable.\n");
1.1       timbl     291:     return NO;                                 /* If no excuse, can't do */
1.80      frystyk   292: #else
                    293:     /*
                    294:     ** We don't know and can't find out. Can we be sure that when opening
                    295:     ** a file in mode "a" that the file is not modified?
                    296:     */
                    297:     return NO;
1.112     frystyk   298: #endif /* GETGROUPS_T */
1.1       timbl     299: }
1.80      frystyk   300: 
1.91      frystyk   301: /*     FileCleanup
                    302: **     -----------
1.80      frystyk   303: **      This function closes the connection and frees memory.
1.91      frystyk   304: **      Returns YES on OK, else NO
1.80      frystyk   305: */
1.91      frystyk   306: PRIVATE int FileCleanup (HTRequest *req, int status)
1.80      frystyk   307: {
1.126     frystyk   308:     HTNet * net = HTRequest_net(req);
                    309:     file_info * file = (file_info *) HTNet_context(net);
                    310:     HTStream * input = HTRequest_inputStream(req);
1.106     frystyk   311: 
                    312:     /* Free stream with data TO Local file system */
1.149     frystyk   313:     if (input) {
                    314:         if (status == HT_INTERRUPTED)
                    315:             (*input->isa->abort)(input, NULL);
                    316:         else
                    317:             (*input->isa->_free)(input);
                    318:         HTRequest_setInputStream(req, NULL);
1.106     frystyk   319:     }
                    320: 
1.140     frystyk   321:     /*
1.149     frystyk   322:     **  Remove if we have registered a timer function as a callback
1.140     frystyk   323:     */
                    324:     if (file->timer) {
                    325:        HTTimer_delete(file->timer);
                    326:        file->timer = NULL;
                    327:     }
                    328: 
1.91      frystyk   329:     if (file) {
1.110     frystyk   330:        HT_FREE(file->local);
                    331:        HT_FREE(file);
1.80      frystyk   332:     }
1.128     frystyk   333:     HTNet_delete(net, status);
1.91      frystyk   334:     return YES;
1.80      frystyk   335: }
                    336: 
                    337: 
1.1       timbl     338: /*     Load a document
                    339: **     ---------------
                    340: **
                    341: ** On entry,
1.80      frystyk   342: **     request         This is the request structure
1.1       timbl     343: ** On exit,
1.91      frystyk   344: **     returns         HT_ERROR        Error has occured in call back
                    345: **                     HT_OK           Call back was OK
1.1       timbl     346: */
1.134     frystyk   347: PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type);
                    348: 
                    349: PUBLIC int HTLoadFile (SOCKET soc, HTRequest * request)
1.1       timbl     350: {
1.134     frystyk   351:     file_info *file;                         /* Specific access information */
                    352:     HTNet * net = HTRequest_net(request);
                    353:     HTParentAnchor * anchor = HTRequest_anchor(request);
                    354: 
1.153     frystyk   355:     HTTRACE(PROT_TRACE, "HTLoadFile.. Looking for `%s\'\n" _ 
1.134     frystyk   356:                            HTAnchor_physical(anchor));
                    357:     if ((file = (file_info *) HT_CALLOC(1, sizeof(file_info))) == NULL)
                    358:        HT_OUTOFMEM("HTLoadFILE");
                    359:     file->state = FS_BEGIN;
                    360:     file->net = net;
                    361:     HTNet_setContext(net, file);
                    362:     HTNet_setEventCallback(net, FileEvent);
                    363:     HTNet_setEventParam(net, file);  /* callbacks get http* */
                    364: 
1.140     frystyk   365:     return FileEvent(soc, file, HTEvent_BEGIN);            /* get it started - ops is ignored */
1.134     frystyk   366: }
                    367: 
1.140     frystyk   368: PRIVATE int ReturnEvent (HTTimer * timer, void * param, HTEventType type)
                    369: {
                    370:     file_info * file = (file_info *) param;
1.141     frystyk   371:     if (timer != file->timer)
1.153     frystyk   372:        HTDEBUGBREAK("File timer %p not in sync\n" _ timer);
                    373:     HTTRACE(PROT_TRACE, "HTLoadFile.. Continuing %p with timer %p\n" _ file _ timer);
1.140     frystyk   374: 
                    375:     /*
                    376:     **  Delete the timer
                    377:     */
1.150     frystyk   378:     HTTimer_delete(file->timer);
1.140     frystyk   379:     file->timer = NULL;
                    380: 
                    381:     /*
                    382:     **  Now call the event again
                    383:     */
                    384:     return FileEvent(INVSOC, file, HTEvent_READ);
                    385: }
                    386: 
                    387: 
1.134     frystyk   388: PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type)
                    389: {
                    390:     file_info *file = pVoid;                         /* Specific access information */
1.80      frystyk   391:     int status = HT_ERROR;
1.134     frystyk   392:     HTNet * net = file->net;
                    393:     HTRequest * request = HTNet_request(net);
1.124     frystyk   394:     HTParentAnchor * anchor = HTRequest_anchor(request);
1.45      luotonen  395: 
1.91      frystyk   396:     /*
                    397:     ** Initiate a new file structure and bind to request structure
                    398:     ** This is actually state FILE_BEGIN, but it can't be in the state
                    399:     ** machine as we need the structure first.
                    400:     */
1.134     frystyk   401:     if (type == HTEvent_CLOSE) {                                     /* Interrupted */
1.107     frystyk   402:        HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED,
1.131     frystyk   403:                           NULL, 0, "HTLoadFile");
1.107     frystyk   404:        FileCleanup(request, HT_INTERRUPTED);
1.91      frystyk   405:        return HT_OK;
1.134     frystyk   406:     }
                    407: 
1.36      luotonen  408: 
1.80      frystyk   409:     /* Now jump into the machine. We know the state from the previous run */
                    410:     while (1) {
                    411:        switch (file->state) {
1.151     frystyk   412:        case FS_BEGIN:
                    413: 
                    414:            /* We only support safe (GET, HEAD, etc) methods for the moment */
                    415:            if (!HTMethod_isSafe(HTRequest_method(request))) {
                    416:                HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_ALLOWED,
                    417:                                   NULL, 0, "HTLoadFile");
                    418:                file->state = FS_ERROR;
                    419:                break;
                    420:            }
                    421: 
                    422:            /* Check whether we have access to local disk at all */
1.102     frystyk   423:            if (HTLib_secure()) {
1.153     frystyk   424:                HTTRACE(PROT_TRACE, "LoadFile.... No access to local file system\n");
1.95      frystyk   425:                file->state = FS_TRY_FTP;
1.80      frystyk   426:                break;
                    427:            }
1.123     frystyk   428:            file->local = HTWWWToLocal(HTAnchor_physical(anchor), "",
                    429:                                       HTRequest_userProfile(request));
1.95      frystyk   430:            if (!file->local) {
                    431:                file->state = FS_TRY_FTP;
1.80      frystyk   432:                break;
1.2       timbl     433:            }
1.135     eric      434: 
1.151     frystyk   435:            /* Create a new host object and link it to the net object */
1.139     frystyk   436:            {
                    437:                HTHost * host = NULL;
1.145     frystyk   438:                if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR;
1.139     frystyk   439:                HTNet_setHost(net, host);
1.156     kahan     440:                if (HTHost_addNet(host, net) == HT_PENDING) {
1.153     frystyk   441:                    HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n");
1.158     kahan     442:                    /* move to the hack state */
                    443:                    file->state = FS_PENDING;
                    444:                    return HT_OK;
1.156     kahan     445:                }
1.139     frystyk   446:            }
1.158     kahan     447:            file->state = FS_DO_CN;
                    448:            break;
                    449: 
                    450:        case FS_PENDING:
                    451:            /*
                    452:            ** 2000/08/10 JK : This is a funny state. Because of the
                    453:            ** internal libwww stacks, when doing multiple local
                    454:            ** requests (e.g., while using the Robot), we need to ask
                    455:            ** again for the host object. If we had jumped directly to
                    456:            ** the FS_DO_CN state, libwww would have blocked because
                    457:            ** of socket starvation.
                    458:            ** This state is similar to FS_BEGINNING, but just requests 
                    459:            ** the host object. 
                    460:            ** YES. THIS IS AN UGLY HACK!!
                    461:            */
                    462:            {
                    463:                HTHost * host = NULL;
                    464:                if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR;
                    465:                HTNet_setHost(net, host);
                    466:                if (HTHost_addNet(host, net) == HT_PENDING) {
                    467:                    HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n");
                    468:                    file->state = FS_PENDING;
                    469:                    return HT_OK;
                    470:                }
                    471:            }
                    472:            file->state = FS_DO_CN;
1.86      frystyk   473:            break;
                    474: 
1.151     frystyk   475:        case FS_DO_CN:
1.80      frystyk   476:            /*
                    477:            ** If we have to do content negotiation then find the object that
                    478:            ** fits best into either what the client has indicated in the
1.86      frystyk   479:            ** accept headers or what the client has registered on its own.
1.80      frystyk   480:            ** The object chosen can in fact be a directory! However, content
1.134     frystyk   481:            ** negotiation only makes sense if we can read the directory!
1.80      frystyk   482:            ** We stat the file in order to find the size and to see it if
                    483:            ** exists.
                    484:            */
1.132     frystyk   485:            if (HTRequest_negotiation(request) &&
                    486:                HTMethod_isSafe(HTRequest_method(request))) {
1.125     frystyk   487:                char * conneg = HTMulti(request, file->local,&file->stat_info);
                    488:                if (conneg) {
                    489:                    HT_FREE(file->local);
                    490:                    file->local = conneg;
                    491:                    HTAnchor_setPhysical(anchor, conneg);
1.153     frystyk   492:                    HTTRACE(PROT_TRACE, "Load File... Found `%s\'\n" _ conneg);
1.80      frystyk   493:                } else {
1.153     frystyk   494:                    HTTRACE(PROT_TRACE, "Load File... Not found - even tried content negotiation\n");
1.142     frystyk   495:                    HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
1.126     frystyk   496:                                       NULL, 0, "HTLoadFile");
1.125     frystyk   497:                    file->state = FS_ERROR;
                    498:                    break;
1.80      frystyk   499:                }
1.125     frystyk   500:            } else {
                    501:                if (HT_STAT(file->local, &file->stat_info) == -1) {
1.153     frystyk   502:                    HTTRACE(PROT_TRACE, "Load File... Not found `%s\'\n" _ file->local);
1.125     frystyk   503:                    HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND,
                    504:                                       NULL, 0, "HTLoadFile");
                    505:                    file->state = FS_ERROR;
                    506:                    break;
1.80      frystyk   507:                }
1.125     frystyk   508:            }
                    509: 
                    510:            /*
1.154     frystyk   511:            ** Check to see if the 'localname' is in fact a directory.
                    512:            ** Note that we can't do a HEAD on a directory
1.125     frystyk   513:            */
                    514:            if (((file->stat_info.st_mode) & S_IFMT) == S_IFDIR) {
1.154     frystyk   515:                if (HTRequest_method(request) == METHOD_GET)
                    516:                    file->state = FS_PARSE_DIR;
                    517:                else {
                    518:                    HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT,
                    519:                                       NULL, 0, "HTLoadFile");
                    520:                    file->state = FS_NO_DATA;
                    521:                }
1.125     frystyk   522:                break;
                    523:            }
                    524: 
                    525:            /*
1.132     frystyk   526:            ** If empty file then only serve it if it is editable. We also get
                    527:            ** the bindings for the file suffixes in lack of better bindings
1.125     frystyk   528:            */
                    529:            {
                    530:                BOOL editable = HTEditable(file->local, &file->stat_info);
1.143     frystyk   531:                if (file_suffix_binding) HTBind_getAnchorBindings(anchor);
1.133     frystyk   532:                if (editable) HTAnchor_appendAllow(anchor, METHOD_PUT);
1.147     frystyk   533: 
                    534:                /* Set the file size */
1.125     frystyk   535:                if (file->stat_info.st_size)
                    536:                    HTAnchor_setLength(anchor, file->stat_info.st_size);
1.147     frystyk   537: 
                    538:                /* Set the file last modified time stamp */
                    539:                if (file->stat_info.st_mtime > 0)
                    540:                    HTAnchor_setLastModified(anchor, file->stat_info.st_mtime);
                    541: 
                    542:                /* Check to see if we can edit it */
1.125     frystyk   543:                if (!editable && !file->stat_info.st_size) {
1.147     frystyk   544:                    HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT,
1.125     frystyk   545:                                       NULL, 0, "HTLoadFile");
                    546:                    file->state = FS_NO_DATA;
1.151     frystyk   547:                } else {
                    548:                    file->state = (HTRequest_method(request)==METHOD_GET) ? 
                    549:                        FS_NEED_OPEN_FILE : FS_GOT_DATA;
                    550:                }
1.2       timbl     551:            }
1.80      frystyk   552:            break;
1.61      frystyk   553: 
1.95      frystyk   554:          case FS_NEED_OPEN_FILE:
1.146     frystyk   555:            status = HTFileOpen(net, file->local, HT_FB_RDONLY);
1.120     frystyk   556:            if (status == HT_OK) {
                    557:                /* 
                    558:                ** Create the stream pipe FROM the channel to the application.
                    559:                ** The target for the input stream pipe is set up using the
1.132     frystyk   560:                ** stream stack.
1.120     frystyk   561:                */
1.139     frystyk   562:                {
                    563:                    HTStream * rstream = HTStreamStack(HTAnchor_format(anchor),
                    564:                                                       HTRequest_outputFormat(request),
                    565:                                                       HTRequest_outputStream(request),
                    566:                                                       request, YES);
                    567:                    HTNet_setReadStream(net, rstream);
                    568:                    HTRequest_setOutputConnected(request, YES);
                    569:                }
1.126     frystyk   570: 
1.120     frystyk   571:                /*
                    572:                ** Create the stream pipe TO the channel from the application
                    573:                ** and hook it up to the request object
                    574:                */
                    575:                {
                    576:                    HTOutputStream * output = HTNet_getOutput(net, NULL, 0);
                    577:                    HTRequest_setInputStream(request, (HTStream *) output);
                    578:                }
1.80      frystyk   579: 
1.120     frystyk   580:                /*
                    581:                ** Set up concurrent read/write if this request isn't the
                    582:                ** source for a PUT or POST. As source we don't start reading
                    583:                ** before all destinations are ready. If destination then
                    584:                ** register the input stream and get ready for read
                    585:                */
1.139     frystyk   586:                if (HTRequest_isSource(request) && !HTRequest_destinationsReady(request))
1.120     frystyk   587:                    return HT_OK;
1.139     frystyk   588:                HTRequest_addError(request, ERR_INFO, NO, HTERR_OK, NULL, 0,
                    589:                                   "HTLoadFile");
1.120     frystyk   590:                file->state = FS_NEED_BODY;
1.106     frystyk   591: 
1.120     frystyk   592:                /* If we are _not_ using preemptive mode and we are Unix fd's
                    593:                ** then return here to get the same effect as when we are
                    594:                ** connecting to a socket. That way, HTFile acts just like any
                    595:                ** other protocol module even though we are in fact doing
                    596:                ** blocking connect
                    597:                */
1.148     frystyk   598:                if (HTEvent_isCallbacksRegistered()) {
                    599:                    if (!HTRequest_preemptive(request)) {
                    600:                        if (!HTNet_preemptive(net)) {
1.153     frystyk   601:                            HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n");
1.148     frystyk   602:                            HTHost_register(HTNet_host(net), net, HTEvent_READ);
                    603:                        } else if (!file->timer) {
1.153     frystyk   604:                            HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n");
1.148     frystyk   605:                            file->timer =
                    606:                                HTTimer_new(NULL, ReturnEvent, file, 1, YES, NO);
                    607:                        }
                    608:                        return HT_OK;
1.140     frystyk   609:                    }
1.120     frystyk   610:                }
1.131     frystyk   611:            } else if (status == HT_WOULD_BLOCK || status == HT_PENDING)
1.120     frystyk   612:                return HT_OK;
1.126     frystyk   613:            else {
                    614:                HTRequest_addError(request, ERR_INFO, NO, HTERR_INTERNAL,
                    615:                                   NULL, 0, "HTLoadFile");
1.120     frystyk   616:                file->state = FS_ERROR;                /* Error or interrupt */
1.126     frystyk   617:            }
1.107     frystyk   618:            break;
1.82      frystyk   619: 
1.95      frystyk   620:          case FS_NEED_BODY:
1.139     frystyk   621:            status = HTHost_read(HTNet_host(net), net);
1.80      frystyk   622:            if (status == HT_WOULD_BLOCK)
1.91      frystyk   623:                return HT_OK;
1.108     frystyk   624:            else if (status == HT_LOADED || status == HT_CLOSED) {
1.95      frystyk   625:                file->state = FS_GOT_DATA;
1.126     frystyk   626:            } else {
                    627:                HTRequest_addError(request, ERR_INFO, NO, HTERR_FORBIDDEN,
                    628:                                   NULL, 0, "HTLoadFile");
1.95      frystyk   629:                file->state = FS_ERROR;
1.126     frystyk   630:            }
1.80      frystyk   631:            break;
1.1       timbl     632: 
1.95      frystyk   633:          case FS_PARSE_DIR:
                    634:            status = HTFile_readDir(request, file);
                    635:            if (status == HT_LOADED)
                    636:                file->state = FS_GOT_DATA;
1.127     frystyk   637:            else
1.95      frystyk   638:                file->state = FS_ERROR;
1.80      frystyk   639:            break;
                    640: 
1.95      frystyk   641:          case FS_TRY_FTP:
1.80      frystyk   642:            {
1.99      frystyk   643:                char *url = HTAnchor_physical(anchor);
1.80      frystyk   644:                HTAnchor *anchor;
                    645:                char *newname = NULL;
                    646:                StrAllocCopy(newname, "ftp:");
                    647:                if (!strncmp(url, "file:", 5))
                    648:                    StrAllocCat(newname, url+5);
                    649:                else
                    650:                    StrAllocCat(newname, url);
                    651:                anchor = HTAnchor_findAddress(newname);
1.109     frystyk   652:                HTRequest_setAnchor(request, anchor);
1.110     frystyk   653:                HT_FREE(newname);
1.91      frystyk   654:                FileCleanup(request, HT_IGNORE);
1.97      frystyk   655:                return HTLoad(request, YES);
1.80      frystyk   656:            }
                    657:            break;
1.1       timbl     658: 
1.95      frystyk   659:          case FS_GOT_DATA:
1.107     frystyk   660:            FileCleanup(request, HT_LOADED);
1.91      frystyk   661:            return HT_OK;
1.80      frystyk   662:            break;
                    663: 
1.95      frystyk   664:          case FS_NO_DATA:
1.107     frystyk   665:            FileCleanup(request, HT_NO_DATA);
1.91      frystyk   666:            return HT_OK;
1.80      frystyk   667:            break;
                    668: 
1.95      frystyk   669:          case FS_RETRY:
1.107     frystyk   670:            FileCleanup(request, HT_RETRY);
1.91      frystyk   671:            return HT_OK;
1.80      frystyk   672:            break;
                    673: 
1.95      frystyk   674:          case FS_ERROR:
1.107     frystyk   675:            FileCleanup(request, HT_ERROR);
1.91      frystyk   676:            return HT_OK;
1.80      frystyk   677:            break;
                    678:        }
                    679:     } /* End of while(1) */
1.1       timbl     680: }
1.159   ! vbancrof  681: 
        !           682:     
        !           683: /* Calculate the required buffer size (in bytes) for directory     
        !           684: ** entries read from the given directory handle.  Return -1 if this
        !           685: ** this cannot be done.                                              
        !           686: */    
        !           687: PUBLIC size_t HTFile_dirent_buf_size(DIR * dirp)
        !           688: {
        !           689:     long name_max;
        !           690: #if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
        !           691:         name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
        !           692:         if (name_max == -1)
        !           693: #if defined(NAME_MAX)
        !           694:                 name_max = NAME_MAX;
        !           695: #else
        !           696:                 return (size_t)(-1);
        !           697: #endif
        !           698: #else
        !           699: #if defined(NAME_MAX)
        !           700:             name_max = NAME_MAX;
        !           701: #else
        !           702: #error "buffer size for readdir_r cannot be determined"
        !           703: #endif
        !           704: #endif
        !           705:         return (size_t)offsetof(struct dirent, d_name) + name_max + 1;
        !           706: }

Webmaster