• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package storage
16
17import (
18	"bytes"
19	"crypto"
20	"crypto/rand"
21	"crypto/rsa"
22	"crypto/sha256"
23	"crypto/x509"
24	"encoding/base64"
25	"encoding/pem"
26	"errors"
27	"fmt"
28	"io"
29	"io/ioutil"
30	"net/http"
31	"net/url"
32	"reflect"
33	"strconv"
34	"strings"
35	"time"
36	"unicode/utf8"
37
38	"google.golang.org/api/option"
39	htransport "google.golang.org/api/transport/http"
40
41	"cloud.google.com/go/internal/optional"
42	"cloud.google.com/go/internal/version"
43	"golang.org/x/net/context"
44	"google.golang.org/api/googleapi"
45	raw "google.golang.org/api/storage/v1"
46)
47
48var (
49	ErrBucketNotExist = errors.New("storage: bucket doesn't exist")
50	ErrObjectNotExist = errors.New("storage: object doesn't exist")
51)
52
53const userAgent = "gcloud-golang-storage/20151204"
54
55const (
56	// ScopeFullControl grants permissions to manage your
57	// data and permissions in Google Cloud Storage.
58	ScopeFullControl = raw.DevstorageFullControlScope
59
60	// ScopeReadOnly grants permissions to
61	// view your data in Google Cloud Storage.
62	ScopeReadOnly = raw.DevstorageReadOnlyScope
63
64	// ScopeReadWrite grants permissions to manage your
65	// data in Google Cloud Storage.
66	ScopeReadWrite = raw.DevstorageReadWriteScope
67)
68
69var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo)
70
71func setClientHeader(headers http.Header) {
72	headers.Set("x-goog-api-client", xGoogHeader)
73}
74
75// Client is a client for interacting with Google Cloud Storage.
76//
77// Clients should be reused instead of created as needed.
78// The methods of Client are safe for concurrent use by multiple goroutines.
79type Client struct {
80	hc  *http.Client
81	raw *raw.Service
82}
83
84// NewClient creates a new Google Cloud Storage client.
85// The default scope is ScopeFullControl. To use a different scope, like ScopeReadOnly, use option.WithScopes.
86func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
87	o := []option.ClientOption{
88		option.WithScopes(ScopeFullControl),
89		option.WithUserAgent(userAgent),
90	}
91	opts = append(o, opts...)
92	hc, ep, err := htransport.NewClient(ctx, opts...)
93	if err != nil {
94		return nil, fmt.Errorf("dialing: %v", err)
95	}
96	rawService, err := raw.New(hc)
97	if err != nil {
98		return nil, fmt.Errorf("storage client: %v", err)
99	}
100	if ep != "" {
101		rawService.BasePath = ep
102	}
103	return &Client{
104		hc:  hc,
105		raw: rawService,
106	}, nil
107}
108
109// Close closes the Client.
110//
111// Close need not be called at program exit.
112func (c *Client) Close() error {
113	c.hc = nil
114	return nil
115}
116
117// SignedURLOptions allows you to restrict the access to the signed URL.
118type SignedURLOptions struct {
119	// GoogleAccessID represents the authorizer of the signed URL generation.
120	// It is typically the Google service account client email address from
121	// the Google Developers Console in the form of "xxx@developer.gserviceaccount.com".
122	// Required.
123	GoogleAccessID string
124
125	// PrivateKey is the Google service account private key. It is obtainable
126	// from the Google Developers Console.
127	// At https://console.developers.google.com/project/<your-project-id>/apiui/credential,
128	// create a service account client ID or reuse one of your existing service account
129	// credentials. Click on the "Generate new P12 key" to generate and download
130	// a new private key. Once you download the P12 file, use the following command
131	// to convert it into a PEM file.
132	//
133	//    $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
134	//
135	// Provide the contents of the PEM file as a byte slice.
136	// Exactly one of PrivateKey or SignBytes must be non-nil.
137	PrivateKey []byte
138
139	// SignBytes is a function for implementing custom signing.
140	// If your application is running on Google App Engine, you can use appengine's internal signing function:
141	//     ctx := appengine.NewContext(request)
142	//     acc, _ := appengine.ServiceAccount(ctx)
143	//     url, err := SignedURL("bucket", "object", &SignedURLOptions{
144	//     	GoogleAccessID: acc,
145	//     	SignBytes: func(b []byte) ([]byte, error) {
146	//     		_, signedBytes, err := appengine.SignBytes(ctx, b)
147	//     		return signedBytes, err
148	//     	},
149	//     	// etc.
150	//     })
151	//
152	// Exactly one of PrivateKey or SignBytes must be non-nil.
153	SignBytes func([]byte) ([]byte, error)
154
155	// Method is the HTTP method to be used with the signed URL.
156	// Signed URLs can be used with GET, HEAD, PUT, and DELETE requests.
157	// Required.
158	Method string
159
160	// Expires is the expiration time on the signed URL. It must be
161	// a datetime in the future.
162	// Required.
163	Expires time.Time
164
165	// ContentType is the content type header the client must provide
166	// to use the generated signed URL.
167	// Optional.
168	ContentType string
169
170	// Headers is a list of extention headers the client must provide
171	// in order to use the generated signed URL.
172	// Optional.
173	Headers []string
174
175	// MD5 is the base64 encoded MD5 checksum of the file.
176	// If provided, the client should provide the exact value on the request
177	// header in order to use the signed URL.
178	// Optional.
179	MD5 string
180}
181
182// SignedURL returns a URL for the specified object. Signed URLs allow
183// the users access to a restricted resource for a limited time without having a
184// Google account or signing in. For more information about the signed
185// URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs.
186func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
187	if opts == nil {
188		return "", errors.New("storage: missing required SignedURLOptions")
189	}
190	if opts.GoogleAccessID == "" {
191		return "", errors.New("storage: missing required GoogleAccessID")
192	}
193	if (opts.PrivateKey == nil) == (opts.SignBytes == nil) {
194		return "", errors.New("storage: exactly one of PrivateKey or SignedBytes must be set")
195	}
196	if opts.Method == "" {
197		return "", errors.New("storage: missing required method option")
198	}
199	if opts.Expires.IsZero() {
200		return "", errors.New("storage: missing required expires option")
201	}
202	if opts.MD5 != "" {
203		md5, err := base64.StdEncoding.DecodeString(opts.MD5)
204		if err != nil || len(md5) != 16 {
205			return "", errors.New("storage: invalid MD5 checksum")
206		}
207	}
208
209	signBytes := opts.SignBytes
210	if opts.PrivateKey != nil {
211		key, err := parseKey(opts.PrivateKey)
212		if err != nil {
213			return "", err
214		}
215		signBytes = func(b []byte) ([]byte, error) {
216			sum := sha256.Sum256(b)
217			return rsa.SignPKCS1v15(
218				rand.Reader,
219				key,
220				crypto.SHA256,
221				sum[:],
222			)
223		}
224	}
225
226	u := &url.URL{
227		Path: fmt.Sprintf("/%s/%s", bucket, name),
228	}
229
230	buf := &bytes.Buffer{}
231	fmt.Fprintf(buf, "%s\n", opts.Method)
232	fmt.Fprintf(buf, "%s\n", opts.MD5)
233	fmt.Fprintf(buf, "%s\n", opts.ContentType)
234	fmt.Fprintf(buf, "%d\n", opts.Expires.Unix())
235	if len(opts.Headers) > 0 {
236		fmt.Fprintf(buf, "%s\n", strings.Join(opts.Headers, "\n"))
237	}
238	fmt.Fprintf(buf, "%s", u.String())
239
240	b, err := signBytes(buf.Bytes())
241	if err != nil {
242		return "", err
243	}
244	encoded := base64.StdEncoding.EncodeToString(b)
245	u.Scheme = "https"
246	u.Host = "storage.googleapis.com"
247	q := u.Query()
248	q.Set("GoogleAccessId", opts.GoogleAccessID)
249	q.Set("Expires", fmt.Sprintf("%d", opts.Expires.Unix()))
250	q.Set("Signature", string(encoded))
251	u.RawQuery = q.Encode()
252	return u.String(), nil
253}
254
255// ObjectHandle provides operations on an object in a Google Cloud Storage bucket.
256// Use BucketHandle.Object to get a handle.
257type ObjectHandle struct {
258	c             *Client
259	bucket        string
260	object        string
261	acl           ACLHandle
262	gen           int64 // a negative value indicates latest
263	conds         *Conditions
264	encryptionKey []byte // AES-256 key
265	userProject   string // for requester-pays buckets
266}
267
268// ACL provides access to the object's access control list.
269// This controls who can read and write this object.
270// This call does not perform any network operations.
271func (o *ObjectHandle) ACL() *ACLHandle {
272	return &o.acl
273}
274
275// Generation returns a new ObjectHandle that operates on a specific generation
276// of the object.
277// By default, the handle operates on the latest generation. Not
278// all operations work when given a specific generation; check the API
279// endpoints at https://cloud.google.com/storage/docs/json_api/ for details.
280func (o *ObjectHandle) Generation(gen int64) *ObjectHandle {
281	o2 := *o
282	o2.gen = gen
283	return &o2
284}
285
286// If returns a new ObjectHandle that applies a set of preconditions.
287// Preconditions already set on the ObjectHandle are ignored.
288// Operations on the new handle will only occur if the preconditions are
289// satisfied. See https://cloud.google.com/storage/docs/generations-preconditions
290// for more details.
291func (o *ObjectHandle) If(conds Conditions) *ObjectHandle {
292	o2 := *o
293	o2.conds = &conds
294	return &o2
295}
296
297// Key returns a new ObjectHandle that uses the supplied encryption
298// key to encrypt and decrypt the object's contents.
299//
300// Encryption key must be a 32-byte AES-256 key.
301// See https://cloud.google.com/storage/docs/encryption for details.
302func (o *ObjectHandle) Key(encryptionKey []byte) *ObjectHandle {
303	o2 := *o
304	o2.encryptionKey = encryptionKey
305	return &o2
306}
307
308// Attrs returns meta information about the object.
309// ErrObjectNotExist will be returned if the object is not found.
310func (o *ObjectHandle) Attrs(ctx context.Context) (*ObjectAttrs, error) {
311	if err := o.validate(); err != nil {
312		return nil, err
313	}
314	call := o.c.raw.Objects.Get(o.bucket, o.object).Projection("full").Context(ctx)
315	if err := applyConds("Attrs", o.gen, o.conds, call); err != nil {
316		return nil, err
317	}
318	if o.userProject != "" {
319		call.UserProject(o.userProject)
320	}
321	if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil {
322		return nil, err
323	}
324	var obj *raw.Object
325	var err error
326	setClientHeader(call.Header())
327	err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
328	if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
329		return nil, ErrObjectNotExist
330	}
331	if err != nil {
332		return nil, err
333	}
334	return newObject(obj), nil
335}
336
337// Update updates an object with the provided attributes.
338// All zero-value attributes are ignored.
339// ErrObjectNotExist will be returned if the object is not found.
340func (o *ObjectHandle) Update(ctx context.Context, uattrs ObjectAttrsToUpdate) (*ObjectAttrs, error) {
341	if err := o.validate(); err != nil {
342		return nil, err
343	}
344	var attrs ObjectAttrs
345	// Lists of fields to send, and set to null, in the JSON.
346	var forceSendFields, nullFields []string
347	if uattrs.ContentType != nil {
348		attrs.ContentType = optional.ToString(uattrs.ContentType)
349		forceSendFields = append(forceSendFields, "ContentType")
350	}
351	if uattrs.ContentLanguage != nil {
352		attrs.ContentLanguage = optional.ToString(uattrs.ContentLanguage)
353		// For ContentLanguage It's an error to send the empty string.
354		// Instead we send a null.
355		if attrs.ContentLanguage == "" {
356			nullFields = append(nullFields, "ContentLanguage")
357		} else {
358			forceSendFields = append(forceSendFields, "ContentLanguage")
359		}
360	}
361	if uattrs.ContentEncoding != nil {
362		attrs.ContentEncoding = optional.ToString(uattrs.ContentEncoding)
363		forceSendFields = append(forceSendFields, "ContentEncoding")
364	}
365	if uattrs.ContentDisposition != nil {
366		attrs.ContentDisposition = optional.ToString(uattrs.ContentDisposition)
367		forceSendFields = append(forceSendFields, "ContentDisposition")
368	}
369	if uattrs.CacheControl != nil {
370		attrs.CacheControl = optional.ToString(uattrs.CacheControl)
371		forceSendFields = append(forceSendFields, "CacheControl")
372	}
373	if uattrs.Metadata != nil {
374		attrs.Metadata = uattrs.Metadata
375		if len(attrs.Metadata) == 0 {
376			// Sending the empty map is a no-op. We send null instead.
377			nullFields = append(nullFields, "Metadata")
378		} else {
379			forceSendFields = append(forceSendFields, "Metadata")
380		}
381	}
382	if uattrs.ACL != nil {
383		attrs.ACL = uattrs.ACL
384		// It's an error to attempt to delete the ACL, so
385		// we don't append to nullFields here.
386		forceSendFields = append(forceSendFields, "Acl")
387	}
388	rawObj := attrs.toRawObject(o.bucket)
389	rawObj.ForceSendFields = forceSendFields
390	rawObj.NullFields = nullFields
391	call := o.c.raw.Objects.Patch(o.bucket, o.object, rawObj).Projection("full").Context(ctx)
392	if err := applyConds("Update", o.gen, o.conds, call); err != nil {
393		return nil, err
394	}
395	if o.userProject != "" {
396		call.UserProject(o.userProject)
397	}
398	if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil {
399		return nil, err
400	}
401	var obj *raw.Object
402	var err error
403	setClientHeader(call.Header())
404	err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
405	if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
406		return nil, ErrObjectNotExist
407	}
408	if err != nil {
409		return nil, err
410	}
411	return newObject(obj), nil
412}
413
414// ObjectAttrsToUpdate is used to update the attributes of an object.
415// Only fields set to non-nil values will be updated.
416// Set a field to its zero value to delete it.
417//
418// For example, to change ContentType and delete ContentEncoding and
419// Metadata, use
420//    ObjectAttrsToUpdate{
421//        ContentType: "text/html",
422//        ContentEncoding: "",
423//        Metadata: map[string]string{},
424//    }
425type ObjectAttrsToUpdate struct {
426	ContentType        optional.String
427	ContentLanguage    optional.String
428	ContentEncoding    optional.String
429	ContentDisposition optional.String
430	CacheControl       optional.String
431	Metadata           map[string]string // set to map[string]string{} to delete
432	ACL                []ACLRule
433}
434
435// Delete deletes the single specified object.
436func (o *ObjectHandle) Delete(ctx context.Context) error {
437	if err := o.validate(); err != nil {
438		return err
439	}
440	call := o.c.raw.Objects.Delete(o.bucket, o.object).Context(ctx)
441	if err := applyConds("Delete", o.gen, o.conds, call); err != nil {
442		return err
443	}
444	if o.userProject != "" {
445		call.UserProject(o.userProject)
446	}
447	// Encryption doesn't apply to Delete.
448	setClientHeader(call.Header())
449	err := runWithRetry(ctx, func() error { return call.Do() })
450	switch e := err.(type) {
451	case nil:
452		return nil
453	case *googleapi.Error:
454		if e.Code == http.StatusNotFound {
455			return ErrObjectNotExist
456		}
457	}
458	return err
459}
460
461// NewReader creates a new Reader to read the contents of the
462// object.
463// ErrObjectNotExist will be returned if the object is not found.
464//
465// The caller must call Close on the returned Reader when done reading.
466func (o *ObjectHandle) NewReader(ctx context.Context) (*Reader, error) {
467	return o.NewRangeReader(ctx, 0, -1)
468}
469
470// NewRangeReader reads part of an object, reading at most length bytes
471// starting at the given offset. If length is negative, the object is read
472// until the end.
473func (o *ObjectHandle) NewRangeReader(ctx context.Context, offset, length int64) (*Reader, error) {
474	if err := o.validate(); err != nil {
475		return nil, err
476	}
477	if offset < 0 {
478		return nil, fmt.Errorf("storage: invalid offset %d < 0", offset)
479	}
480	if o.conds != nil {
481		if err := o.conds.validate("NewRangeReader"); err != nil {
482			return nil, err
483		}
484	}
485	u := &url.URL{
486		Scheme:   "https",
487		Host:     "storage.googleapis.com",
488		Path:     fmt.Sprintf("/%s/%s", o.bucket, o.object),
489		RawQuery: conditionsQuery(o.gen, o.conds),
490	}
491	verb := "GET"
492	if length == 0 {
493		verb = "HEAD"
494	}
495	req, err := http.NewRequest(verb, u.String(), nil)
496	if err != nil {
497		return nil, err
498	}
499	req = withContext(req, ctx)
500	if length < 0 && offset > 0 {
501		req.Header.Set("Range", fmt.Sprintf("bytes=%d-", offset))
502	} else if length > 0 {
503		req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", offset, offset+length-1))
504	}
505	if o.userProject != "" {
506		req.Header.Set("X-Goog-User-Project", o.userProject)
507	}
508	if err := setEncryptionHeaders(req.Header, o.encryptionKey, false); err != nil {
509		return nil, err
510	}
511	var res *http.Response
512	err = runWithRetry(ctx, func() error {
513		res, err = o.c.hc.Do(req)
514		if err != nil {
515			return err
516		}
517		if res.StatusCode == http.StatusNotFound {
518			res.Body.Close()
519			return ErrObjectNotExist
520		}
521		if res.StatusCode < 200 || res.StatusCode > 299 {
522			body, _ := ioutil.ReadAll(res.Body)
523			res.Body.Close()
524			return &googleapi.Error{
525				Code:   res.StatusCode,
526				Header: res.Header,
527				Body:   string(body),
528			}
529		}
530		if offset > 0 && length != 0 && res.StatusCode != http.StatusPartialContent {
531			res.Body.Close()
532			return errors.New("storage: partial request not satisfied")
533		}
534		return nil
535	})
536	if err != nil {
537		return nil, err
538	}
539
540	var size int64 // total size of object, even if a range was requested.
541	if res.StatusCode == http.StatusPartialContent {
542		cr := strings.TrimSpace(res.Header.Get("Content-Range"))
543		if !strings.HasPrefix(cr, "bytes ") || !strings.Contains(cr, "/") {
544			return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
545		}
546		size, err = strconv.ParseInt(cr[strings.LastIndex(cr, "/")+1:], 10, 64)
547		if err != nil {
548			return nil, fmt.Errorf("storage: invalid Content-Range %q", cr)
549		}
550	} else {
551		size = res.ContentLength
552	}
553
554	remain := res.ContentLength
555	body := res.Body
556	if length == 0 {
557		remain = 0
558		body.Close()
559		body = emptyBody
560	}
561	var (
562		checkCRC bool
563		crc      uint32
564	)
565	// Even if there is a CRC header, we can't compute the hash on partial data.
566	if remain == size {
567		crc, checkCRC = parseCRC32c(res)
568	}
569	return &Reader{
570		body:         body,
571		size:         size,
572		remain:       remain,
573		contentType:  res.Header.Get("Content-Type"),
574		cacheControl: res.Header.Get("Cache-Control"),
575		wantCRC:      crc,
576		checkCRC:     checkCRC,
577	}, nil
578}
579
580func parseCRC32c(res *http.Response) (uint32, bool) {
581	const prefix = "crc32c="
582	for _, spec := range res.Header["X-Goog-Hash"] {
583		if strings.HasPrefix(spec, prefix) {
584			c, err := decodeUint32(spec[len(prefix):])
585			if err == nil {
586				return c, true
587			}
588		}
589	}
590	return 0, false
591}
592
593var emptyBody = ioutil.NopCloser(strings.NewReader(""))
594
595// NewWriter returns a storage Writer that writes to the GCS object
596// associated with this ObjectHandle.
597//
598// A new object will be created unless an object with this name already exists.
599// Otherwise any previous object with the same name will be replaced.
600// The object will not be available (and any previous object will remain)
601// until Close has been called.
602//
603// Attributes can be set on the object by modifying the returned Writer's
604// ObjectAttrs field before the first call to Write. If no ContentType
605// attribute is specified, the content type will be automatically sniffed
606// using net/http.DetectContentType.
607//
608// It is the caller's responsibility to call Close when writing is done.
609func (o *ObjectHandle) NewWriter(ctx context.Context) *Writer {
610	return &Writer{
611		ctx:         ctx,
612		o:           o,
613		donec:       make(chan struct{}),
614		ObjectAttrs: ObjectAttrs{Name: o.object},
615		ChunkSize:   googleapi.DefaultUploadChunkSize,
616	}
617}
618
619func (o *ObjectHandle) validate() error {
620	if o.bucket == "" {
621		return errors.New("storage: bucket name is empty")
622	}
623	if o.object == "" {
624		return errors.New("storage: object name is empty")
625	}
626	if !utf8.ValidString(o.object) {
627		return fmt.Errorf("storage: object name %q is not valid UTF-8", o.object)
628	}
629	return nil
630}
631
632// parseKey converts the binary contents of a private key file
633// to an *rsa.PrivateKey. It detects whether the private key is in a
634// PEM container or not. If so, it extracts the the private key
635// from PEM container before conversion. It only supports PEM
636// containers with no passphrase.
637func parseKey(key []byte) (*rsa.PrivateKey, error) {
638	if block, _ := pem.Decode(key); block != nil {
639		key = block.Bytes
640	}
641	parsedKey, err := x509.ParsePKCS8PrivateKey(key)
642	if err != nil {
643		parsedKey, err = x509.ParsePKCS1PrivateKey(key)
644		if err != nil {
645			return nil, err
646		}
647	}
648	parsed, ok := parsedKey.(*rsa.PrivateKey)
649	if !ok {
650		return nil, errors.New("oauth2: private key is invalid")
651	}
652	return parsed, nil
653}
654
655func toRawObjectACL(oldACL []ACLRule) []*raw.ObjectAccessControl {
656	var acl []*raw.ObjectAccessControl
657	if len(oldACL) > 0 {
658		acl = make([]*raw.ObjectAccessControl, len(oldACL))
659		for i, rule := range oldACL {
660			acl[i] = &raw.ObjectAccessControl{
661				Entity: string(rule.Entity),
662				Role:   string(rule.Role),
663			}
664		}
665	}
666	return acl
667}
668
669// toRawObject copies the editable attributes from o to the raw library's Object type.
670func (o *ObjectAttrs) toRawObject(bucket string) *raw.Object {
671	acl := toRawObjectACL(o.ACL)
672	return &raw.Object{
673		Bucket:             bucket,
674		Name:               o.Name,
675		ContentType:        o.ContentType,
676		ContentEncoding:    o.ContentEncoding,
677		ContentLanguage:    o.ContentLanguage,
678		CacheControl:       o.CacheControl,
679		ContentDisposition: o.ContentDisposition,
680		StorageClass:       o.StorageClass,
681		Acl:                acl,
682		Metadata:           o.Metadata,
683	}
684}
685
686// ObjectAttrs represents the metadata for a Google Cloud Storage (GCS) object.
687type ObjectAttrs struct {
688	// Bucket is the name of the bucket containing this GCS object.
689	// This field is read-only.
690	Bucket string
691
692	// Name is the name of the object within the bucket.
693	// This field is read-only.
694	Name string
695
696	// ContentType is the MIME type of the object's content.
697	ContentType string
698
699	// ContentLanguage is the content language of the object's content.
700	ContentLanguage string
701
702	// CacheControl is the Cache-Control header to be sent in the response
703	// headers when serving the object data.
704	CacheControl string
705
706	// ACL is the list of access control rules for the object.
707	ACL []ACLRule
708
709	// Owner is the owner of the object. This field is read-only.
710	//
711	// If non-zero, it is in the form of "user-<userId>".
712	Owner string
713
714	// Size is the length of the object's content. This field is read-only.
715	Size int64
716
717	// ContentEncoding is the encoding of the object's content.
718	ContentEncoding string
719
720	// ContentDisposition is the optional Content-Disposition header of the object
721	// sent in the response headers.
722	ContentDisposition string
723
724	// MD5 is the MD5 hash of the object's content. This field is read-only,
725	// except when used from a Writer. If set on a Writer, the uploaded
726	// data is rejected if its MD5 hash does not match this field.
727	MD5 []byte
728
729	// CRC32C is the CRC32 checksum of the object's content using
730	// the Castagnoli93 polynomial. This field is read-only, except when
731	// used from a Writer. If set on a Writer and Writer.SendCRC32C
732	// is true, the uploaded data is rejected if its CRC32c hash does not
733	// match this field.
734	CRC32C uint32
735
736	// MediaLink is an URL to the object's content. This field is read-only.
737	MediaLink string
738
739	// Metadata represents user-provided metadata, in key/value pairs.
740	// It can be nil if no metadata is provided.
741	Metadata map[string]string
742
743	// Generation is the generation number of the object's content.
744	// This field is read-only.
745	Generation int64
746
747	// Metageneration is the version of the metadata for this
748	// object at this generation. This field is used for preconditions
749	// and for detecting changes in metadata. A metageneration number
750	// is only meaningful in the context of a particular generation
751	// of a particular object. This field is read-only.
752	Metageneration int64
753
754	// StorageClass is the storage class of the object.
755	// This value defines how objects in the bucket are stored and
756	// determines the SLA and the cost of storage. Typical values are
757	// "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD"
758	// and "DURABLE_REDUCED_AVAILABILITY".
759	// It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL"
760	// or "REGIONAL" depending on the bucket's location settings.
761	StorageClass string
762
763	// Created is the time the object was created. This field is read-only.
764	Created time.Time
765
766	// Deleted is the time the object was deleted.
767	// If not deleted, it is the zero value. This field is read-only.
768	Deleted time.Time
769
770	// Updated is the creation or modification time of the object.
771	// For buckets with versioning enabled, changing an object's
772	// metadata does not change this property. This field is read-only.
773	Updated time.Time
774
775	// CustomerKeySHA256 is the base64-encoded SHA-256 hash of the
776	// customer-supplied encryption key for the object. It is empty if there is
777	// no customer-supplied encryption key.
778	// See // https://cloud.google.com/storage/docs/encryption for more about
779	// encryption in Google Cloud Storage.
780	CustomerKeySHA256 string
781
782	// Prefix is set only for ObjectAttrs which represent synthetic "directory
783	// entries" when iterating over buckets using Query.Delimiter. See
784	// ObjectIterator.Next. When set, no other fields in ObjectAttrs will be
785	// populated.
786	Prefix string
787}
788
789// convertTime converts a time in RFC3339 format to time.Time.
790// If any error occurs in parsing, the zero-value time.Time is silently returned.
791func convertTime(t string) time.Time {
792	var r time.Time
793	if t != "" {
794		r, _ = time.Parse(time.RFC3339, t)
795	}
796	return r
797}
798
799func newObject(o *raw.Object) *ObjectAttrs {
800	if o == nil {
801		return nil
802	}
803	acl := make([]ACLRule, len(o.Acl))
804	for i, rule := range o.Acl {
805		acl[i] = ACLRule{
806			Entity: ACLEntity(rule.Entity),
807			Role:   ACLRole(rule.Role),
808		}
809	}
810	owner := ""
811	if o.Owner != nil {
812		owner = o.Owner.Entity
813	}
814	md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash)
815	crc32c, _ := decodeUint32(o.Crc32c)
816	var sha256 string
817	if o.CustomerEncryption != nil {
818		sha256 = o.CustomerEncryption.KeySha256
819	}
820	return &ObjectAttrs{
821		Bucket:             o.Bucket,
822		Name:               o.Name,
823		ContentType:        o.ContentType,
824		ContentLanguage:    o.ContentLanguage,
825		CacheControl:       o.CacheControl,
826		ACL:                acl,
827		Owner:              owner,
828		ContentEncoding:    o.ContentEncoding,
829		ContentDisposition: o.ContentDisposition,
830		Size:               int64(o.Size),
831		MD5:                md5,
832		CRC32C:             crc32c,
833		MediaLink:          o.MediaLink,
834		Metadata:           o.Metadata,
835		Generation:         o.Generation,
836		Metageneration:     o.Metageneration,
837		StorageClass:       o.StorageClass,
838		CustomerKeySHA256:  sha256,
839		Created:            convertTime(o.TimeCreated),
840		Deleted:            convertTime(o.TimeDeleted),
841		Updated:            convertTime(o.Updated),
842	}
843}
844
845// Decode a uint32 encoded in Base64 in big-endian byte order.
846func decodeUint32(b64 string) (uint32, error) {
847	d, err := base64.StdEncoding.DecodeString(b64)
848	if err != nil {
849		return 0, err
850	}
851	if len(d) != 4 {
852		return 0, fmt.Errorf("storage: %q does not encode a 32-bit value", d)
853	}
854	return uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3]), nil
855}
856
857// Encode a uint32 as Base64 in big-endian byte order.
858func encodeUint32(u uint32) string {
859	b := []byte{byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u)}
860	return base64.StdEncoding.EncodeToString(b)
861}
862
863// Query represents a query to filter objects from a bucket.
864type Query struct {
865	// Delimiter returns results in a directory-like fashion.
866	// Results will contain only objects whose names, aside from the
867	// prefix, do not contain delimiter. Objects whose names,
868	// aside from the prefix, contain delimiter will have their name,
869	// truncated after the delimiter, returned in prefixes.
870	// Duplicate prefixes are omitted.
871	// Optional.
872	Delimiter string
873
874	// Prefix is the prefix filter to query objects
875	// whose names begin with this prefix.
876	// Optional.
877	Prefix string
878
879	// Versions indicates whether multiple versions of the same
880	// object will be included in the results.
881	Versions bool
882}
883
884// contentTyper implements ContentTyper to enable an
885// io.ReadCloser to specify its MIME type.
886type contentTyper struct {
887	io.Reader
888	t string
889}
890
891func (c *contentTyper) ContentType() string {
892	return c.t
893}
894
895// Conditions constrain methods to act on specific generations of
896// objects.
897//
898// The zero value is an empty set of constraints. Not all conditions or
899// combinations of conditions are applicable to all methods.
900// See https://cloud.google.com/storage/docs/generations-preconditions
901// for details on how these operate.
902type Conditions struct {
903	// Generation constraints.
904	// At most one of the following can be set to a non-zero value.
905
906	// GenerationMatch specifies that the object must have the given generation
907	// for the operation to occur.
908	// If GenerationMatch is zero, it has no effect.
909	// Use DoesNotExist to specify that the object does not exist in the bucket.
910	GenerationMatch int64
911
912	// GenerationNotMatch specifies that the object must not have the given
913	// generation for the operation to occur.
914	// If GenerationNotMatch is zero, it has no effect.
915	GenerationNotMatch int64
916
917	// DoesNotExist specifies that the object must not exist in the bucket for
918	// the operation to occur.
919	// If DoesNotExist is false, it has no effect.
920	DoesNotExist bool
921
922	// Metadata generation constraints.
923	// At most one of the following can be set to a non-zero value.
924
925	// MetagenerationMatch specifies that the object must have the given
926	// metageneration for the operation to occur.
927	// If MetagenerationMatch is zero, it has no effect.
928	MetagenerationMatch int64
929
930	// MetagenerationNotMatch specifies that the object must not have the given
931	// metageneration for the operation to occur.
932	// If MetagenerationNotMatch is zero, it has no effect.
933	MetagenerationNotMatch int64
934}
935
936func (c *Conditions) validate(method string) error {
937	if *c == (Conditions{}) {
938		return fmt.Errorf("storage: %s: empty conditions", method)
939	}
940	if !c.isGenerationValid() {
941		return fmt.Errorf("storage: %s: multiple conditions specified for generation", method)
942	}
943	if !c.isMetagenerationValid() {
944		return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
945	}
946	return nil
947}
948
949func (c *Conditions) isGenerationValid() bool {
950	n := 0
951	if c.GenerationMatch != 0 {
952		n++
953	}
954	if c.GenerationNotMatch != 0 {
955		n++
956	}
957	if c.DoesNotExist {
958		n++
959	}
960	return n <= 1
961}
962
963func (c *Conditions) isMetagenerationValid() bool {
964	return c.MetagenerationMatch == 0 || c.MetagenerationNotMatch == 0
965}
966
967// applyConds modifies the provided call using the conditions in conds.
968// call is something that quacks like a *raw.WhateverCall.
969func applyConds(method string, gen int64, conds *Conditions, call interface{}) error {
970	cval := reflect.ValueOf(call)
971	if gen >= 0 {
972		if !setConditionField(cval, "Generation", gen) {
973			return fmt.Errorf("storage: %s: generation not supported", method)
974		}
975	}
976	if conds == nil {
977		return nil
978	}
979	if err := conds.validate(method); err != nil {
980		return err
981	}
982	switch {
983	case conds.GenerationMatch != 0:
984		if !setConditionField(cval, "IfGenerationMatch", conds.GenerationMatch) {
985			return fmt.Errorf("storage: %s: ifGenerationMatch not supported", method)
986		}
987	case conds.GenerationNotMatch != 0:
988		if !setConditionField(cval, "IfGenerationNotMatch", conds.GenerationNotMatch) {
989			return fmt.Errorf("storage: %s: ifGenerationNotMatch not supported", method)
990		}
991	case conds.DoesNotExist:
992		if !setConditionField(cval, "IfGenerationMatch", int64(0)) {
993			return fmt.Errorf("storage: %s: DoesNotExist not supported", method)
994		}
995	}
996	switch {
997	case conds.MetagenerationMatch != 0:
998		if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
999			return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
1000		}
1001	case conds.MetagenerationNotMatch != 0:
1002		if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
1003			return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
1004		}
1005	}
1006	return nil
1007}
1008
1009func applySourceConds(gen int64, conds *Conditions, call *raw.ObjectsRewriteCall) error {
1010	if gen >= 0 {
1011		call.SourceGeneration(gen)
1012	}
1013	if conds == nil {
1014		return nil
1015	}
1016	if err := conds.validate("CopyTo source"); err != nil {
1017		return err
1018	}
1019	switch {
1020	case conds.GenerationMatch != 0:
1021		call.IfSourceGenerationMatch(conds.GenerationMatch)
1022	case conds.GenerationNotMatch != 0:
1023		call.IfSourceGenerationNotMatch(conds.GenerationNotMatch)
1024	case conds.DoesNotExist:
1025		call.IfSourceGenerationMatch(0)
1026	}
1027	switch {
1028	case conds.MetagenerationMatch != 0:
1029		call.IfSourceMetagenerationMatch(conds.MetagenerationMatch)
1030	case conds.MetagenerationNotMatch != 0:
1031		call.IfSourceMetagenerationNotMatch(conds.MetagenerationNotMatch)
1032	}
1033	return nil
1034}
1035
1036// setConditionField sets a field on a *raw.WhateverCall.
1037// We can't use anonymous interfaces because the return type is
1038// different, since the field setters are builders.
1039func setConditionField(call reflect.Value, name string, value interface{}) bool {
1040	m := call.MethodByName(name)
1041	if !m.IsValid() {
1042		return false
1043	}
1044	m.Call([]reflect.Value{reflect.ValueOf(value)})
1045	return true
1046}
1047
1048// conditionsQuery returns the generation and conditions as a URL query
1049// string suitable for URL.RawQuery.  It assumes that the conditions
1050// have been validated.
1051func conditionsQuery(gen int64, conds *Conditions) string {
1052	// URL escapes are elided because integer strings are URL-safe.
1053	var buf []byte
1054
1055	appendParam := func(s string, n int64) {
1056		if len(buf) > 0 {
1057			buf = append(buf, '&')
1058		}
1059		buf = append(buf, s...)
1060		buf = strconv.AppendInt(buf, n, 10)
1061	}
1062
1063	if gen >= 0 {
1064		appendParam("generation=", gen)
1065	}
1066	if conds == nil {
1067		return string(buf)
1068	}
1069	switch {
1070	case conds.GenerationMatch != 0:
1071		appendParam("ifGenerationMatch=", conds.GenerationMatch)
1072	case conds.GenerationNotMatch != 0:
1073		appendParam("ifGenerationNotMatch=", conds.GenerationNotMatch)
1074	case conds.DoesNotExist:
1075		appendParam("ifGenerationMatch=", 0)
1076	}
1077	switch {
1078	case conds.MetagenerationMatch != 0:
1079		appendParam("ifMetagenerationMatch=", conds.MetagenerationMatch)
1080	case conds.MetagenerationNotMatch != 0:
1081		appendParam("ifMetagenerationNotMatch=", conds.MetagenerationNotMatch)
1082	}
1083	return string(buf)
1084}
1085
1086// composeSourceObj wraps a *raw.ComposeRequestSourceObjects, but adds the methods
1087// that modifyCall searches for by name.
1088type composeSourceObj struct {
1089	src *raw.ComposeRequestSourceObjects
1090}
1091
1092func (c composeSourceObj) Generation(gen int64) {
1093	c.src.Generation = gen
1094}
1095
1096func (c composeSourceObj) IfGenerationMatch(gen int64) {
1097	// It's safe to overwrite ObjectPreconditions, since its only field is
1098	// IfGenerationMatch.
1099	c.src.ObjectPreconditions = &raw.ComposeRequestSourceObjectsObjectPreconditions{
1100		IfGenerationMatch: gen,
1101	}
1102}
1103
1104func setEncryptionHeaders(headers http.Header, key []byte, copySource bool) error {
1105	if key == nil {
1106		return nil
1107	}
1108	// TODO(jbd): Ask the API team to return a more user-friendly error
1109	// and avoid doing this check at the client level.
1110	if len(key) != 32 {
1111		return errors.New("storage: not a 32-byte AES-256 key")
1112	}
1113	var cs string
1114	if copySource {
1115		cs = "copy-source-"
1116	}
1117	headers.Set("x-goog-"+cs+"encryption-algorithm", "AES256")
1118	headers.Set("x-goog-"+cs+"encryption-key", base64.StdEncoding.EncodeToString(key))
1119	keyHash := sha256.Sum256(key)
1120	headers.Set("x-goog-"+cs+"encryption-key-sha256", base64.StdEncoding.EncodeToString(keyHash[:]))
1121	return nil
1122}
1123
1124// TODO(jbd): Add storage.objects.watch.
1125