/*****************************************************************************/ /* 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 --------------- 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; /*****************************************************************************/ /* 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 *cptr; 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; } } } else if (VMSnok (mtaptr->VmsStatus)) { /* blanket 412 for any locking failure */ DavWebResponse (rqptr, 412, mtaptr->VmsStatus, NULL, FI_LI); DavWebEnd (rqptr); return; } /* the list head will be the most recently added/refreshed lock */ lckptr = LIST_GET_HEAD(&mtaptr->LockList); if (!lckptr) { ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI); DavWebResponse (rqptr, 500, SS$_BUGCHECK, NULL, FI_LI); DavWebEnd (rqptr); return; } if (!(rqptr->rqHeader.WebDavIfPtr && rqptr->WebDavTaskPtr->IfLockPtr)) { /* lock token response is only supplied with fresh locks not refreshes */ strcpy (LockToken+12, lckptr->TokenString); strcat (LockToken+12, "\r\n"); cptr = LockToken; } else cptr = ""; rqptr->rqResponse.NoGzip = true; ResponseHeader (rqptr, tkptr->ResponseStatusCode, "text/xml; charset=\"utf-8\"", -1, NULL, cptr); FaoToNet (rqptr, "\n\ \n\ \n\ \n\ \n\ \n\ !AZ\n\ !AZ!AZ!AZ\n\ \n\ !AZ\n\ \n\ !AZ\n\ \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" : "", STR_DSC_PTR(&lckptr->OwnerDsc), lckptr->TimeoutString); 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()"); 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); } DavWebDateTimeTo3339 (ExpiresDateTime, lckptr->ExpiresTime64); FaoToBuffer (lckptr->ExpiresString, sizeof(lckptr->ExpiresString), NULL, "!AZ !20%D", ExpiresDateTime, &lckptr->ExpiresTime64); 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()"); 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\"\n"); else FaoToBuffer (wdptr, -1, NULL, "timeout=\"Second-!UL\"\n", lckptr->Timeout); FaoToBuffer (wdptr, -1, NULL, "expires=\"!AZ\">\n", lckptr->ExpiresString); StrDscBuild (wdptr, NULL, ""); StrDscBuild (wdptr, &lckptr->OwnerDsc, NULL); StrDscBuild (wdptr, NULL, "\n"); StrDscBuild (wdptr, NULL, "\n"); } /*****************************************************************************/ /* 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\ \n\ !AZ\n\ \n\ !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" : "", STR_DSC_PTR(&lckptr->OwnerDsc), lckptr->TimeoutString); } } /*****************************************************************************/ /* Find a lock that matches the request header "Lock-token:". */ WEBDAV_LOCK* DavLockFindToken (REQUEST_STRUCT *rqptr) { char *cptr, *sptr, *tptr; WEBDAV_LOCK *lckptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockFindToken() !AZ", rqptr->rqHeader.WebDavLockTokenPtr ? rqptr->rqHeader.WebDavLockTokenPtr : "NONE"); /* if a matching token was found during request "If:" processing */ if (rqptr->rqHeader.WebDavIfPtr && rqptr->WebDavTaskPtr->IfLockPtr) return (rqptr->WebDavTaskPtr->IfLockPtr); if (!rqptr->rqHeader.WebDavLockTokenPtr) return (NULL); for (tptr = rqptr->rqHeader.WebDavLockTokenPtr; !MATCH8 (tptr, "urn:uuid:"); tptr++); if (!*tptr) return (NULL); LIST_ITERATE (lckptr, &rqptr->WebDavTaskPtr->MetaData.LockList) { cptr = tptr; sptr = lckptr->TokenString; while (*cptr && *cptr != '>' && *sptr && *cptr == *sptr) { cptr++; sptr++; } /* if a match return the lock pointer */ if (*cptr == '>' && !*sptr) break; } if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "found !AZ", lckptr ? lckptr->TokenString : "NONE"); 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_META *mtaptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ tkptr = rqptr->WebDavTaskPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockTest() !&Z !UL", tkptr->LockTestName, tkptr->TestLockDepth); mtaptr = &tkptr->MetaData; 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 = DavLockTestMeta (rqptr); if (VMSnok (tkptr->TestLockStatus)) tkptr->TestLockedAt = tkptr->TestLockDepth; } 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'); } tkptr->TestLockAstFunction = AstFunction; tkptr->TestLockAstParam = AstParam; tkptr->TestLockedAt = 0; tkptr->TestLockDepth++; DavMetaRead (rqptr, mtaptr, tkptr->LockTestName, &DavLockTest, rqptr); } /*****************************************************************************/ /* Is the meta-data allowed to be changed (i.e. not locked)? Return an indicative VMS status. SS$_NORMAL indicates not locked. */ int DavLockTestMeta (REQUEST_STRUCT *rqptr) { int status, IfStatus; unsigned long ScratchTime64 [2]; char *cptr, *sptr; WEBDAV_LOCK *lckptr; WEBDAV_META *mtaptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavLockTestMeta()"); tkptr = rqptr->WebDavTaskPtr; mtaptr = &tkptr->MetaData; /* if allowed by the "If:" header */ if (IfStatus = DavWebIf (rqptr)) if (VMSnok (IfStatus)) return (SS$_ABORT); if (!(lckptr = tkptr->IfLockPtr)) { /* no lock matching request "If:" header */ lckptr = LIST_GET_HEAD(&mtaptr->LockList); /* if no locks at all */ if (!lckptr) return (SS$_NORMAL); /* look for an exclusive lock */ while (lckptr) { /* can only be one exclusive lock at a time */ if (lckptr->Scope == WEBDAV_LOCK_SCOPE_EXCLUSIVE) break; lckptr = LIST_GET_NEXT (&lckptr->ListEntry); } if (!lckptr) { /* no exclusive locks found */ if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK none exclusive"); return (SS$_NORMAL); } /* now drop thru to evaluate the exclusive lock */ } if (lckptr->Type != WEBDAV_LOCK_TYPE_WRITE) { /* WebDAV only knows about write locks at this stage */ if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK type NOT write"); return (SS$_NORMAL); } if (lckptr->Scope != WEBDAV_LOCK_SCOPE_EXCLUSIVE) { /* it may be a write lock but it's not exclusive */ if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK scope NOT exclusive"); return (SS$_NORMAL); } if (lckptr->Timeout) { /* a negative time indicates the timeout has expired */ status = lib$sub_times (&lckptr->ExpiresTime64, &HttpdTime64, &ScratchTime64); if (status == LIB$_NEGTIM) { if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK timeout !20%D expired", lckptr->ExpiresTime64); return (SS$_NORMAL); } if (VMSnok (status)) { /* note any error and allow the resource to be modified */ ErrorExitVmsStatus (status, ErrorSanityCheck, FI_LI); return (SS$_NORMAL); } } if (rqptr->rqHeader.WebDavLockTokenPtr) { cptr = strstr (rqptr->rqHeader.WebDavLockTokenPtr, "urn:uuid:"); if (cptr) { sptr = lckptr->TokenString; while (*cptr && *sptr && *cptr == *sptr) { cptr++; sptr++; } if (*cptr == '>' && !*sptr) { /* return OK if a matching token */ if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK token match"); return (SS$_NORMAL); } } if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK token mismatch"); } if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK at !&?resource\rcollection\r(!UL) !&?overridden\renforced\r", tkptr->TestLockDepth == 1, tkptr->TestLockDepth, IfStatus); if (IfStatus) return (SS$_NORMAL); return (SS$_ABORT); } /*****************************************************************************/ /* 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; memcpy (sptr, &rqptr->rqTime.BeginTime64, 8); sptr += 8; memcpy (sptr, &TokenCounter, 4); sptr += 4; for (cptr = rqptr->ParseOds.ExpFileName; *cptr && sptr < zptr; *sptr++ = TOLO(*cptr++)); *sptr = '\0'; 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); } /*****************************************************************************/