// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // L O C K M E T A . C P P // // HTTP 1.1/DAV 1.0 request handling via ISAPI // // // Copyright 1986-1997 Microsoft Corporation, All Rights Reserved // #include <_davprs.h> #include //_strspnp #include #include #include #include #include #include // LockDiscovery ------------------------------------------------------------- // SCODE ScAddInLockToken (CEmitterNode& en, LPCWSTR pwszLockToken) { // Outer nodes must be declared first // CEmitterNode enLToken; CEmitterNode enToken; SCODE sc = S_OK; WCHAR rgwsz[MAX_LOCKTOKEN_LENGTH]; LPCWSTR pwsz; // Make sure they give us a locktoken string. // Assert(pwszLockToken); // AND remove the quote marks (currently <>) // if (L'<' == pwszLockToken[0]) pwszLockToken++; pwsz = wcschr(pwszLockToken, L'>'); if (pwsz) { UINT cch = static_cast(pwsz - pwszLockToken); if (MAX_LOCKTOKEN_LENGTH - 1 < cch) { sc = E_FAIL; goto ret; } Assert(MAX_LOCKTOKEN_LENGTH > cch); memcpy(rgwsz, pwszLockToken, cch * sizeof(WCHAR)); rgwsz[cch] = L'\0'; pwszLockToken = rgwsz; } // Create and add a locktoken node under activelock. // (locktoken node contains a single href node.) // sc = en.ScAddNode (gc_wszLockToken, enLToken); if (FAILED (sc)) goto ret; sc = enLToken.ScAddNode (gc_wszXML__Href, enToken, pwszLockToken); if (FAILED (sc)) goto ret; ret: return sc; } // ======================================================================== // // ScBuildLockDiscovery // // Takes an emitter and an already-constructed lockdiscovery node, // and adds an activelock node under it. // May be called multiple times -- each call will add a new activelock // node under the lockdiscovery node in en. // SCODE ScBuildLockDiscovery (CXMLEmitter& emitter, CEmitterNode& en, LPCWSTR pwszLockToken, LPCWSTR pwszLockType, LPCWSTR pwszLockScope, BOOL fRollback, BOOL fDepthInfinity, DWORD dwTimeout, LPCWSTR pwszOwnerComment, LPCWSTR pwszSubType) { CEmitterNode enActive; SCODE sc = S_OK; WCHAR wsz[50]; // Zero is an invalid timeout. // Assert(dwTimeout); // Add in the 'DAV:activelock' node // sc = en.ScAddNode (gc_wszLockActive, enActive); if (FAILED (sc)) goto ret; // Create a node for the locktype. // { CEmitterNode enLType; sc = enActive.ScAddNode (gc_wszLockType, enLType); if (FAILED (sc)) goto ret; { CEmitterNode enType; sc = enLType.ScAddNode (pwszLockType, enType); if (FAILED (sc)) goto ret; if (pwszSubType) { CEmitterNode enSubLType; sc = enType.ScAddNode (pwszSubType, enSubLType); if (FAILED (sc)) goto ret; } } } // Create a node for the lockscope // { CEmitterNode enLScope; sc = enActive.ScAddNode (gc_wszLockScope, enLScope); if (FAILED (sc)) goto ret; { CEmitterNode enScope; sc = enLScope.ScAddNode (pwszLockScope, enScope); if (FAILED (sc)) goto ret; } } // Create a node for the owner. The comment is well contructed XML already // if (pwszOwnerComment) { sc = enActive.Pxn()->ScSetFormatedXML (pwszOwnerComment, static_cast(wcslen(pwszOwnerComment))); if (FAILED (sc)) goto ret; } // If this is a rollback lock... // if (fRollback) { CEmitterNode enRollback; sc = enActive.ScAddNode (gc_wszLockRollback, enRollback); if (FAILED (sc)) goto ret; } // Add in the lock token // sc = ScAddInLockToken (enActive, pwszLockToken); if (FAILED (sc)) goto ret; // Add an appropriate depth node. // { CEmitterNode enDepth; if (fDepthInfinity) { sc = enActive.ScAddNode (gc_wszLockDepth, enDepth, gc_wszInfinity); if (FAILED (sc)) goto ret; } else { sc = enActive.ScAddNode (gc_wszLockDepth, enDepth, gc_wsz0); if (FAILED (sc)) goto ret; } } // Finally, create and add a timeout node // { CEmitterNode enTimeout; wsprintfW (wsz, L"Second-%d", dwTimeout); sc = enActive.ScAddNode (gc_wszLockTimeout, enTimeout, wsz); if (FAILED (sc)) goto ret; } ret: return sc; } // ======================================================================== // // Lock utility functions // //$REVIEW: This should really be common impl code. Move to _davcom later. // // ------------------------------------------------------------------------ // // FGetLockTimeout // // Fetches and parses an incoming Time-Out header on the request. // Returns FALSE if an invalid option was encountered. // Returns TRUE with *pdwSeconds=gc_cSecondsDefaultLock // if NO Time-Out header was present. // BOOL FGetLockTimeout (LPMETHUTIL pmu, DWORD * pdwSeconds, DWORD dwMaxOverride) { LPCWSTR pwsz; DWORD dwMax = gc_cSecondsMaxLock; Assert (pmu); Assert (pdwSeconds); *pdwSeconds = gc_cSecondsDefaultLock; // If there is NO Time-Out header, leave our timeout set to the default, // which was set at construction time. // NOTE: It IS valid to have NO Time-Out header. Just use the defaults. // pwsz = pmu->LpwszGetRequestHeader (gc_szTimeout, FALSE); if (!pwsz) { LockTrace ("Dav: No Timeout header found.\n"); goto ret; } // Skip any initial whitespace. // pwsz = _wcsspnp(pwsz, gc_wszLWS); if (!pwsz) { LockTrace ("Dav: No params found in LOCK Time-Out header.\n"); return FALSE; } Assert(pwsz); // Check for a new-style timeout header. // // Load a header iterator -- there could be multiple values here. // { HDRITER_W hdr(pwsz); pwsz = hdr.PszNext(); if (!pwsz) { // No data found. That's an error. // return FALSE; } if (dwMaxOverride) dwMax = dwMaxOverride; while (pwsz) { // Loop until we find an acceptable time. // (Ignore any header values we don't understand.) // If no acceptable time is found, it's okay. // dwSeconds stays zero, and return TRUE. // if (!_wcsnicmp (gc_wszSecondDash, pwsz, gc_cchSecondDash)) { DWORD dwSeconds; pwsz += gc_cchSecondDash; if (!*pwsz) return FALSE; dwSeconds = _wtol(pwsz); if (dwSeconds > dwMax) { // Remember that they asked for something huge. // *pdwSeconds = dwMax; } else { // We found a request that we'll grant. // Set it and stop looping. // *pdwSeconds = dwSeconds; break; } } else if (!_wcsnicmp (gc_wszInfinite, pwsz, gc_cchInfinite)) { // We don't yet handle infinite timeouts. // Remember that they asked for something huge. // Skip to the next token. // *pdwSeconds = dwMax; } // else skip to next token // (ignore unrecognized tokens). // pwsz = hdr.PszNext(); } // elihw } ret: //$HACK:ROSEBUD_OFFICE9_TIMEOUT_HACK // For the bug where rosebud waits until the last second // before issueing the refresh. Need to filter out this check with // the user agent string. The hack is to increase the timeout // by 30 seconds and return the actual timeout. // if (pmu->FIsOffice9Request()) { *pdwSeconds += gc_dwSecondsHackTimeoutForRosebud; } //$HACK:END:ROSEBUD_OFFICE9_TIMEOUT_HACK return TRUE; }