2020-01-14 21:19:54 +00:00
|
|
|
package carddav
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/xml"
|
2020-01-19 10:05:56 +00:00
|
|
|
"fmt"
|
2020-01-14 22:44:21 +00:00
|
|
|
|
|
|
|
"github.com/emersion/go-webdav/internal"
|
2020-01-14 21:19:54 +00:00
|
|
|
)
|
|
|
|
|
2020-01-17 16:09:23 +00:00
|
|
|
const namespace = "urn:ietf:params:xml:ns:carddav"
|
|
|
|
|
2020-01-18 11:43:47 +00:00
|
|
|
var (
|
2020-01-19 14:10:54 +00:00
|
|
|
addressBookHomeSetName = xml.Name{namespace, "addressbook-home-set"}
|
|
|
|
|
2020-02-27 11:36:14 +00:00
|
|
|
addressBookName = xml.Name{namespace, "addressbook"}
|
|
|
|
addressBookDescriptionName = xml.Name{namespace, "addressbook-description"}
|
|
|
|
supportedAddressDataName = xml.Name{namespace, "supported-address-data"}
|
|
|
|
maxResourceSizeName = xml.Name{namespace, "max-resource-size"}
|
2020-01-19 11:08:53 +00:00
|
|
|
|
2020-01-19 14:29:51 +00:00
|
|
|
addressBookQueryName = xml.Name{namespace, "addressbook-query"}
|
|
|
|
addressBookMultigetName = xml.Name{namespace, "addressbook-multiget"}
|
2020-01-19 14:06:09 +00:00
|
|
|
|
2020-01-19 14:29:51 +00:00
|
|
|
addressDataName = xml.Name{namespace, "address-data"}
|
2020-01-18 11:43:47 +00:00
|
|
|
)
|
2020-01-17 16:09:23 +00:00
|
|
|
|
2020-01-19 14:10:54 +00:00
|
|
|
// https://tools.ietf.org/html/rfc6352#section-6.2.3
|
2020-01-14 21:19:54 +00:00
|
|
|
type addressbookHomeSet struct {
|
2020-01-22 10:07:30 +00:00
|
|
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-home-set"`
|
2020-01-29 16:41:28 +00:00
|
|
|
Href internal.Href `xml:"DAV: href"`
|
2020-01-14 22:13:23 +00:00
|
|
|
}
|
|
|
|
|
2022-03-21 08:16:50 +00:00
|
|
|
func (a *addressbookHomeSet) GetXMLName() xml.Name {
|
|
|
|
return addressBookHomeSetName
|
|
|
|
}
|
|
|
|
|
2020-01-14 22:13:23 +00:00
|
|
|
type addressbookDescription struct {
|
2020-01-19 13:53:58 +00:00
|
|
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav addressbook-description"`
|
|
|
|
Description string `xml:",chardata"`
|
2020-01-14 22:13:23 +00:00
|
|
|
}
|
|
|
|
|
2020-01-19 14:00:15 +00:00
|
|
|
// https://tools.ietf.org/html/rfc6352#section-6.2.2
|
2020-02-27 11:36:14 +00:00
|
|
|
type supportedAddressData struct {
|
|
|
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav supported-address-data"`
|
2020-01-19 14:00:15 +00:00
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
2020-01-19 14:06:09 +00:00
|
|
|
// 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"`
|
|
|
|
}
|
|
|
|
|
2020-01-14 22:13:23 +00:00
|
|
|
// https://tools.ietf.org/html/rfc6352#section-10.3
|
|
|
|
type addressbookQuery struct {
|
2020-01-22 17:59:01 +00:00
|
|
|
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"`
|
2020-01-22 19:36:35 +00:00
|
|
|
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 {
|
2020-01-24 10:25:58 +00:00
|
|
|
switch FilterTest(b) {
|
|
|
|
case FilterAnyOf, FilterAllOf:
|
|
|
|
*ft = filterTest(b)
|
2020-01-22 19:36:35 +00:00
|
|
|
return nil
|
|
|
|
default:
|
2020-01-24 10:25:58 +00:00
|
|
|
return fmt.Errorf("carddav: invalid filter test value: %q", string(b))
|
2020-01-22 19:36:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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"`
|
2020-01-24 11:24:35 +00:00
|
|
|
TextMatches []textMatch `xml:"text-match,omitempty"`
|
|
|
|
Params []paramFilter `xml:"param-filter,omitempty"`
|
2020-01-22 19:36:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// https://tools.ietf.org/html/rfc6352#section-10.5.4
|
|
|
|
type textMatch struct {
|
2020-01-23 18:32:10 +00:00
|
|
|
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
|
2020-01-23 18:27:31 +00:00
|
|
|
}
|
|
|
|
|
2020-01-24 10:25:58 +00:00
|
|
|
type matchType MatchType
|
2020-01-23 18:27:31 +00:00
|
|
|
|
|
|
|
func (mt *matchType) UnmarshalText(b []byte) error {
|
2020-01-24 10:25:58 +00:00
|
|
|
switch MatchType(b) {
|
|
|
|
case MatchEquals, MatchContains, MatchStartsWith, MatchEndsWith:
|
|
|
|
*mt = matchType(b)
|
2020-01-23 18:27:31 +00:00
|
|
|
return nil
|
|
|
|
default:
|
2020-01-24 10:25:58 +00:00
|
|
|
return fmt.Errorf("carddav: invalid match type value: %q", string(b))
|
2020-01-23 18:27:31 +00:00
|
|
|
}
|
2020-01-22 19:36:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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"`
|
2020-01-14 21:19:54 +00:00
|
|
|
}
|
2020-01-14 22:44:21 +00:00
|
|
|
|
2020-01-15 11:09:42 +00:00
|
|
|
// https://tools.ietf.org/html/rfc6352#section-8.7
|
|
|
|
type addressbookMultiget struct {
|
2020-01-22 17:59:01 +00:00
|
|
|
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"`
|
2020-01-15 11:09:42 +00:00
|
|
|
}
|
|
|
|
|
2020-01-14 22:44:21 +00:00
|
|
|
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 {
|
2020-01-22 18:18:58 +00:00
|
|
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:carddav address-data"`
|
|
|
|
Props []prop `xml:"prop"`
|
|
|
|
Allprop *struct{} `xml:"allprop"`
|
2020-01-14 22:44:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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"`
|
|
|
|
}
|
2020-01-19 10:05:56 +00:00
|
|
|
|
|
|
|
type reportReq struct {
|
2020-01-19 11:08:53 +00:00
|
|
|
Query *addressbookQuery
|
2020-01-19 10:05:56 +00:00
|
|
|
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)
|
|
|
|
}
|
2024-02-07 10:47:56 +00:00
|
|
|
|
|
|
|
type mkcolReq struct {
|
|
|
|
XMLName xml.Name `xml:"DAV: mkcol"`
|
|
|
|
ResourceType internal.ResourceType `xml:"set>prop>resourcetype"`
|
|
|
|
DisplayName string `xml:"set>prop>displayname"`
|
|
|
|
Description addressbookDescription `xml:"set>prop>addressbook-description"`
|
|
|
|
// TODO this could theoretically contain all addressbook properties?
|
|
|
|
}
|