package handler import ( "encoding/xml" "fmt" ) // ListBucketsResponse -- format for list buckets response. type ListBucketsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult" json:"-"` Owner Owner // Container for one or more buckets. Buckets struct { Buckets []Bucket `xml:"Bucket"` } // Buckets are nested } // ListObjectsV1Response -- format for ListObjectsV1 response. type ListObjectsV1Response struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` CommonPrefixes []CommonPrefix `xml:"CommonPrefixes"` Contents []Object `xml:"Contents"` Delimiter string `xml:"Delimiter,omitempty"` EncodingType string `xml:"EncodingType,omitempty"` IsTruncated bool `xml:"IsTruncated"` Marker string `xml:"Marker"` MaxKeys int `xml:"MaxKeys"` Name string `xml:"Name"` NextMarker string `xml:"NextMarker,omitempty"` Prefix string `xml:"Prefix"` } // ListObjectsV2Response -- format for ListObjectsV2 response. type ListObjectsV2Response struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` CommonPrefixes []CommonPrefix `xml:"CommonPrefixes"` Contents []Object `xml:"Contents"` ContinuationToken string `xml:"ContinuationToken,omitempty"` Delimiter string `xml:"Delimiter,omitempty"` EncodingType string `xml:"EncodingType,omitempty"` IsTruncated bool `xml:"IsTruncated"` KeyCount int `xml:"KeyCount"` MaxKeys int `xml:"MaxKeys"` Name string `xml:"Name"` NextContinuationToken string `xml:"NextContinuationToken,omitempty"` Prefix string `xml:"Prefix"` StartAfter string `xml:"StartAfter,omitempty"` } // Bucket container for bucket metadata. type Bucket struct { Name string CreationDate string // time string of format "2006-01-02T15:04:05.000Z" } // AccessControlPolicy contains ACL. type AccessControlPolicy struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ AccessControlPolicy" json:"-"` Owner Owner AccessControlList []*Grant `xml:"AccessControlList>Grant"` } // Grant is container for Grantee data. type Grant struct { Grantee *Grantee Permission AWSACL } // Grantee is info about access rights of some actor. type Grantee struct { XMLName xml.Name `xml:"Grantee"` XMLNS string `xml:"xmlns:xsi,attr"` ID string `xml:"ID,omitempty"` DisplayName string `xml:"DisplayName,omitempty"` EmailAddress string `xml:"EmailAddress,omitempty"` URI string `xml:"URI,omitempty"` Type GranteeType `xml:"xsi:type,attr"` } // NewGrantee creates new grantee using workaround // https://github.com/golang/go/issues/9519#issuecomment-252196382 func NewGrantee(t GranteeType) *Grantee { return &Grantee{ XMLNS: "http://www.w3.org/2001/XMLSchema-instance", Type: t, } } // Owner -- bucket owner/principal. type Owner struct { ID string DisplayName string } // CommonPrefix container for prefix response in ListObjects's response. type CommonPrefix struct { Prefix string } // Object container for object metadata. type Object struct { Key string LastModified string // time string of format "2006-01-02T15:04:05.000Z" ETag string `xml:"ETag,omitempty"` Size uint64 // Owner of the object. Owner *Owner `xml:"Owner,omitempty"` // Class of storage used to store the object. StorageClass string `xml:"StorageClass,omitempty"` } // ObjectVersionResponse container for object version in the response of ListBucketObjectVersionsHandler. type ObjectVersionResponse struct { ETag string `xml:"ETag"` IsLatest bool `xml:"IsLatest"` Key string `xml:"Key"` LastModified string `xml:"LastModified"` Owner Owner `xml:"Owner"` Size uint64 `xml:"Size"` StorageClass string `xml:"StorageClass,omitempty"` // is empty!! VersionID string `xml:"VersionId"` } // DeleteMarkerEntry container for deleted object's version in the response of ListBucketObjectVersionsHandler. type DeleteMarkerEntry struct { IsLatest bool `xml:"IsLatest"` Key string `xml:"Key"` LastModified string `xml:"LastModified"` Owner Owner `xml:"Owner"` VersionID string `xml:"VersionId"` } // StringMap is a map[string]string. type StringMap map[string]string // LocationResponse -- format for location response. type LocationResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ LocationConstraint" json:"-"` Location string `xml:",chardata"` } // CopyObjectResponse container returns ETag and LastModified of the successfully copied object. type CopyObjectResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CopyObjectResult" json:"-"` LastModified string // time string of format "2006-01-02T15:04:05.000Z" ETag string // md5sum of the copied object. } // ListObjectsVersionsResponse is a response of ListBucketObjectVersionsHandler. type ListObjectsVersionsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListVersionsResult" json:"-"` EncodingType string `xml:"EncodingType,omitempty"` Name string `xml:"Name"` IsTruncated bool `xml:"IsTruncated"` KeyMarker string `xml:"KeyMarker"` NextKeyMarker string `xml:"NextKeyMarker,omitempty"` NextVersionIDMarker string `xml:"NextVersionIdMarker,omitempty"` VersionIDMarker string `xml:"VersionIdMarker"` DeleteMarker []DeleteMarkerEntry `xml:"DeleteMarker"` Version []ObjectVersionResponse `xml:"Version"` CommonPrefixes []CommonPrefix `xml:"CommonPrefixes"` } // VersioningConfiguration contains VersioningConfiguration XML representation. type VersioningConfiguration struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ VersioningConfiguration"` Status string `xml:"Status,omitempty"` MfaDelete string `xml:"MfaDelete,omitempty"` } // Tagging contains tag set. type Tagging struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ Tagging"` TagSet []Tag `xml:"TagSet>Tag"` } // PostResponse contains result of posting object. type PostResponse struct { Bucket string `xml:"Bucket"` Key string `xml:"Key"` ETag string `xml:"Etag"` } // Tag is an AWS key-value tag. type Tag struct { Key string Value string } // MarshalXML -- StringMap marshals into XML. func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { tokens := []xml.Token{start} for key, value := range s { t := xml.StartElement{} t.Name = xml.Name{ Space: "", Local: key, } tokens = append(tokens, t, xml.CharData(value), xml.EndElement{Name: t.Name}) } tokens = append(tokens, xml.EndElement{ Name: start.Name, }) for _, t := range tokens { if err := e.EncodeToken(t); err != nil { return fmt.Errorf("encode token: %w", err) } } // flush to ensure tokens are written return e.Flush() }