go-webdav/carddav/elements.go

210 lines
6.4 KiB
Go

package carddav
import (
"encoding/xml"
"fmt"
"github.com/emersion/go-webdav/internal"
)
const namespace = "urn:ietf:params:xml:ns:carddav"
var (
addressBookHomeSetName = xml.Name{namespace, "addressbook-home-set"}
addressBookName = xml.Name{namespace, "addressbook"}
addressBookDescriptionName = xml.Name{namespace, "addressbook-description"}
addressBookSupportedAddressData = xml.Name{namespace, "addressbook-supported-address-data"}
maxResourceSizeName = xml.Name{namespace, "max-resource-size"}
addressBookQueryName = xml.Name{namespace, "addressbook-query"}
addressBookMultigetName = xml.Name{namespace, "addressbook-multiget"}
addressDataName = xml.Name{namespace, "address-data"}
)
// https://tools.ietf.org/html/rfc6352#section-6.2.3
type addressbookHomeSet struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-home-set"`
Href internal.Href `xml:"DAV: href"`
}
type addressbookDescription struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-description"`
Description string `xml:",chardata"`
}
// https://tools.ietf.org/html/rfc6352#section-6.2.2
type addressbookSupportedAddressData struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-supported-address-data"`
Types []addressDataType `xml:"address-data-type"`
}
type addressDataType struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav address-data-type"`
ContentType string `xml:"content-type,attr"`
Version string `xml:"version,attr"`
}
// https://tools.ietf.org/html/rfc6352#section-6.2.3
type maxResourceSize struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav max-resource-size"`
Size int64 `xml:",chardata"`
}
// https://tools.ietf.org/html/rfc6352#section-10.3
type addressbookQuery struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-query"`
Prop *internal.Prop `xml:"DAV: prop,omitempty"`
AllProp *struct{} `xml:"DAV: allprop,omitempty"`
PropName *struct{} `xml:"DAV: propname,omitempty"`
Filter filter `xml:"filter"`
Limit *limit `xml:"limit,omitempty"`
}
// https://tools.ietf.org/html/rfc6352#section-10.5
type filter struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav filter"`
Test filterTest `xml:"test,attr,omitempty"`
Props []propFilter `xml:"prop-filter"`
}
type filterTest string
func (ft *filterTest) UnmarshalText(b []byte) error {
switch FilterTest(b) {
case FilterAnyOf, FilterAllOf:
*ft = filterTest(b)
return nil
default:
return fmt.Errorf("carddav: invalid filter test value: %q", string(b))
}
}
// https://tools.ietf.org/html/rfc6352#section-10.5.1
type propFilter struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav prop-filter"`
Name string `xml:"name,attr"`
Test filterTest `xml:"test,attr,omitempty"`
IsNotDefined *struct{} `xml:"is-not-defined,omitempty"`
TextMatches []textMatch `xml:"text-match,omitempty"`
Params []paramFilter `xml:"param-filter,omitempty"`
}
// https://tools.ietf.org/html/rfc6352#section-10.5.4
type textMatch struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav text-match"`
Text string `xml:",chardata"`
Collation string `xml:"collation,attr,omitempty"`
NegateCondition negateCondition `xml:"negate-condition,attr,omitempty"`
MatchType matchType `xml:"match-type,attr,omitempty"`
}
type negateCondition bool
func (nc *negateCondition) UnmarshalText(b []byte) error {
switch s := string(b); s {
case "yes":
*nc = true
case "no":
*nc = false
default:
return fmt.Errorf("carddav: invalid negate-condition value: %q", s)
}
return nil
}
func (nc negateCondition) MarshalText() ([]byte, error) {
if nc {
return []byte("yes"), nil
}
return nil, nil
}
type matchType MatchType
func (mt *matchType) UnmarshalText(b []byte) error {
switch MatchType(b) {
case MatchEquals, MatchContains, MatchStartsWith, MatchEndsWith:
*mt = matchType(b)
return nil
default:
return fmt.Errorf("carddav: invalid match type value: %q", string(b))
}
}
// https://tools.ietf.org/html/rfc6352#section-10.5.2
type paramFilter struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav param-filter"`
Name string `xml:"name,attr"`
IsNotDefined *struct{} `xml:"is-not-defined"`
TextMatch *textMatch `xml:"text-match"`
}
// https://tools.ietf.org/html/rfc6352#section-10.6
type limit struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav limit"`
NResults uint `xml:"nresults"`
}
// https://tools.ietf.org/html/rfc6352#section-8.7
type addressbookMultiget struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-multiget"`
Hrefs []internal.Href `xml:"DAV: href"`
Prop *internal.Prop `xml:"DAV: prop,omitempty"`
AllProp *struct{} `xml:"DAV: allprop,omitempty"`
PropName *struct{} `xml:"DAV: propname,omitempty"`
}
func newProp(name string, noValue bool) *internal.RawXMLValue {
attrs := []xml.Attr{{Name: xml.Name{namespace, "name"}, Value: name}}
if noValue {
attrs = append(attrs, xml.Attr{Name: xml.Name{namespace, "novalue"}, Value: "yes"})
}
xmlName := xml.Name{namespace, "prop"}
return internal.NewRawXMLElement(xmlName, attrs, nil)
}
// https://tools.ietf.org/html/rfc6352#section-10.4
type addressDataReq struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav address-data"`
Props []prop `xml:"prop"`
Allprop *struct{} `xml:"allprop"`
}
// https://tools.ietf.org/html/rfc6352#section-10.4.2
type prop struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav prop"`
Name string `xml:"name,attr"`
// TODO: novalue
}
// https://tools.ietf.org/html/rfc6352#section-10.4
type addressDataResp struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav address-data"`
Data []byte `xml:",chardata"`
}
type reportReq struct {
Query *addressbookQuery
Multiget *addressbookMultiget
}
func (r *reportReq) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var v interface{}
switch start.Name {
case addressBookQueryName:
r.Query = &addressbookQuery{}
v = r.Query
case addressBookMultigetName:
r.Multiget = &addressbookMultiget{}
v = r.Multiget
default:
return fmt.Errorf("carddav: unsupported REPORT root %q %q", start.Name.Space, start.Name.Local)
}
return d.DecodeElement(v, &start)
}