/*****************************************************************************/ /* DAVlock.c WASD WebDAV locks are implemented as meta-data and stored along with properties in the meta-data administered by the DAVMETA.C module. VERSION HISTORY --------------- 19-OCT-2018 MGD bugfix; DavLockTest() munge the ]NAME.DIR into a directory specification .NAME] 12-SEP-2018 MGD bugfix; significant refactor of locking 18-JUN-2014 MGD DavLockUrnUuidToken() move from opaque to uuid token (from "opaquelocktoken:..." to "urn:uuid:...") 15-JUN-2014 MGD interim test of metadata directory 07-SEP-2013 MGD DavLockSetTimeout() BitKinex at least uses a timeout of "infinity" rather than the RFC "infinite" 02-SEP-2009 MGD bugfix; scanning backwards through extended file spec 31-DEC-2006 MGD initial */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif #include #include #include #include #include "wasd.h" #include "davweb.h" #define WASD_MODULE "DAVLOCK" #ifndef WASD_WEBDAV #define WASD_WEBDAV 1 #endif /******************/ /* global storage */ /******************/ /********************/ /* external storage */ /********************/ extern BOOL WebDavEnabled, WebDavLockingEnabled; extern int WebDavLockCollectionDepth; WebDavLockTimeoutDefaultSeconds, WebDavLockTimeoutMaxSeconds; extern int ToLowerCase[], ToUpperCase[]; extern unsigned long HttpdTime64[], SysPrvMask[]; extern char ErrorSanityCheck[], HttpdVersion[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Should be performed as soon as the metadata file XML has been parsed. Scan all locks looking for any that may have timed-out. Simply remove them from the in-memory lock list. If the metadata file is updated those lock(s) will not be updated with it. If the file is not updated then they just sit there harmlessly until it is. If a lock is being checked then timed-out locks will be absent. */ DavLockPreProcess (REQUEST_STRUCT *rqptr) { int status; unsigned long ScratchTime64 [2]; WEBDAV_LOCK *lckptr, *nextptr; WEBDAV_META *mtaptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockPreProcess()"); tkptr = rqptr->WebDavTaskPtr; mtaptr = &tkptr->MetaData; /* don't use LIST_ITERATE as we may need to manipulate the list itself */ for (lckptr = LIST_GET_HEAD(&mtaptr->LockList); lckptr; lckptr = nextptr) { /* get any next entry in case the current is removed */ nextptr = LIST_GET_NEXT (&lckptr->ListEntry); if (lckptr->Timeout) { /* a negative time indicates the timeout has expired */ status = lib$sub_times (&lckptr->ExpiresTime64, &HttpdTime64, &ScratchTime64); if (VMSnok (status)) { if (status == LIB$_NEGTIM) { if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK timeout !20%D expired", lckptr->ExpiresTime64); } /* negative time or some other error (finger-poken?) */ ListRemove (&mtaptr->LockList, lckptr); } } } } /*****************************************************************************/ /* No up-front VMS DLM lock required. Just rely on the DLM lock acquired by the meta-data lock. */ DavLockBegin (REQUEST_STRUCT *rqptr) { WEBDAV_META *mtaptr; /*********/ /* begin */ /*********/ if (!WebDavLockingEnabled) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockBegin()"); if (WATCHING (rqptr, WATCH_RESPONSE)) WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "LOCK !AZ", rqptr->ParseOds.ExpFileName); mtaptr = &rqptr->WebDavTaskPtr->MetaData; DavMetaLock (rqptr, mtaptr, rqptr->ParseOds.ExpFileName, &DavLockUpdate, mtaptr); } /*****************************************************************************/ /* If obtained the DLM and meta-data lock the update with new/refresh lock. */ DavLockUpdate (WEBDAV_META *mtaptr) { REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockUpdate()"); if (VMSok (mtaptr->VmsStatus)) DavMetaUpdate (mtaptr, DavLockEnd, mtaptr); else DavLockEnd (mtaptr); } /*****************************************************************************/ /* Results of the lock update. */ DavLockEnd (WEBDAV_META *mtaptr) { static char LockToken [64] = "Lock-Token: "; int status; char *ltptr; REQUEST_STRUCT *rqptr; WEBDAV_LOCK *lckptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; tkptr = rqptr->WebDavTaskPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockEnd()"); if (tkptr->DestOds.Fab.fab$l_sts) { /* just created an unmapped URL empty resource */ mtaptr->VmsStatus = tkptr->DestOds.Fab.fab$l_sts; if (WATCHING (rqptr, WATCH_WEBDAV)) if (VMSok (mtaptr->VmsStatus)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK unmapped URL"); else WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK !&S unmapped URL", mtaptr->VmsStatus); if (VMSok (mtaptr->VmsStatus)) { tkptr->ResponseStatusCode = 201; status = OdsClose (&tkptr->DestOds, NULL, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, NULL, FI_LI); } } else if (mtaptr->MetaCreated) { if (!mtaptr->MetaForDir) { /* not a directory meta, check if the resource exists */ sys$setprv (1, &SysPrvMask, 0, 0); status = OdsFileExists (NULL, rqptr->ParseOds.ExpFileName); sys$setprv (0, &SysPrvMask, 0, 0); if (VMSnok (status)) { /* does not exists - create unmapped URL empty resource */ AuthAccessEnable (rqptr, rqptr->ParseOds.ExpFileName, AUTH_ACCESS_WRITE); OdsCreate (&tkptr->DestOds, rqptr->ParseOds.ExpFileName, rqptr->ParseOds.ExpFileNameLength, NULL, 0, FAB$M_PUT + FAB$M_GET + FAB$M_BIO + FAB$M_TRN, FAB$M_CIF + FAB$M_TEF, FAB$M_NIL, FAB$C_UDF, 0, NULL, &DavLockEnd, mtaptr); AuthAccessEnable (rqptr, 0, 0); return; } } } if (VMSok (mtaptr->VmsStatus)) { /* the list head will be the most recently added/refreshed lock */ if (!(lckptr = LIST_GET_HEAD(&mtaptr->LockList))) { ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI); DavWebResponse (rqptr, 500, SS$_BUGCHECK, NULL, FI_LI); DavWebEnd (rqptr); return; } if (mtaptr->LockCreated) { /* lock-token response only supplied with new locks not refreshes */ strcpy (LockToken+12, lckptr->TokenString); strcat (LockToken+12, "\r\n"); ltptr = LockToken; } else ltptr = ""; } else { /* blanket 412 for any locking failure */ DavWebResponse (rqptr, 412, mtaptr->VmsStatus, NULL, FI_LI); DavWebEnd (rqptr); return; } rqptr->rqResponse.NoGzip = true; ResponseHeader (rqptr, tkptr->ResponseStatusCode, "text/xml; charset=\"utf-8\"", -1, NULL, ltptr); FaoToNet (rqptr, "\n\ \n\ \n\ \n\ \n\ \n\ !AZ\n\ !AZ!AZ!AZ\n\ !AZ!AZ!AZ\ !AZ!AZ!AZ\ \n\ \n\ \n", lckptr->Type == WEBDAV_LOCK_TYPE_WRITE ? "write" : "", lckptr->Scope == WEBDAV_LOCK_SCOPE_EXCLUSIVE ? "exclusive" : "", lckptr->Scope == WEBDAV_LOCK_SCOPE_SHARED ? "shared" : "", lckptr->TokenString, lckptr->Depth == WEBDAV_DEPTH_ZERO ? "0" : "", lckptr->Depth == WEBDAV_DEPTH_ONE ? "1" : "", lckptr->Depth == WEBDAV_DEPTH_INFINITY ? "infinity" : "", lckptr->Timeout ? " " : "", lckptr->Timeout ? lckptr->TimeoutString : "", lckptr->Timeout ? "\n" : "", STR_DSC_LEN(&lckptr->OwnerDsc) ? " " : "", /* the owner string has already been HTML-sanitised */ STR_DSC_LEN(&lckptr->OwnerDsc) ? STR_DSC_PTR(&lckptr->OwnerDsc) : "", STR_DSC_LEN(&lckptr->OwnerDsc) ? "\n" : ""); DavWebEnd (rqptr); } /*****************************************************************************/ /* No up-front VMS DLM lock required. Just rely on the DLM lock acquired by the meta-data lock. */ DavUnLockBegin (REQUEST_STRUCT *rqptr) { WEBDAV_META *mtaptr; /*********/ /* begin */ /*********/ if (!WebDavLockingEnabled) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavUnLockBegin()"); if (WATCHING (rqptr, WATCH_RESPONSE)) WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "UNLOCK !AZ !AZ", rqptr->ParseOds.ExpFileName, rqptr->rqHeader.WebDavLockTokenPtr); mtaptr = &rqptr->WebDavTaskPtr->MetaData; DavMetaLock (rqptr, mtaptr, rqptr->ParseOds.ExpFileName, &DavUnLockUpdate, mtaptr); } /*****************************************************************************/ /* If obtained the DLM and meta-data lock the update with no lock. */ DavUnLockUpdate (WEBDAV_META *mtaptr) { REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavUnLockUpdate()"); if (VMSok (mtaptr->VmsStatus)) DavMetaUpdate (mtaptr, DavUnLockEnd, mtaptr); else DavUnLockEnd (mtaptr); } /*****************************************************************************/ /* */ DavUnLockEnd (WEBDAV_META *mtaptr) { REQUEST_STRUCT *rqptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavUnLockEnd() !&S", mtaptr->VmsStatus); tkptr = rqptr->WebDavTaskPtr; if (VMSok (mtaptr->VmsStatus)) { /* success status but with no content */ DavWebResponse (rqptr, 204, 0, "success", FI_LI); } else { /* unlock failed */ rqptr->rqResponse.HttpStatus = 412; DavWebResponse (rqptr, 412, mtaptr->VmsStatus, NULL, FI_LI); } DavWebEnd (rqptr); } /*****************************************************************************/ /* Build either a new or refreshed lock XML text into the supplied lock. Return a zero to indicate success or an HTTP error status code. */ int DavLockSetTimeout ( REQUEST_STRUCT *rqptr, WEBDAV_LOCK *lckptr ) { int status, expsecs, tmosecs; char ExpiresDateTime [32]; unsigned long TimeoutTime64 [2]; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockSetTimeout() !&Z", rqptr->rqHeader.WebDavTimeoutPtr); tkptr = rqptr->WebDavTaskPtr; if (rqptr->rqHeader.WebDavTimeoutPtr) { /* from request header (both "infinite" and "infinity" seem in use) */ if (strsame (rqptr->rqHeader.WebDavTimeoutPtr, "Infinit", 7)) lckptr->Timeout = WEBDAV_TIMEOUT_INFINITE; else if (strsame (rqptr->rqHeader.WebDavTimeoutPtr, "Second-", 7)) lckptr->Timeout = atoi(rqptr->rqHeader.WebDavTimeoutPtr+7); else { if (WATCHING (rqptr, WATCH_RESPONSE)) WatchThis (WATCHITM(rqptr), WATCH_RESPONSE, "TIMEOUT? !AZ", rqptr->rqHeader.WebDavTimeoutPtr); /* default timeouts */ if (!(lckptr->Timeout = rqptr->rqPathSet.WebDavLockTimeoutDefault)) lckptr->Timeout = WebDavLockTimeoutDefaultSeconds; } } else { /* default timeouts */ if (!(lckptr->Timeout = rqptr->rqPathSet.WebDavLockTimeoutDefault)) lckptr->Timeout = WebDavLockTimeoutDefaultSeconds; } /* limit to configured maximum */ if (rqptr->rqPathSet.WebDavLockTimeoutMax) { if (lckptr->Timeout > rqptr->rqPathSet.WebDavLockTimeoutMax) lckptr->Timeout = rqptr->rqPathSet.WebDavLockTimeoutMax; } else if (WebDavLockTimeoutMaxSeconds) { if (lckptr->Timeout > WebDavLockTimeoutMaxSeconds) lckptr->Timeout = WebDavLockTimeoutMaxSeconds; } if (lckptr->Timeout) { /* current lock timeout */ expsecs = tmosecs = lckptr->Timeout; } else { /* default timeouts */ if (!(expsecs = tmosecs = rqptr->rqPathSet.WebDavLockTimeoutDefault)) expsecs = tmosecs = WebDavLockTimeoutDefaultSeconds; } /* limit to configured maximum */ if (rqptr->rqPathSet.WebDavLockTimeoutMax) { if (expsecs > rqptr->rqPathSet.WebDavLockTimeoutMax) expsecs = rqptr->rqPathSet.WebDavLockTimeoutMax; } else if (WebDavLockTimeoutMaxSeconds) { if (expsecs > WebDavLockTimeoutMaxSeconds) expsecs = WebDavLockTimeoutMaxSeconds; } /* one second delta multipled by the number of seconds */ TimeoutTime64[0] = -10000000; TimeoutTime64[1] = -1; status = lib$mult_delta_time (&expsecs, &TimeoutTime64); if (VMSnok (status)) { ErrorNoticed (rqptr, status, NULL, FI_LI); return (500); } /* out in the future */ status = lib$add_times (&HttpdTime64, &TimeoutTime64, &lckptr->ExpiresTime64); if (VMSnok (status)) { ErrorNoticed (rqptr, status, NULL, FI_LI); return (500); } if (lckptr->Timeout == WEBDAV_TIMEOUT_INFINITE) strcpy (lckptr->TimeoutString, "infinite"); else if (lckptr->Timeout) sprintf (lckptr->TimeoutString, "Second-%d", lckptr->Timeout); else lckptr->TimeoutString[0] = '\0'; if (!QUAD_ZERO(lckptr->ExpiresTime64)) { DavWebDateTimeTo3339 (ExpiresDateTime, lckptr->ExpiresTime64); FaoToBuffer (lckptr->ExpiresString, sizeof(lckptr->ExpiresString), NULL, "!AZ !20%D", ExpiresDateTime, &lckptr->ExpiresTime64); } else lckptr->ExpiresString[0] = '\0'; return (0); } /*****************************************************************************/ /* Generate XML text equivalent to the lock data adding it to the supplied string descriptor. */ DavLockXml ( REQUEST_STRUCT *rqptr, WEBDAV_LOCK *lckptr, STR_DSC *wdptr ) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockXml() !8XL !8XL", lckptr, wdptr); StrDscBuild (wdptr, NULL, "TokenString); StrDscBuild (wdptr, NULL, "\"\n"); if (lckptr->Depth == WEBDAV_DEPTH_ZERO) StrDscBuild (wdptr, NULL, "depth=\"0\"\n"); else if (lckptr->Depth == WEBDAV_DEPTH_ONE) StrDscBuild (wdptr, NULL, "depth=\"1\"\n"); else if (lckptr->Depth == WEBDAV_DEPTH_INFINITY) StrDscBuild (wdptr, NULL, "depth=\"infinity\"\n"); if (lckptr->Type == WEBDAV_LOCK_TYPE_WRITE) StrDscBuild (wdptr, NULL, "type=\"write\"\n"); if (lckptr->Scope == WEBDAV_LOCK_SCOPE_EXCLUSIVE) StrDscBuild (wdptr, NULL, "scope=\"exclusive\"\n"); else if (lckptr->Scope == WEBDAV_LOCK_SCOPE_SHARED) StrDscBuild (wdptr, NULL, "scope=\"shared\"\n"); if (lckptr->Timeout == WEBDAV_TIMEOUT_INFINITE) StrDscBuild (wdptr, NULL, "timeout=\"infinite\""); else if (lckptr->Timeout) FaoToBuffer (wdptr, -1, NULL, "timeout=\"Second-!UL\"", lckptr->Timeout); else FaoToBuffer (wdptr, -1, NULL, "timeout=\"Second-!UL\"", WebDavLockTimeoutDefaultSeconds); if (!QUAD_ZERO(&lckptr->ExpiresTime64)) FaoToBuffer (wdptr, -1, NULL, "\nexpires=\"!AZ\">\n", lckptr->ExpiresString); else FaoToBuffer (wdptr, -1, NULL, ">\n"); if (STR_DSC_LEN(&lckptr->OwnerDsc)) FaoToBuffer (wdptr, -1, NULL, "!&;AZ\n", STR_DSC_PTR(&lckptr->OwnerDsc)); StrDscBuild (wdptr, NULL, "\n"); if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "!AZ", STR_DSC_PTR(wdptr)); } /*****************************************************************************/ /* From the meta-data. */ DavLockDiscovery (REQUEST_STRUCT *rqptr) { WEBDAV_LOCK *lckptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockDiscovery()"); /* step through each lock in the list generating an XML text equivalent */ LIST_ITERATE (lckptr, &rqptr->WebDavTaskPtr->MetaData.LockList) { FaoToNet (rqptr, " \n\ \n\ \n\ \n\ !AZ\n\ !AZ!AZ!AZ\n\ !AZ!AZ!AZ\ !AZ!AZ!AZ\ \n\ \n", lckptr->Type == WEBDAV_LOCK_TYPE_WRITE ? "write" : "", lckptr->Scope == WEBDAV_LOCK_SCOPE_EXCLUSIVE ? "exclusive" : "", lckptr->Scope == WEBDAV_LOCK_SCOPE_SHARED ? "shared" : "", lckptr->TokenString, lckptr->Depth == WEBDAV_DEPTH_ZERO ? "0" : "", lckptr->Depth == WEBDAV_DEPTH_ONE ? "1" : "", lckptr->Depth == WEBDAV_DEPTH_INFINITY ? "infinity" : "", lckptr->Timeout ? " " : "", lckptr->Timeout ? lckptr->TimeoutString : "", lckptr->Timeout ? "\n" : "", STR_DSC_LEN(&lckptr->OwnerDsc) ? " " : "", /* the owner string has already been HTML-sanitised */ STR_DSC_LEN(&lckptr->OwnerDsc) ? STR_DSC_PTR(&lckptr->OwnerDsc) : "", STR_DSC_LEN(&lckptr->OwnerDsc) ? "\n" : ""); } } /*****************************************************************************/ /* Find a lock that matches the request header "Lock-token:". Return NULL if no locks. Return NOTPTR if lock(s) exist but the header token did not match (any one). Returns NOPTR if there is no Lock-Token: header. Returns the pointer to any matching lock. */ WEBDAV_LOCK* DavLockFindToken (REQUEST_STRUCT *rqptr) { char *cptr, *sptr; WEBDAV_META *mtaptr; WEBDAV_LOCK *lckptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockFindToken() !AZ", rqptr->rqHeader.WebDavLockTokenPtr ? rqptr->rqHeader.WebDavLockTokenPtr : "NONE"); tkptr = rqptr->WebDavTaskPtr; mtaptr = &tkptr->MetaData; /* if no header token */ if (!rqptr->rqHeader.WebDavLockTokenPtr) return (NOTPTR); /* if no locks */ if (!LIST_GET_HEAD(&mtaptr->LockList)) return (NULL); LIST_ITERATE (lckptr, &mtaptr->LockList) { sptr = lckptr->TokenString; cptr = rqptr->rqHeader.WebDavLockTokenPtr; if (*cptr == '<') cptr++; while (*cptr && *cptr != '>' && *sptr && *cptr == *sptr) { cptr++; sptr++; } /* if a match return the lock pointer */ if (*cptr == '>' && !*sptr) break; } if (!lckptr) lckptr = NOTPTR; if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "TOKEN match !AZ", lckptr && lckptr != NOTPTR ? lckptr->TokenString : "NONE"); return (lckptr); } /*****************************************************************************/ /* Search through the lock list for any lock matching the supplied scope. If found check the If: header data to determine whether the action is permitted. Return a pointer to lock if permitted, a NOTPTR if not permitted, or a NULL if no applicable lock. */ WEBDAV_LOCK* DavLockFindIf ( REQUEST_STRUCT *rqptr, int scope ) { char *cptr, *sptr; WEBDAV_LOCK *lckptr, *notptr; WEBDAV_META *mtaptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockFindIf() !UL", scope); tkptr = rqptr->WebDavTaskPtr; mtaptr = &tkptr->MetaData; notptr = NULL; LIST_ITERATE (lckptr, &mtaptr->LockList) { if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "!AZ !UL !UL", lckptr->TokenString, lckptr->Type, lckptr->Scope); if (lckptr->Type == WEBDAV_LOCK_TYPE_WRITE && lckptr->Scope == scope) { if (DavLockIfToken (rqptr, lckptr)) break; notptr = NOTPTR; } } if (!lckptr) lckptr = notptr; if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK search !AZ match !AZ", scope == WEBDAV_LOCK_SCOPE_EXCLUSIVE ? "EXCLUSIVE" : "SHARED", (!lckptr || lckptr == NOTPTR) ? "NONE" : lckptr->TokenString); return (lckptr); } /*****************************************************************************/ /* Asynchronously reads the meta-data (if any) and tests locking (if any) for permission to modify resource. Return success VMS status for modify permission or an error status to forbid. The meta-data read ASTs back this same function which then dispatches another AST to the originally supplied routine (and of course only the first argument is then valid). Performs this test for the supplied file specification and any parent directory(ies) depending on configuration. The configuration depth parameter is 0 (default) or 1 for files only, 2 for the parent directory, 3 for the grandparent directory, etc. If the resource is locked then 'TestLockedAt' is set to the collection level, where 1 is the resource itself, 2 is the parent, 3 the grandparent, etc., otherwise it is zero. */ DavLockTest ( REQUEST_STRUCT *rqptr, char *FileName, BOOL IsDirFile, REQUEST_AST AstFunction, unsigned long AstParam ) { char *cptr, *sptr, *zptr; WEBDAV_LOCK *lckptr; WEBDAV_META *mtaptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ tkptr = rqptr->WebDavTaskPtr; mtaptr = &tkptr->MetaData; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockTest() !&Z !UL !&S", tkptr->LockTestName, tkptr->TestLockDepth, mtaptr->VmsStatus); if (tkptr->TestLockDepth) { /***************/ /* assess test */ /***************/ /* meta-data has been read, check it */ if (mtaptr->VmsStatus == RMS$_FNF || mtaptr->VmsStatus == RMS$_DNF) tkptr->TestLockStatus = SS$_NORMAL; else if (VMSok (mtaptr->VmsStatus)) { tkptr->TestLockStatus = SS$_NORMAL; if (rqptr->rqHeader.WebDavIfPtr) { if (lckptr = DavLockFindIf (rqptr, WEBDAV_LOCK_SCOPE_EXCLUSIVE)) { if (lckptr == NOTPTR) { tkptr->TestLockStatus = SS$_ACCONFLICT; tkptr->TestLockedAt = tkptr->TestLockDepth; } } } else if (rqptr->rqHeader.WebDavLockTokenPtr) { if (lckptr = DavLockFindToken (rqptr)) if (lckptr == NOTPTR) tkptr->TestLockStatus = SS$_ACCONFLICT; } else if (LIST_GET_HEAD(&mtaptr->LockList)) { /* the metadata contains a lock and so a conflict */ tkptr->TestLockStatus = SS$_ACCONFLICT; } } else tkptr->TestLockStatus = mtaptr->VmsStatus; if (VMSnok (tkptr->TestLockStatus) || tkptr->TestLockDepth > WebDavLockCollectionDepth) { /*********************/ /* testing concluded */ /*********************/ SysDclAst (tkptr->TestLockAstFunction, tkptr->TestLockAstParam); tkptr->TestLockAstFunction = NULL; tkptr->TestLockAstParam = tkptr->TestLockDepth = 0; tkptr->LockTestName[0] = '\0'; return; } /******************************/ /* test parent directory(ies) */ /******************************/ /* create a parent directory out of the current file specification */ zptr = (sptr = tkptr->LockTestName) + sizeof(tkptr->LockTestName)-1; while (*sptr) sptr++; if (*(sptr-1) == ']') while (sptr > tkptr->LockTestName && (*sptr != '.' || SAME2(sptr-1,'^.')) && (*sptr != '[' || SAME2(sptr-1,'^['))) sptr--; else while (sptr > tkptr->LockTestName && (*sptr != ']' || SAME2(sptr-1,'^]')) && (*sptr != '[' || SAME2(sptr-1,'^['))) sptr--; if (*sptr == '[') { for (cptr = "[000000]"; *cptr && sptr < zptr; *sptr++ = *cptr++); /* reached the MFD so this will be the final test */ tkptr->TestLockDepth = WebDavLockCollectionDepth; } else *sptr++ = ']'; *sptr = '\0'; tkptr->TestLockDepth++; DavMetaRead (rqptr, mtaptr, tkptr->LockTestName, &DavLockTest, rqptr); return; } /****************/ /* initial call */ /****************/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "!&Z !&B", FileName, IsDirFile); zptr = (sptr = tkptr->LockTestName) + sizeof(tkptr->LockTestName)-1; for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; if (IsDirFile) { /* munge the ]NAME.DIR into a directory specification .NAME] */ while (sptr > tkptr->LockTestName && (*sptr != '.' || SAME2(sptr-1,'^.')) && (*sptr != ']' || SAME2(sptr-1,'^]'))) sptr--; SET2(sptr,']\0'); if (sptr > tkptr->LockTestName) sptr--; while (sptr > tkptr->LockTestName && (*sptr != ']' || SAME2(sptr-1,'^]'))) sptr--; if (sptr > tkptr->LockTestName) *sptr = '.'; } tkptr->TestLockAstFunction = AstFunction; tkptr->TestLockAstParam = AstParam; tkptr->TestLockedAt = 0; tkptr->TestLockDepth++; DavMetaRead (rqptr, mtaptr, tkptr->LockTestName, &DavLockTest, rqptr); } /*****************************************************************************/ /* When looking for the lock's token in the request's if: header. If found and not negated return true. If not found and negated returns true. Otherwise returns false. The function does not fully implement RFC2518 section 9.4. As WASD uses unqiue (UUID) lock tokens it should only have to check for the presence of one of these, and not need to match on a tagged list or etag. */ BOOL DavLockIfToken ( REQUEST_STRUCT *rqptr, WEBDAV_LOCK *lckptr ) { BOOL hit, notif; char *cptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockIfToken() !8XL !8XL", rqptr->rqHeader.WebDavIfPtr, lckptr); if (!lckptr) return (false); if (!(cptr = rqptr->rqHeader.WebDavIfPtr)) { if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "IF: none"); return (false); } while (*cptr == ' ' || *cptr == '\t') cptr++; if (*cptr == '(' && strsame (cptr, "(NOT ", 5)) notif = true; else notif = false; while (*cptr) { if (*cptr == '<' && MATCH10 (cptr, "TokenString, 45) && *(cptr+46) == '>') break; cptr++; } hit = (*cptr && !notif) || (!*cptr && notif); if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "IF: !&B !AZ!AZ>", hit, notif ? "(NOT <" : "<", lckptr->TokenString); return (hit); } /*****************************************************************************/ /* Generates a lock token UUID as described in RFC 2518, section 6.3. This function (substantially and practically) meets this requirement by combining a quadword time component, a function-internal counter, and the file-system specification for the resource. The UUID is then just the MD5 hash transformed into the required "urn:uuid:..." format. */ DavLockSetUrnUuidToken ( REQUEST_STRUCT *rqptr, WEBDAV_LOCK *lckptr ) { static unsigned long TokenCounter; static $DESCRIPTOR (TokenDsc, ""); static $DESCRIPTOR (TokenFaoDsc, "urn:uuid:!8XL-!4XL-!4XL-!4XL-!4XL!8XL\0"); unsigned char Hash16 [16]; char *cptr, *sptr, *zptr; char NameBuffer [1024]; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockSetUrnUuidToken()"); tkptr = rqptr->WebDavTaskPtr; /* initialize to a quasi-indeterminate value */ if (!TokenCounter++) TokenCounter = rqptr->rqTime.BeginTime64[0]; zptr = (sptr = NameBuffer) + sizeof(NameBuffer)-1; *(ULONGPTR)sptr = rqptr->rqTime.BeginTime64[0]; sptr += 4; *(ULONGPTR)sptr = rqptr->rqTime.BeginTime64[1]; sptr += 4; *(ULONGPTR)sptr = TokenCounter; sptr += 4; for (cptr = rqptr->ParseOds.ExpFileName; *cptr && sptr < zptr; *sptr++ = *cptr++); Md5Digest (NameBuffer, sptr-NameBuffer, &Hash16); TokenDsc.dsc$a_pointer = lckptr->TokenString; TokenDsc.dsc$w_length = sizeof(lckptr->TokenString); sys$fao (&TokenFaoDsc, 0, &TokenDsc, *(ULONGPTR)Hash16, *(USHORTPTR)(Hash16+4), *(USHORTPTR)(Hash16+6), *(USHORTPTR)(Hash16+8), *(USHORTPTR)(Hash16+10), *(ULONGPTR)(Hash16+12)); if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "TOKEN !AZ", TokenDsc.dsc$a_pointer); } /*****************************************************************************/