You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
349 lines
7.3 KiB
349 lines
7.3 KiB
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
//
|
|
// 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 <tchar.h> //_strspnp
|
|
|
|
#include <gencache.h>
|
|
#include <sz.h>
|
|
#include <xemit.h>
|
|
#include <xlock.h>
|
|
#include <statetok.h>
|
|
#include <nonimpl.h>
|
|
|
|
// 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<UINT>(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<UINT>(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;
|
|
}
|
|
|