123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- package webdav
- import (
- "container/heap"
- "errors"
- "strconv"
- "strings"
- "sync"
- "time"
- )
- var (
-
- ErrConfirmationFailed = errors.New("webdav: confirmation failed")
-
- ErrForbidden = errors.New("webdav: forbidden")
-
- ErrLocked = errors.New("webdav: locked")
-
- ErrNoSuchLock = errors.New("webdav: no such lock")
- )
- type Condition struct {
- Not bool
- Token string
- ETag string
- }
- type LockSystem interface {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Confirm(now time.Time, name0, name1 string, conditions ...Condition) (release func(), err error)
-
-
-
-
-
-
-
-
-
-
-
-
-
- Create(now time.Time, details LockDetails) (token string, err error)
-
-
-
-
-
-
-
-
-
- Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error)
-
-
-
-
-
-
-
-
-
-
-
- Unlock(now time.Time, token string) error
- }
- type LockDetails struct {
-
-
- Root string
-
- Duration time.Duration
-
-
-
-
-
- OwnerXML string
-
-
- ZeroDepth bool
- }
- func NewMemLS() LockSystem {
- return &memLS{
- byName: make(map[string]*memLSNode),
- byToken: make(map[string]*memLSNode),
- gen: uint64(time.Now().Unix()),
- }
- }
- type memLS struct {
- mu sync.Mutex
- byName map[string]*memLSNode
- byToken map[string]*memLSNode
- gen uint64
-
-
- byExpiry byExpiry
- }
- func (m *memLS) nextToken() string {
- m.gen++
- return strconv.FormatUint(m.gen, 10)
- }
- func (m *memLS) collectExpiredNodes(now time.Time) {
- for len(m.byExpiry) > 0 {
- if now.Before(m.byExpiry[0].expiry) {
- break
- }
- m.remove(m.byExpiry[0])
- }
- }
- func (m *memLS) Confirm(now time.Time, name0, name1 string, conditions ...Condition) (func(), error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.collectExpiredNodes(now)
- var n0, n1 *memLSNode
- if name0 != "" {
- if n0 = m.lookup(slashClean(name0), conditions...); n0 == nil {
- return nil, ErrConfirmationFailed
- }
- }
- if name1 != "" {
- if n1 = m.lookup(slashClean(name1), conditions...); n1 == nil {
- return nil, ErrConfirmationFailed
- }
- }
-
- if n1 == n0 {
- n1 = nil
- }
- if n0 != nil {
- m.hold(n0)
- }
- if n1 != nil {
- m.hold(n1)
- }
- return func() {
- m.mu.Lock()
- defer m.mu.Unlock()
- if n1 != nil {
- m.unhold(n1)
- }
- if n0 != nil {
- m.unhold(n0)
- }
- }, nil
- }
- func (m *memLS) lookup(name string, conditions ...Condition) (n *memLSNode) {
-
- for _, c := range conditions {
- n = m.byToken[c.Token]
- if n == nil || n.held {
- continue
- }
- if name == n.details.Root {
- return n
- }
- if n.details.ZeroDepth {
- continue
- }
- if n.details.Root == "/" || strings.HasPrefix(name, n.details.Root+"/") {
- return n
- }
- }
- return nil
- }
- func (m *memLS) hold(n *memLSNode) {
- if n.held {
- panic("webdav: memLS inconsistent held state")
- }
- n.held = true
- if n.details.Duration >= 0 && n.byExpiryIndex >= 0 {
- heap.Remove(&m.byExpiry, n.byExpiryIndex)
- }
- }
- func (m *memLS) unhold(n *memLSNode) {
- if !n.held {
- panic("webdav: memLS inconsistent held state")
- }
- n.held = false
- if n.details.Duration >= 0 {
- heap.Push(&m.byExpiry, n)
- }
- }
- func (m *memLS) Create(now time.Time, details LockDetails) (string, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.collectExpiredNodes(now)
- details.Root = slashClean(details.Root)
- if !m.canCreate(details.Root, details.ZeroDepth) {
- return "", ErrLocked
- }
- n := m.create(details.Root)
- n.token = m.nextToken()
- m.byToken[n.token] = n
- n.details = details
- if n.details.Duration >= 0 {
- n.expiry = now.Add(n.details.Duration)
- heap.Push(&m.byExpiry, n)
- }
- return n.token, nil
- }
- func (m *memLS) Refresh(now time.Time, token string, duration time.Duration) (LockDetails, error) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.collectExpiredNodes(now)
- n := m.byToken[token]
- if n == nil {
- return LockDetails{}, ErrNoSuchLock
- }
- if n.held {
- return LockDetails{}, ErrLocked
- }
- if n.byExpiryIndex >= 0 {
- heap.Remove(&m.byExpiry, n.byExpiryIndex)
- }
- n.details.Duration = duration
- if n.details.Duration >= 0 {
- n.expiry = now.Add(n.details.Duration)
- heap.Push(&m.byExpiry, n)
- }
- return n.details, nil
- }
- func (m *memLS) Unlock(now time.Time, token string) error {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.collectExpiredNodes(now)
- n := m.byToken[token]
- if n == nil {
- return ErrNoSuchLock
- }
- if n.held {
- return ErrLocked
- }
- m.remove(n)
- return nil
- }
- func (m *memLS) canCreate(name string, zeroDepth bool) bool {
- return walkToRoot(name, func(name0 string, first bool) bool {
- n := m.byName[name0]
- if n == nil {
- return true
- }
- if first {
- if n.token != "" {
-
- return false
- }
- if !zeroDepth {
-
-
- return false
- }
- } else if n.token != "" && !n.details.ZeroDepth {
-
- return false
- }
- return true
- })
- }
- func (m *memLS) create(name string) (ret *memLSNode) {
- walkToRoot(name, func(name0 string, first bool) bool {
- n := m.byName[name0]
- if n == nil {
- n = &memLSNode{
- details: LockDetails{
- Root: name0,
- },
- byExpiryIndex: -1,
- }
- m.byName[name0] = n
- }
- n.refCount++
- if first {
- ret = n
- }
- return true
- })
- return ret
- }
- func (m *memLS) remove(n *memLSNode) {
- delete(m.byToken, n.token)
- n.token = ""
- walkToRoot(n.details.Root, func(name0 string, first bool) bool {
- x := m.byName[name0]
- x.refCount--
- if x.refCount == 0 {
- delete(m.byName, name0)
- }
- return true
- })
- if n.byExpiryIndex >= 0 {
- heap.Remove(&m.byExpiry, n.byExpiryIndex)
- }
- }
- func walkToRoot(name string, f func(name0 string, first bool) bool) bool {
- for first := true; ; first = false {
- if !f(name, first) {
- return false
- }
- if name == "/" {
- break
- }
- name = name[:strings.LastIndex(name, "/")]
- if name == "" {
- name = "/"
- }
- }
- return true
- }
- type memLSNode struct {
-
-
- details LockDetails
-
-
- token string
-
- refCount int
-
- expiry time.Time
-
-
- byExpiryIndex int
-
- held bool
- }
- type byExpiry []*memLSNode
- func (b *byExpiry) Len() int {
- return len(*b)
- }
- func (b *byExpiry) Less(i, j int) bool {
- return (*b)[i].expiry.Before((*b)[j].expiry)
- }
- func (b *byExpiry) Swap(i, j int) {
- (*b)[i], (*b)[j] = (*b)[j], (*b)[i]
- (*b)[i].byExpiryIndex = i
- (*b)[j].byExpiryIndex = j
- }
- func (b *byExpiry) Push(x interface{}) {
- n := x.(*memLSNode)
- n.byExpiryIndex = len(*b)
- *b = append(*b, n)
- }
- func (b *byExpiry) Pop() interface{} {
- i := len(*b) - 1
- n := (*b)[i]
- (*b)[i] = nil
- n.byExpiryIndex = -1
- *b = (*b)[:i]
- return n
- }
- const infiniteTimeout = -1
- func parseTimeout(s string) (time.Duration, error) {
- if s == "" {
- return infiniteTimeout, nil
- }
- if i := strings.IndexByte(s, ','); i >= 0 {
- s = s[:i]
- }
- s = strings.TrimSpace(s)
- if s == "Infinite" {
- return infiniteTimeout, nil
- }
- const pre = "Second-"
- if !strings.HasPrefix(s, pre) {
- return 0, errInvalidTimeout
- }
- s = s[len(pre):]
- if s == "" || s[0] < '0' || '9' < s[0] {
- return 0, errInvalidTimeout
- }
- n, err := strconv.ParseInt(s, 10, 64)
- if err != nil || 1<<32-1 < n {
- return 0, errInvalidTimeout
- }
- return time.Duration(n) * time.Second, nil
- }
|