WebDavStoreItemLock.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Xml;
  6. using WebDAVSharp.Server.Stores.Locks.Enums;
  7. using WebDAVSharp.Server.Stores.Locks.Interfaces;
  8. namespace WebDAVSharp.Server.Stores.Locks
  9. {
  10. /// <summary>
  11. /// This class provides the locking functionality.
  12. /// </summary>
  13. public class WebDavStoreItemLock : WebDavStoreItemLockBase
  14. {
  15. /// <summary>
  16. /// </summary>
  17. public Dictionary<string, List<IWebDavStoreItemLockInstance>> ObjectLocks = new Dictionary<string, List<IWebDavStoreItemLockInstance>>();
  18. /// <summary>
  19. /// </summary>
  20. /// <param name="storeItem"></param>
  21. public void CleanLocks(IWebDavStoreItem storeItem)
  22. {
  23. lock (ObjectLocks)
  24. {
  25. if (!ObjectLocks.ContainsKey(storeItem.ItemPath))
  26. return;
  27. foreach (
  28. IWebDavStoreItemLockInstance ilock in ObjectLocks[storeItem.ItemPath].ToList()
  29. .Where(ilock => ilock.ExpirationDate != null && (DateTime)ilock.ExpirationDate < DateTime.Now)
  30. )
  31. ObjectLocks[storeItem.ItemPath].Remove(ilock);
  32. }
  33. }
  34. /// <summary>
  35. /// This function will refresh an existing lock.
  36. /// </summary>
  37. /// <param name="storeItem">Target URI to the file or folder </param>
  38. /// <param name="locktoken">The token issued when the lock was established</param>
  39. /// <param name="requestedlocktimeout">The requested timeout</param>
  40. /// <param name="requestDocument">
  41. /// Output parameter, returns the Request document that was used when the lock was
  42. /// established.
  43. /// </param>
  44. /// <returns></returns>
  45. public override int RefreshLock(IWebDavStoreItem storeItem, Guid? locktoken, double? requestedlocktimeout, out XmlDocument requestDocument)
  46. {
  47. CleanLocks(storeItem);
  48. //Refreshing an existing lock
  49. //If a lock doesn't exist then lets just reply with a Precondition Failed.
  50. //412 (Precondition Failed), with 'lock-token-matches-request-uri' precondition code - The LOCK request was
  51. //made with an If header, indicating that the client wishes to refresh the given lock. However, the Request-URI
  52. //did not fall within the scope of the lock identified by the token. The lock may have a scope that does not
  53. //include the Request-URI, or the lock could have disappeared, or the token may be invalid.
  54. requestDocument = null;
  55. lock (ObjectLocks)
  56. {
  57. IWebDavStoreItemLockInstance ilock = ObjectLocks[storeItem.ItemPath].FirstOrDefault(d => (d.Token == locktoken));
  58. if (ilock == null)
  59. {
  60. return 412;
  61. }
  62. ilock.RefreshLock(requestedlocktimeout);
  63. requestDocument = ilock.RequestDocument;
  64. return (int)HttpStatusCode.OK;
  65. }
  66. }
  67. /// <summary>
  68. /// Locks the request Path.
  69. /// </summary>
  70. /// <param name="storeItem">URI to the item to be locked</param>
  71. /// <param name="lockscope">The lock Scope used for locking</param>
  72. /// <param name="locktype">The lock Type used for locking</param>
  73. /// <param name="lockowner">The owner of the lock</param>
  74. /// <param name="requestedlocktimeout">The requested timeout</param>
  75. /// <param name="locktoken">Out parameter, returns the issued token</param>
  76. /// <param name="requestDocument">the Request Document</param>
  77. /// <param name="depth">How deep to lock, 0,1, or infinity</param>
  78. /// <returns></returns>
  79. public override int Lock(IWebDavStoreItem storeItem, WebDavLockScope lockscope, WebDavLockType locktype, string lockowner,
  80. double? requestedlocktimeout, out Guid? locktoken, XmlDocument requestDocument, int depth)
  81. {
  82. CleanLocks(storeItem);
  83. locktoken = null;
  84. Guid tmpLockToken = Guid.NewGuid();
  85. lock (ObjectLocks)
  86. {
  87. /*
  88. The table below describes the behavior that occurs when a lock request is made on a resource.
  89. Current State Shared Lock OK Exclusive Lock OK
  90. None True True
  91. Shared Lock True False
  92. Exclusive Lock False False*
  93. Legend: True = lock may be granted. False = lock MUST NOT be granted. *=It is illegal for a principal to request the same lock twice.
  94. The current lock state of a resource is given in the leftmost column, and lock requests are listed in the first row. The intersection of a row and column gives the result of a lock request. For example, if a shared lock is held on a resource, and an exclusive lock is requested, the table entry is "false", indicating that the lock must not be granted.
  95. */
  96. //if ObjectLocks doesn't contain the path, then this is a new lock and regardless
  97. //of whether it is Exclusive or Shared it is successful.
  98. if (!ObjectLocks.ContainsKey(storeItem.ItemPath))
  99. {
  100. ObjectLocks.Add(storeItem.ItemPath, new List<IWebDavStoreItemLockInstance>());
  101. ObjectLocks[storeItem.ItemPath].Add(new WebDavStoreItemLockInstance(storeItem.ItemPath, lockscope, locktype, lockowner, requestedlocktimeout, tmpLockToken, requestDocument, depth, this));
  102. locktoken = tmpLockToken;
  103. return (int)HttpStatusCode.OK;
  104. }
  105. if (ObjectLocks[storeItem.ItemPath].Count == 0)
  106. {
  107. ObjectLocks[storeItem.ItemPath].Add(new WebDavStoreItemLockInstance(storeItem.ItemPath, lockscope, locktype, lockowner, requestedlocktimeout, tmpLockToken, requestDocument, depth, this));
  108. locktoken = tmpLockToken;
  109. return (int)HttpStatusCode.OK;
  110. }
  111. //The fact that ObjectLocks contains this URI means that there is already a lock on this object,
  112. //This means the lock fails because you can only have 1 exclusive lock.
  113. switch (lockscope)
  114. {
  115. case WebDavLockScope.Exclusive:
  116. return 423;
  117. case WebDavLockScope.Shared:
  118. if (ObjectLocks[storeItem.ItemPath].Any(itemLock => itemLock.LockScope == WebDavLockScope.Exclusive))
  119. {
  120. return 423;
  121. }
  122. break;
  123. }
  124. //If the scope is shared and all other locks on this uri are shared we are ok, otherwise we fail.
  125. //423 (Locked), potentially with 'no-conflicting-lock' precondition code -
  126. //There is already a lock on the resource that is not compatible with the
  127. //requested lock (see lock compatibility table above).
  128. //If it gets to here, then we are most likely creating another shared lock on the file.
  129. #region Create New Lock
  130. ObjectLocks[storeItem.ItemPath].Add(new WebDavStoreItemLockInstance(storeItem.ItemPath, lockscope, locktype, lockowner, requestedlocktimeout, tmpLockToken, requestDocument, depth, this));
  131. locktoken = tmpLockToken;
  132. #endregion Create New Lock
  133. return (int)HttpStatusCode.OK;
  134. }
  135. }
  136. /// <summary>
  137. /// Unlocks the URI passed if the token matches a lock token in use.
  138. /// </summary>
  139. /// <param name="storeItem">URI to resource</param>
  140. /// <param name="locktoken">Token used to lock.</param>
  141. /// <param name="owner">Owner.</param>
  142. /// <returns></returns>
  143. public override int UnLock(IWebDavStoreItem storeItem, Guid? locktoken, string owner)
  144. {
  145. CleanLocks(storeItem);
  146. if (locktoken == null)
  147. {
  148. return (int)HttpStatusCode.BadRequest;
  149. }
  150. lock (ObjectLocks)
  151. {
  152. if (!ObjectLocks.ContainsKey(storeItem.ItemPath))
  153. {
  154. return (int)HttpStatusCode.Conflict;
  155. }
  156. WebDavStoreItemLockInstance ilock = (WebDavStoreItemLockInstance)ObjectLocks[storeItem.ItemPath].FirstOrDefault(d => d.Token == locktoken && d.Owner == owner);
  157. if (ilock == null)
  158. return (int)HttpStatusCode.Conflict;
  159. //Remove the lock
  160. ObjectLocks[storeItem.ItemPath].Remove(ilock);
  161. //if there are no locks left of the uri then remove it from ObjectLocks.
  162. if (ObjectLocks[storeItem.ItemPath].Count == 0)
  163. ObjectLocks.Remove(storeItem.ItemPath);
  164. return (int)HttpStatusCode.NoContent;
  165. }
  166. }
  167. /// <summary>
  168. /// </summary>
  169. /// <param name="storeItem"></param>
  170. /// <returns></returns>
  171. public override List<IWebDavStoreItemLockInstance> GetLocks(IWebDavStoreItem storeItem)
  172. {
  173. lock (ObjectLocks)
  174. {
  175. return ObjectLocks.ContainsKey(storeItem.ItemPath) ? ObjectLocks[storeItem.ItemPath].ToList() : new List<IWebDavStoreItemLockInstance>();
  176. }
  177. }
  178. }
  179. }