/*****************************************************************************/ /* 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 --------------- 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 ToUpperCase[]; extern unsigned long HttpdBinTime[], 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, WATCH_MOD_WEBDAV, "DavLockBegin()"); if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_RESPONSE)) WatchThis (rqptr, FI_LI, 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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 OpaqueLockToken [64] = "Lock-Token: "; int status; char *cptr; REQUEST_STRUCT *rqptr; WEBDAV_LOCK *lckptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; tkptr = rqptr->WebDavTaskPtr; if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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_CATEGORY(WATCH_WEBDAV)) if (VMSok (mtaptr->VmsStatus)) WatchThis (rqptr, FI_LI, WATCH_WEBDAV, "LOCK unmapped URL"); else WatchThis (rqptr, FI_LI, 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 necessary create unmapped URL empty resource */ if (VMSnok (OdsFileExists (NULL, rqptr->ParseOds.ExpFileName))) { 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 (OpaqueLockToken+12, lckptr->TokenString); strcat (OpaqueLockToken+12, "\r\n"); cptr = OpaqueLockToken; } 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, WATCH_MOD_WEBDAV, "DavUnLockBegin()"); if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_RESPONSE)) WatchThis (rqptr, FI_LI, 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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 TimeoutBinTime [2]; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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_CATEGORY(WATCH_RESPONSE)) WatchThis (rqptr, FI_LI, 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 */ TimeoutBinTime[0] = -10000000; TimeoutBinTime[1] = -1; status = lib$mult_delta_time (&expsecs, &TimeoutBinTime); if (VMSnok (status)) { ErrorNoticed (rqptr, status, NULL, FI_LI); return (500); } /* out in the future */ status = lib$add_times (&HttpdBinTime, &TimeoutBinTime, &lckptr->ExpiresBinTime); if (VMSnok (status)) { ErrorNoticed (rqptr, status, NULL, FI_LI); return (500); } DavWebDateTimeTo3339 (ExpiresDateTime, lckptr->ExpiresBinTime); FaoToBuffer (lckptr->ExpiresString, sizeof(lckptr->ExpiresString), NULL, "!AZ !20%D", ExpiresDateTime, &lckptr->ExpiresBinTime); 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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, "opaquelo"); /* "opaquelocktoken" */ 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 (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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 ) { WEBDAV_META *mtaptr; WEBDAV_TASK *tkptr; char *cptr, *sptr, *zptr; char FileSpec [ODS_MAX_FILE_NAME_LENGTH+1]; /*********/ /* begin */ /*********/ tkptr = rqptr->WebDavTaskPtr; if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, WATCH_MOD_WEBDAV, "DavLockTest() !UL", tkptr->TestLockDepth); mtaptr = &tkptr->MetaData; if (tkptr->TestLockDepth) { /***************/ /* assess test */ /***************/ /* meta-data has been read, check it */ if (mtaptr->VmsStatus == RMS$_FNF) 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; return; } } if (tkptr->TestLockDepth) { /******************************/ /* test parent directory(ies) */ /******************************/ /* create a parent directory out of the current file specification */ zptr = (sptr = FileSpec) + sizeof(FileSpec)-1; for (cptr = mtaptr->ReadMetaName; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr-- = '\0'; if (sptr > FileSpec && *sptr == ']' && !SAME2(sptr-1,'^]')) { /* directory specification */ while (sptr > FileSpec && (*sptr != '.' || SAME2(sptr-1,'^.')) && (*sptr != '[' || SAME2(sptr-1,'^['))) sptr--; if (*sptr == '.') *sptr++ = ']'; else if (*sptr == '[') { for (cptr = "[000000]"; *cptr && sptr < zptr; *sptr++ = *cptr++); /* reached the MFD so this will be the final test */ tkptr->TestLockDepth = WebDavLockCollectionDepth; } } else { /* file specification */ while (sptr > FileSpec && (*sptr != ']' || SAME2(sptr-1,'^]'))) sptr--; if (*sptr == ']') sptr++; } *sptr = '\0'; tkptr->TestLockDepth++; DavMetaRead (rqptr, mtaptr, FileSpec, &DavLockTest, rqptr); return; } /****************/ /* initial call */ /****************/ if (IsDirFile) { /* munge the ]NAME.DIR into a directory specification .NAME] */ zptr = (sptr = FileSpec) + sizeof(FileSpec)-1; for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr-- = '\0'; while (sptr > FileSpec && (*sptr != '.' || SAME2(sptr-1,'^.')) && (*sptr != ']' || SAME2(sptr-1,'^]'))) sptr--; if (!strcmp (sptr, ".DIR") || !strncmp (sptr, ".DIR;", 5)) { SET2(sptr--,']\0'); while (sptr > FileSpec && (*sptr != ']' || SAME2(sptr-1,'^]'))) sptr--; if (*sptr = ']') { *sptr = '.'; FileName = FileSpec; } } } tkptr->TestLockAstFunction = AstFunction; tkptr->TestLockAstParam = AstParam; tkptr->TestLockedAt = 0; tkptr->TestLockDepth++; DavMetaRead (rqptr, mtaptr, FileName, &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 ScratchBinTime [2]; char *cptr, *sptr; WEBDAV_LOCK *lckptr; WEBDAV_META *mtaptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, 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_CATEGORY(WATCH_WEBDAV)) WatchThis (rqptr, FI_LI, 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_CATEGORY(WATCH_WEBDAV)) WatchThis (rqptr, FI_LI, 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_CATEGORY(WATCH_WEBDAV)) WatchThis (rqptr, FI_LI, 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->ExpiresBinTime, &HttpdBinTime, &ScratchBinTime); if (status == LIB$_NEGTIM) { if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_WEBDAV)) WatchThis (rqptr, FI_LI, WATCH_WEBDAV, "LOCK timeout !20%D expired", lckptr->ExpiresBinTime); 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, "opaquelocktoken:"); 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_CATEGORY(WATCH_WEBDAV)) WatchThis (rqptr, FI_LI, WATCH_WEBDAV, "LOCK token match"); return (SS$_NORMAL); } } if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_WEBDAV)) WatchThis (rqptr, FI_LI, WATCH_WEBDAV, "LOCK token mismatch"); } if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_WEBDAV)) WatchThis (rqptr, FI_LI, 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: Lock token URIs MUST be unique across all resources for all time. This uniqueness constraint allows lock tokens to be submitted across resources and servers without fear of confusion. This specification provides a lock token URI scheme called opaquelocktoken that meets the uniqueness requirements. However resources are free to return any URI scheme so long as it meets the uniqueness requirements. 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 32 hexadecimal digits. */ DavLockSetToken ( REQUEST_STRUCT *rqptr, WEBDAV_LOCK *lckptr ) { static unsigned long TokenCounter; static $DESCRIPTOR (TokenDsc, ""); static $DESCRIPTOR (TokenFaoDsc, "opaquelocktoken:!8XL!8XL!8XL!8XL\0"); unsigned long Hash4 [4]; char *cptr, *sptr, *zptr; char NameBuffer [1024]; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHING(rqptr) && WATCH_MODULE(WATCH_MOD_WEBDAV)) WatchThis (rqptr, FI_LI, WATCH_MOD_WEBDAV, "DavLockSetToken()"); tkptr = rqptr->WebDavTaskPtr; /* initialize to a quasi-indeterminate value */ if (!TokenCounter++) TokenCounter = rqptr->rqTime.Vms64bit[0]; zptr = (sptr = NameBuffer) + sizeof(NameBuffer)-1; memcpy (sptr, &rqptr->rqTime.Vms64bit, 8); sptr += 8; memcpy (sptr, &TokenCounter, 4); sptr += 4; for (cptr = rqptr->ParseOds.ExpFileName; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; Md5Digest (NameBuffer, sptr-NameBuffer, &Hash4); TokenDsc.dsc$a_pointer = lckptr->TokenString; TokenDsc.dsc$w_length = sizeof(lckptr->TokenString); sys$fao (&TokenFaoDsc, 0, &TokenDsc, Hash4[0], Hash4[1], Hash4[2], Hash4[3]); if (WATCHING(rqptr) && WATCH_CATEGORY(WATCH_WEBDAV)) WatchThis (rqptr, FI_LI, WATCH_WEBDAV, "TOKEN !AZ", TokenDsc.dsc$a_pointer); } /*****************************************************************************/