/* * S T A T E T O K. H * * Sources implementation of DAV-Lock common definitions. * * Copyright 1986-1997 Microsoft Corporation, All Rights Reserved */ /* * This file contains the definitions used for parsing state token * relared headers. * */ #ifndef __STATETOK_H__ #define __STATETOK_H__ // Current max seconds = 1 day. // DEC_CONST INT gc_cSecondsMaxLock = 60 * 60 * 24; // Current default lock time out is 3 minutes // DEC_CONST INT gc_cSecondsDefaultLock = 60 * 3; //$REVIEW These flags are duplicated in lockmgr.h and statetok.h. before //$REVIEW this is addressed, to be safe, we make sure they match //$REVIEW Also inherit the excellent comments from the lockmgr.h regarding //$REVIEW how the flags should be defined when we merge. #define DAV_LOCKTYPE_ROLLBACK 0x08000000 #define DAV_LOCKTYPE_CHECKOUT 0x04000000 #define DAV_LOCKTYPE_TRANSACTION_GOP 0x00100000 #define DAV_LOCKTYPE_READWRITE (GENERIC_READ | GENERIC_WRITE) #define DAV_LOCKTYPE_FLAGS (GENERIC_READ | GENERIC_WRITE | DAV_LOCKTYPE_ROLLBACK | DAV_LOCKTYPE_CHECKOUT | DAV_LOCKTYPE_TRANSACTION_GOP) #define DAV_EXCLUSIVE_LOCK 0x01000000 #define DAV_SHARED_LOCK 0x02000000 #define DAV_LOCKSCOPE_LOCAL 0x04000000 #define DAV_LOCKSCOPE_FLAGS (DAV_EXCLUSIVE_LOCK | DAV_SHARED_LOCK | DAV_LOCKSCOPE_LOCAL) #define DAV_RECURSIVE_LOCK 0x00800000 #define DAV_LOCK_FLAGS (DAV_LOCKTYPE_FLAGS | DAV_RECURSIVE_LOCK | DAV_LOCKSCOPE_FLAGS) /* - IFITER - * * This is the parser copied from the original IF header processor * used in lockutil.cpp. Eventually lockutil.cpp shall use this * file since this file shall have only the common stuff sharable * between davex and davfs lock code. * * Comment format change to the style used in this file otherwise. * * */ // ======================================================================== // // class IFITER // // Built to parse the new If header. // // Format of the If header // If = "If" ":" ( 1*No-tag-list | 1*Tagged-list) // No-tag-list = List // Tagged-list = Resource 1*List // Resource = Coded-url // List = "(" 1*(["Not"](State-token | "[" entity-tag "]")) ")" // State-token = Coded-url // Coded-url = "<" URI ">" // // // NOTE: We are going to be lax about tagged/untagged lists. // If the first list is not tagged, but we find tagged lists later, // that's cool by me. // (Realize that there no problem switching from tagged to untagged -- // because that case cannot be detected & distinguished from another // list for the same URI! The only problem is if the first list is // untagged, and later there are tagged lists. That is a case that // *should*, by a perfectly tight reading of the spec, be a bad request. // I am treating it as perfectly valid until someone tells me that I have // to do the extra 1 bit of bookkeeping.) // // State machine for this class // It's a really simple state machine. // (NOTE that I'm calling statetokens and etags "tokens", and the // contents of a single set of parentheses a "list", just like above.) // // Three possible states: NONE, NAME, and LIST. // Starts in state NONE -- can accept a tag (URI) or a start of a list. // Moves to NAME if a tag (URI) is encountered. // Only a list can follow a tag (URI). // Moves to LIST when a list start (left paren) is encountered. // Moves back to NONE when a list end (right paren) is encountered. // // ------------------------------------------------------------------------ // enum FETCH_TOKEN_TYPE // These are the flags used in IFITER::PszNextToken. // There are two basic types of fetching: // o advance to next item of this type (xxx_NEW_xxx) // o fetch the next item & fail if the type does not match. // enum FETCH_TOKEN_TYPE { TOKEN_URI, // Fetch a URI, don't skip anything. TOKEN_NEW_URI, // Advance to the next URI, skipping stuff in between. TOKEN_START_LIST, // Fetch the next list item. Must be the starting list item. TOKEN_SAME_LIST, // Fetch the next internal item in this list. TOKEN_NEW_LIST, // Advance to the next start of a list, skipping past the // end of the current list if necessary. Don't skip uris. TOKEN_ANY_LIST, // NTRaid#244243 -- special for looking up locktokens // Fetch the next item for this same uri -- can cross lists, // but not uris. TOKEN_NONE, // Empty marker. }; class IFITER { private: enum STATE_TYPE { STATE_NONE, STATE_NAME, STATE_LIST, }; const LPCWSTR m_pwszHdr; LPCWSTR m_pwch; StringBuffer m_buf; // State bits STATE_TYPE m_state; BOOL m_fCurrentNot; // NOT IMPLEMENTED // IFITER& operator=( const IFITER& ); IFITER( const IFITER& ); public: IFITER (LPCWSTR pwsz=0) : m_pwszHdr(pwsz), m_pwch(pwsz), m_state(STATE_NONE), m_fCurrentNot(FALSE) { } ~IFITER() {} LPCWSTR PszNextToken (FETCH_TOKEN_TYPE type); BOOL FCurrentNot() const { return m_fCurrentNot; } void Restart() { m_pwch = m_pwszHdr; m_state = STATE_NONE; } }; /* - PwszSkipCodes - * Remove <> or [] tags around stuff. Useful for if: header * tags. also eliminates the LWS near to the delimiters. * * *pdwLen may be zero or the length of the string. If zero * the routine calculate the length using strlen. Wasteful, * if you already know the length. * * Returns the pointer to the first non-lws, non-delimiter. * dwLen shall be set to the actual number of chars, from the * first to the last char which is non-lws, non-delimiter when * we start looking from the end. Does not stick the null char * at the end. Do it yourself, if you need to, using dwLen. * */ LPCWSTR PwszSkipCodes(IN LPCWSTR pwszTagged, IN OUT DWORD *pdwLen); /* - CStateToken - * The state token is the lean string that we use to communicate * with the client. It is the external representation of a DAV lock * or any other kind of state information. * * State token is a quoted uri which is for the external world. * So we provide facilities to deal with this in this class. The < and * > are not useful for internal processing - so we hide this to our * customers - this will avoid copying to prepend the <. * * E-TAGs are special beasts and are just plain quoted strings surrounded * by [ and ]. * */ class CStateToken { public: // Common defintions which are public, also used privately! // typedef enum StateTokenType { TOKEN_NONE = 0, TOKEN_LOCK, TOKEN_TRANS, TOKEN_ETAG, TOKEN_RESTAG, } STATE_TOKEN_TYPE; // normally state tokens are about of this size // ie lock tokens. // enum { NORMAL_STATE_TOKEN_SIZE = 128 }; private: // Token buffer // LPWSTR m_pwszToken; // Allocated size of the current buffer. // DWORD m_cchBuf; // type of the token // STATE_TOKEN_TYPE m_tType; // Never implemented // CStateToken( const CStateToken& ); CStateToken& operator=( const CStateToken& ); public: CStateToken() : m_pwszToken(NULL), m_cchBuf(0), m_tType(TOKEN_NONE) { }; ~CStateToken() { if (NULL != m_pwszToken) ExFree(m_pwszToken); } // Plain token accepted here. // If the dwLen is zero, NULL terminated pszToken // is the token. If non zero, it gives actual // number of chars in the token. // Useful when we parse the if: header. // BOOL FSetToken(LPCWSTR pwszToken, BOOL fEtag, DWORD dwLen = 0); // Accessors to the token info // inline STATE_TOKEN_TYPE GetTokenType() const { return m_tType; } // TRUE if the lock tokens are equal. // BOOL FIsEqual(CStateToken *pstokRhs); // get a pointer to the token string // inline LPCWSTR WszGetToken() const { return m_pwszToken; } // Parses the state token as a lock token and // get the lock token information.. Note that our lock // tokens consist of a GUIID and a long long(int64). // The guid string must be long enough to hold a GUID // string (37 chars). // BOOL FGetLockTokenInfo(unsigned __int64 *pi64SeqNum, LPWSTR pwszGuid); }; /* - CStateMatchOp - * This class is used as the base class for doing * state match operations including e-tag * checks. Each implementation shall derive its own * ways to check the state of the resource. This way * the core parse code is shared between subsystems. * * Not multi-thread safe - create,use and delete in a * single thread. * */ class CStateMatchOp { private: // NOT IMPLEMENTED // CStateMatchOp( const CStateMatchOp& ); CStateMatchOp& operator=( const CStateMatchOp& ); protected: // Current token under investigation. // All derived classes can access it. // We do not pass this as the parameter. // CStateToken m_tokCurrent; friend class CIfHeadParser; // --------------------------------------------------------- // support API for the ifheader parser // set the current token // inline BOOL FSetToken(LPCWSTR pwszToken, BOOL fEtag) { return m_tokCurrent.FSetToken(pwszToken, fEtag); } // get the current token type // inline CStateToken::STATE_TOKEN_TYPE GetTokenType() const { return m_tokCurrent.GetTokenType(); } // return the storage path of the uri. Note that davex and davfs // has different implementations of this. // virtual SCODE ScGetResourcePath(LPCWSTR pwszUri, LPCWSTR * ppwszStoragePath) = 0; // check if the resource is locked by the lock specified // by the current lock token above. fRecusrive says if the // condition is to be applied to all the resources under the // given path. Believe me, lpwszPath can be NULL. And it is // NULL when the match condition is to be applied on the // first path given to HrApplyIf!. Why we do this: normally // we do lotsa processing on the method's resource before // we call the if-header parser. This processing generates // info like e-tags which can be used to do the state match // check here. So the parser needs to tell the match checker // that this is for the original uri and NULL is the indication // of that. // virtual SCODE ScMatchLockToken(LPCWSTR pwszPath, BOOL fRecursive) = 0; virtual SCODE ScMatchResTag(LPCWSTR pwszPath) = 0; virtual SCODE ScMatchTransactionToken(LPCWSTR pwszPath) = 0; // Checks if the resource is in the state specified by the // (e-tag) state token above. Parameters have same meaning as above. // virtual SCODE ScMatchETag(LPCWSTR pwszPath, BOOL fRecursive) = 0; // ----------------------------------------------------------- public: // Usual suspects of CTOR and DTOR // CStateMatchOp() { }; ~CStateMatchOp() { }; // Using this object as the match operator parse an if header. // This is used by all method impls. // SCODE ScParseIf(LPCWSTR pwszIfHeader, LPCWSTR rgpwszPaths[], DWORD cPaths, BOOL fRecur, SCODE * pSC); }; /* - FCompareSids - * compare two sids * */ inline BOOL FCompareSids(PSID pSidLeft, PSID pSidRight) { if ((NULL == pSidLeft) || (NULL == pSidRight)) return FALSE; // Assert the SID validity. // Assert(IsValidSid(pSidLeft)); Assert(IsValidSid(pSidRight)); return EqualSid(pSidLeft, pSidRight); } /* - FSeparator - * returns true if input is a path separator - used below * */ inline BOOL FSeparator(WCHAR wch) { return ((wch == L'\\') || (wch == L'/')); } /* - FIsChildPath - * compare two paths to check if the child is within the scope * of the parent. * * For non recursive match, the two paths must match exactly for * TRUE return. This is useful when tagged URIs within the IF header * is processed and we have a deep operation going on. The other place * this function is used is when we have a recursive lock and need to * find out if a path is locked by this lock. * */ inline BOOL FIsChildPath(LPCWSTR pwszPathParent, LPCWSTR pwszPathChild, BOOL fRecursive) { UINT cchParentLen; if ((NULL == pwszPathParent) || (NULL == pwszPathChild)) return FALSE; cchParentLen = static_cast(wcslen(pwszPathParent)); // If the parent path is not an initial substring // of child return fail immediately. // if ( 0 != _wcsnicmp(pwszPathChild, pwszPathParent, cchParentLen) ) { return FALSE; } // Parent indeed is the initial substring. // Check the next char of child (for NULL) to see // if they match exactly. This is an instand good condition. // if (L'\0' == pwszPathChild[cchParentLen]) { return TRUE; } // We still have hope for a match ONLY for recursive checks. // if (! fRecursive) { return FALSE; } else { // either parent or child need to have a separator // if ( FSeparator(pwszPathParent[cchParentLen-1]) || FSeparator(pwszPathChild[cchParentLen]) ) return TRUE; else return FALSE; } } #endif