1
2
3
4
5 package webdav
6
7 import (
8 "container/heap"
9 "errors"
10 "strconv"
11 "strings"
12 "sync"
13 "time"
14 )
15
16 var (
17
18 ErrConfirmationFailed = errors.New("webdav: confirmation failed")
19
20 ErrForbidden = errors.New("webdav: forbidden")
21
22 ErrLocked = errors.New("webdav: locked")
23
24 ErrNoSuchLock = errors.New("webdav: no such lock")
25 )
26
27
28
29 type Condition struct {
30 Not bool
31 Token string
32 ETag string
33 }
34
35
36
37
38 type LockSystem interface {
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 Confirm(now time.Time, name0, name1 string, conditions ...Condition) (release func(), err error)
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 Create(now time.Time, details LockDetails) (token string, err error)
70
71
72
73
74
75
76
77
78
79
80 Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error)
81
82
83
84
85
86
87
88
89
90
91
92
93 Unlock(now time.Time, token string) error
94 }
95
96
97 type LockDetails struct {
98
99
100 Root string
101
102 Duration time.Duration
103
104
105
106
107
108 OwnerXML string
109
110
111 ZeroDepth bool
112 }
113
114
115 func NewMemLS() LockSystem {
116 return &memLS{
117 byName: make(map[string]*memLSNode),
118 byToken: make(map[string]*memLSNode),
119 gen: uint64(time.Now().Unix()),
120 }
121 }
122
123 type memLS struct {
124 mu sync.Mutex
125 byName map[string]*memLSNode
126 byToken map[string]*memLSNode
127 gen uint64
128
129
130 byExpiry byExpiry
131 }
132
133 func (m *memLS) nextToken() string {
134 m.gen++
135 return strconv.FormatUint(m.gen, 10)
136 }
137
138 func (m *memLS) collectExpiredNodes(now time.Time) {
139 for len(m.byExpiry) > 0 {
140 if now.Before(m.byExpiry[0].expiry) {
141 break
142 }
143 m.remove(m.byExpiry[0])
144 }
145 }
146
147 func (m *memLS) Confirm(now time.Time, name0, name1 string, conditions ...Condition) (func(), error) {
148 m.mu.Lock()
149 defer m.mu.Unlock()
150 m.collectExpiredNodes(now)
151
152 var n0, n1 *memLSNode
153 if name0 != "" {
154 if n0 = m.lookup(slashClean(name0), conditions...); n0 == nil {
155 return nil, ErrConfirmationFailed
156 }
157 }
158 if name1 != "" {
159 if n1 = m.lookup(slashClean(name1), conditions...); n1 == nil {
160 return nil, ErrConfirmationFailed
161 }
162 }
163
164
165 if n1 == n0 {
166 n1 = nil
167 }
168
169 if n0 != nil {
170 m.hold(n0)
171 }
172 if n1 != nil {
173 m.hold(n1)
174 }
175 return func() {
176 m.mu.Lock()
177 defer m.mu.Unlock()
178 if n1 != nil {
179 m.unhold(n1)
180 }
181 if n0 != nil {
182 m.unhold(n0)
183 }
184 }, nil
185 }
186
187
188
189
190
191
192 func (m *memLS) lookup(name string, conditions ...Condition) (n *memLSNode) {
193
194 for _, c := range conditions {
195 n = m.byToken[c.Token]
196 if n == nil || n.held {
197 continue
198 }
199 if name == n.details.Root {
200 return n
201 }
202 if n.details.ZeroDepth {
203 continue
204 }
205 if n.details.Root == "/" || strings.HasPrefix(name, n.details.Root+"/") {
206 return n
207 }
208 }
209 return nil
210 }
211
212 func (m *memLS) hold(n *memLSNode) {
213 if n.held {
214 panic("webdav: memLS inconsistent held state")
215 }
216 n.held = true
217 if n.details.Duration >= 0 && n.byExpiryIndex >= 0 {
218 heap.Remove(&m.byExpiry, n.byExpiryIndex)
219 }
220 }
221
222 func (m *memLS) unhold(n *memLSNode) {
223 if !n.held {
224 panic("webdav: memLS inconsistent held state")
225 }
226 n.held = false
227 if n.details.Duration >= 0 {
228 heap.Push(&m.byExpiry, n)
229 }
230 }
231
232 func (m *memLS) Create(now time.Time, details LockDetails) (string, error) {
233 m.mu.Lock()
234 defer m.mu.Unlock()
235 m.collectExpiredNodes(now)
236 details.Root = slashClean(details.Root)
237
238 if !m.canCreate(details.Root, details.ZeroDepth) {
239 return "", ErrLocked
240 }
241 n := m.create(details.Root)
242 n.token = m.nextToken()
243 m.byToken[n.token] = n
244 n.details = details
245 if n.details.Duration >= 0 {
246 n.expiry = now.Add(n.details.Duration)
247 heap.Push(&m.byExpiry, n)
248 }
249 return n.token, nil
250 }
251
252 func (m *memLS) Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error) {
253 m.mu.Lock()
254 defer m.mu.Unlock()
255 m.collectExpiredNodes(now)
256
257 n := m.byToken[token]
258 if n == nil {
259 return LockDetails{}, ErrNoSuchLock
260 }
261 if n.held {
262 return LockDetails{}, ErrLocked
263 }
264 if n.byExpiryIndex >= 0 {
265 heap.Remove(&m.byExpiry, n.byExpiryIndex)
266 }
267 n.details.Duration = duration
268 if n.details.Duration >= 0 {
269 n.expiry = now.Add(n.details.Duration)
270 heap.Push(&m.byExpiry, n)
271 }
272 return n.details, nil
273 }
274
275 func (m *memLS) Unlock(now time.Time, token string) error {
276 m.mu.Lock()
277 defer m.mu.Unlock()
278 m.collectExpiredNodes(now)
279
280 n := m.byToken[token]
281 if n == nil {
282 return ErrNoSuchLock
283 }
284 if n.held {
285 return ErrLocked
286 }
287 m.remove(n)
288 return nil
289 }
290
291 func (m *memLS) canCreate(name string, zeroDepth bool) bool {
292 return walkToRoot(name, func(name0 string, first bool) bool {
293 n := m.byName[name0]
294 if n == nil {
295 return true
296 }
297 if first {
298 if n.token != "" {
299
300 return false
301 }
302 if !zeroDepth {
303
304
305 return false
306 }
307 } else if n.token != "" && !n.details.ZeroDepth {
308
309 return false
310 }
311 return true
312 })
313 }
314
315 func (m *memLS) create(name string) (ret *memLSNode) {
316 walkToRoot(name, func(name0 string, first bool) bool {
317 n := m.byName[name0]
318 if n == nil {
319 n = &memLSNode{
320 details: LockDetails{
321 Root: name0,
322 },
323 byExpiryIndex: -1,
324 }
325 m.byName[name0] = n
326 }
327 n.refCount++
328 if first {
329 ret = n
330 }
331 return true
332 })
333 return ret
334 }
335
336 func (m *memLS) remove(n *memLSNode) {
337 delete(m.byToken, n.token)
338 n.token = ""
339 walkToRoot(n.details.Root, func(name0 string, first bool) bool {
340 x := m.byName[name0]
341 x.refCount--
342 if x.refCount == 0 {
343 delete(m.byName, name0)
344 }
345 return true
346 })
347 if n.byExpiryIndex >= 0 {
348 heap.Remove(&m.byExpiry, n.byExpiryIndex)
349 }
350 }
351
352 func walkToRoot(name string, f func(name0 string, first bool) bool) bool {
353 for first := true; ; first = false {
354 if !f(name, first) {
355 return false
356 }
357 if name == "/" {
358 break
359 }
360 name = name[:strings.LastIndex(name, "/")]
361 if name == "" {
362 name = "/"
363 }
364 }
365 return true
366 }
367
368 type memLSNode struct {
369
370
371 details LockDetails
372
373
374 token string
375
376 refCount int
377
378 expiry time.Time
379
380
381 byExpiryIndex int
382
383 held bool
384 }
385
386 type byExpiry []*memLSNode
387
388 func (b *byExpiry) Len() int {
389 return len(*b)
390 }
391
392 func (b *byExpiry) Less(i, j int) bool {
393 return (*b)[i].expiry.Before((*b)[j].expiry)
394 }
395
396 func (b *byExpiry) Swap(i, j int) {
397 (*b)[i], (*b)[j] = (*b)[j], (*b)[i]
398 (*b)[i].byExpiryIndex = i
399 (*b)[j].byExpiryIndex = j
400 }
401
402 func (b *byExpiry) Push(x interface{}) {
403 n := x.(*memLSNode)
404 n.byExpiryIndex = len(*b)
405 *b = append(*b, n)
406 }
407
408 func (b *byExpiry) Pop() interface{} {
409 i := len(*b) - 1
410 n := (*b)[i]
411 (*b)[i] = nil
412 n.byExpiryIndex = -1
413 *b = (*b)[:i]
414 return n
415 }
416
417 const infiniteTimeout = -1
418
419
420
421 func parseTimeout(s string) (time.Duration, error) {
422 if s == "" {
423 return infiniteTimeout, nil
424 }
425 if i := strings.IndexByte(s, ','); i >= 0 {
426 s = s[:i]
427 }
428 s = strings.TrimSpace(s)
429 if s == "Infinite" {
430 return infiniteTimeout, nil
431 }
432 const pre = "Second-"
433 if !strings.HasPrefix(s, pre) {
434 return 0, errInvalidTimeout
435 }
436 s = s[len(pre):]
437 if s == "" || s[0] < '0' || '9' < s[0] {
438 return 0, errInvalidTimeout
439 }
440 n, err := strconv.ParseInt(s, 10, 64)
441 if err != nil || 1<<32-1 < n {
442 return 0, errInvalidTimeout
443 }
444 return time.Duration(n) * time.Second, nil
445 }
446
View as plain text