| // Copyright 2014 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package webdav | 
 |  | 
 | // The If header is covered by Section 10.4. | 
 | // http://www.webdav.org/specs/rfc4918.html#HEADER_If | 
 |  | 
 | import ( | 
 | 	"strings" | 
 | ) | 
 |  | 
 | // ifHeader is a disjunction (OR) of ifLists. | 
 | type ifHeader struct { | 
 | 	lists []ifList | 
 | } | 
 |  | 
 | // ifList is a conjunction (AND) of Conditions, and an optional resource tag. | 
 | type ifList struct { | 
 | 	resourceTag string | 
 | 	conditions  []Condition | 
 | } | 
 |  | 
 | // parseIfHeader parses the "If: foo bar" HTTP header. The httpHeader string | 
 | // should omit the "If:" prefix and have any "\r\n"s collapsed to a " ", as is | 
 | // returned by req.Header.Get("If") for a http.Request req. | 
 | func parseIfHeader(httpHeader string) (h ifHeader, ok bool) { | 
 | 	s := strings.TrimSpace(httpHeader) | 
 | 	switch tokenType, _, _ := lex(s); tokenType { | 
 | 	case '(': | 
 | 		return parseNoTagLists(s) | 
 | 	case angleTokenType: | 
 | 		return parseTaggedLists(s) | 
 | 	default: | 
 | 		return ifHeader{}, false | 
 | 	} | 
 | } | 
 |  | 
 | func parseNoTagLists(s string) (h ifHeader, ok bool) { | 
 | 	for { | 
 | 		l, remaining, ok := parseList(s) | 
 | 		if !ok { | 
 | 			return ifHeader{}, false | 
 | 		} | 
 | 		h.lists = append(h.lists, l) | 
 | 		if remaining == "" { | 
 | 			return h, true | 
 | 		} | 
 | 		s = remaining | 
 | 	} | 
 | } | 
 |  | 
 | func parseTaggedLists(s string) (h ifHeader, ok bool) { | 
 | 	resourceTag, n := "", 0 | 
 | 	for first := true; ; first = false { | 
 | 		tokenType, tokenStr, remaining := lex(s) | 
 | 		switch tokenType { | 
 | 		case angleTokenType: | 
 | 			if !first && n == 0 { | 
 | 				return ifHeader{}, false | 
 | 			} | 
 | 			resourceTag, n = tokenStr, 0 | 
 | 			s = remaining | 
 | 		case '(': | 
 | 			n++ | 
 | 			l, remaining, ok := parseList(s) | 
 | 			if !ok { | 
 | 				return ifHeader{}, false | 
 | 			} | 
 | 			l.resourceTag = resourceTag | 
 | 			h.lists = append(h.lists, l) | 
 | 			if remaining == "" { | 
 | 				return h, true | 
 | 			} | 
 | 			s = remaining | 
 | 		default: | 
 | 			return ifHeader{}, false | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func parseList(s string) (l ifList, remaining string, ok bool) { | 
 | 	tokenType, _, s := lex(s) | 
 | 	if tokenType != '(' { | 
 | 		return ifList{}, "", false | 
 | 	} | 
 | 	for { | 
 | 		tokenType, _, remaining = lex(s) | 
 | 		if tokenType == ')' { | 
 | 			if len(l.conditions) == 0 { | 
 | 				return ifList{}, "", false | 
 | 			} | 
 | 			return l, remaining, true | 
 | 		} | 
 | 		c, remaining, ok := parseCondition(s) | 
 | 		if !ok { | 
 | 			return ifList{}, "", false | 
 | 		} | 
 | 		l.conditions = append(l.conditions, c) | 
 | 		s = remaining | 
 | 	} | 
 | } | 
 |  | 
 | func parseCondition(s string) (c Condition, remaining string, ok bool) { | 
 | 	tokenType, tokenStr, s := lex(s) | 
 | 	if tokenType == notTokenType { | 
 | 		c.Not = true | 
 | 		tokenType, tokenStr, s = lex(s) | 
 | 	} | 
 | 	switch tokenType { | 
 | 	case strTokenType, angleTokenType: | 
 | 		c.Token = tokenStr | 
 | 	case squareTokenType: | 
 | 		c.ETag = tokenStr | 
 | 	default: | 
 | 		return Condition{}, "", false | 
 | 	} | 
 | 	return c, s, true | 
 | } | 
 |  | 
 | // Single-rune tokens like '(' or ')' have a token type equal to their rune. | 
 | // All other tokens have a negative token type. | 
 | const ( | 
 | 	errTokenType    = rune(-1) | 
 | 	eofTokenType    = rune(-2) | 
 | 	strTokenType    = rune(-3) | 
 | 	notTokenType    = rune(-4) | 
 | 	angleTokenType  = rune(-5) | 
 | 	squareTokenType = rune(-6) | 
 | ) | 
 |  | 
 | func lex(s string) (tokenType rune, tokenStr string, remaining string) { | 
 | 	// The net/textproto Reader that parses the HTTP header will collapse | 
 | 	// Linear White Space that spans multiple "\r\n" lines to a single " ", | 
 | 	// so we don't need to look for '\r' or '\n'. | 
 | 	for len(s) > 0 && (s[0] == '\t' || s[0] == ' ') { | 
 | 		s = s[1:] | 
 | 	} | 
 | 	if len(s) == 0 { | 
 | 		return eofTokenType, "", "" | 
 | 	} | 
 | 	i := 0 | 
 | loop: | 
 | 	for ; i < len(s); i++ { | 
 | 		switch s[i] { | 
 | 		case '\t', ' ', '(', ')', '<', '>', '[', ']': | 
 | 			break loop | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if i != 0 { | 
 | 		tokenStr, remaining = s[:i], s[i:] | 
 | 		if tokenStr == "Not" { | 
 | 			return notTokenType, "", remaining | 
 | 		} | 
 | 		return strTokenType, tokenStr, remaining | 
 | 	} | 
 |  | 
 | 	j := 0 | 
 | 	switch s[0] { | 
 | 	case '<': | 
 | 		j, tokenType = strings.IndexByte(s, '>'), angleTokenType | 
 | 	case '[': | 
 | 		j, tokenType = strings.IndexByte(s, ']'), squareTokenType | 
 | 	default: | 
 | 		return rune(s[0]), "", s[1:] | 
 | 	} | 
 | 	if j < 0 { | 
 | 		return errTokenType, "", "" | 
 | 	} | 
 | 	return tokenType, s[1:j], s[j+1:] | 
 | } |