1// Copyright 2014 Google LLC
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	"context"
20	"crypto"
21	"crypto/rand"
22	"crypto/rsa"
23	"crypto/sha256"
24	"crypto/x509"
25	"encoding/base64"
26	"encoding/hex"
27	"encoding/pem"
28	"errors"
29	"fmt"
30	"net/http"
31	"net/url"
32	"reflect"
33	"regexp"
34	"sort"
35	"strconv"
36	"strings"
37	"time"
38	"unicode/utf8"
39
40	"cloud.google.com/go/internal/optional"
41	"cloud.google.com/go/internal/trace"
42	"cloud.google.com/go/internal/version"
43	"google.golang.org/api/googleapi"
44	"google.golang.org/api/option"
45	raw "google.golang.org/api/storage/v1"
46	htransport "google.golang.org/api/transport/http"
47)
48
49var (
50	// ErrBucketNotExist indicates that the bucket does not exist.
51	ErrBucketNotExist = errors.New("storage: bucket doesn't exist")
52	// ErrObjectNotExist indicates that the object does not exist.
53	ErrObjectNotExist = errors.New("storage: object doesn't exist")
54)
55
56const userAgent = "gcloud-golang-storage/20151204"
57
58const (
59	// ScopeFullControl grants permissions to manage your
60	// data and permissions in Google Cloud Storage.
61	ScopeFullControl = raw.DevstorageFullControlScope
62
63	// ScopeReadOnly grants permissions to
64	// view your data in Google Cloud Storage.
65	ScopeReadOnly = raw.DevstorageReadOnlyScope
66
67	// ScopeReadWrite grants permissions to manage your
68	// data in Google Cloud Storage.
69	ScopeReadWrite = raw.DevstorageReadWriteScope
70)
71
72var xGoogHeader = fmt.Sprintf("gl-go/%s gccl/%s", version.Go(), version.Repo)
73
74func setClientHeader(headers http.Header) {
75	headers.Set("x-goog-api-client", xGoogHeader)
76}
77
78// Client is a client for interacting with Google Cloud Storage.
79//
80// Clients should be reused instead of created as needed.
81// The methods of Client are safe for concurrent use by multiple goroutines.
82type Client struct {
83	hc  *http.Client
84	raw *raw.Service
85}
86
87// NewClient creates a new Google Cloud Storage client.
88// The default scope is ScopeFullControl. To use a different scope, like ScopeReadOnly, use option.WithScopes.
89func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) {
90	o := []option.ClientOption{
91		option.WithScopes(ScopeFullControl),
92		option.WithUserAgent(userAgent),
93	}
94	opts = append(o, opts...)
95	hc, ep, err := htransport.NewClient(ctx, opts...)
96	if err != nil {
97		return nil, fmt.Errorf("dialing: %v", err)
98	}
99	rawService, err := raw.New(hc)
100	if err != nil {
101		return nil, fmt.Errorf("storage client: %v", err)
102	}
103	if ep != "" {
104		rawService.BasePath = ep
105	}
106	return &Client{
107		hc:  hc,
108		raw: rawService,
109	}, nil
110}
111
112// Close closes the Client.
113//
114// Close need not be called at program exit.
115func (c *Client) Close() error {
116	// Set fields to nil so that subsequent uses will panic.
117	c.hc = nil
118	c.raw = nil
119	return nil
120}
121
122// SigningScheme determines the API version to use when signing URLs.
123type SigningScheme int
124
125const (
126	// SigningSchemeDefault is presently V2 and will change to V4 in the future.
127	SigningSchemeDefault SigningScheme = iota
128
129	// SigningSchemeV2 uses the V2 scheme to sign URLs.
130	SigningSchemeV2
131
132	// SigningSchemeV4 uses the V4 scheme to sign URLs.
133	SigningSchemeV4
134)
135
136// SignedURLOptions allows you to restrict the access to the signed URL.
137type SignedURLOptions struct {
138	// GoogleAccessID represents the authorizer of the signed URL generation.
139	// It is typically the Google service account client email address from
140	// the Google Developers Console in the form of "xxx@developer.gserviceaccount.com".
141	// Required.
142	GoogleAccessID string
143
144	// PrivateKey is the Google service account private key. It is obtainable
145	// from the Google Developers Console.
146	// At https://console.developers.google.com/project/<your-project-id>/apiui/credential,
147	// create a service account client ID or reuse one of your existing service account
148	// credentials. Click on the "Generate new P12 key" to generate and download
149	// a new private key. Once you download the P12 file, use the following command
150	// to convert it into a PEM file.
151	//
152	//    $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
153	//
154	// Provide the contents of the PEM file as a byte slice.
155	// Exactly one of PrivateKey or SignBytes must be non-nil.
156	PrivateKey []byte
157
158	// SignBytes is a function for implementing custom signing. For example, if
159	// your application is running on Google App Engine, you can use
160	// appengine's internal signing function:
161	//     ctx := appengine.NewContext(request)
162	//     acc, _ := appengine.ServiceAccount(ctx)
163	//     url, err := SignedURL("bucket", "object", &SignedURLOptions{
164	//     	GoogleAccessID: acc,
165	//     	SignBytes: func(b []byte) ([]byte, error) {
166	//     		_, signedBytes, err := appengine.SignBytes(ctx, b)
167	//     		return signedBytes, err
168	//     	},
169	//     	// etc.
170	//     })
171	//
172	// Exactly one of PrivateKey or SignBytes must be non-nil.
173	SignBytes func([]byte) ([]byte, error)
174
175	// Method is the HTTP method to be used with the signed URL.
176	// Signed URLs can be used with GET, HEAD, PUT, and DELETE requests.
177	// Required.
178	Method string
179
180	// Expires is the expiration time on the signed URL. It must be
181	// a datetime in the future. For SigningSchemeV4, the expiration may be no
182	// more than seven days in the future.
183	// Required.
184	Expires time.Time
185
186	// ContentType is the content type header the client must provide
187	// to use the generated signed URL.
188	// Optional.
189	ContentType string
190
191	// Headers is a list of extension headers the client must provide
192	// in order to use the generated signed URL.
193	// Optional.
194	Headers []string
195
196	// MD5 is the base64 encoded MD5 checksum of the file.
197	// If provided, the client should provide the exact value on the request
198	// header in order to use the signed URL.
199	// Optional.
200	MD5 string
201
202	// Scheme determines the version of URL signing to use. Default is
203	// SigningSchemeV2.
204	Scheme SigningScheme
205}
206
207var (
208	tabRegex = regexp.MustCompile(`[\t]+`)
209	// I was tempted to call this spacex. :)
210	spaceRegex = regexp.MustCompile(` +`)
211
212	canonicalHeaderRegexp    = regexp.MustCompile(`(?i)^(x-goog-[^:]+):(.*)?$`)
213	excludedCanonicalHeaders = map[string]bool{
214		"x-goog-encryption-key":        true,
215		"x-goog-encryption-key-sha256": true,
216	}
217)
218
219// v2SanitizeHeaders applies the specifications for canonical extension headers at
220// https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers.
221func v2SanitizeHeaders(hdrs []string) []string {
222	headerMap := map[string][]string{}
223	for _, hdr := range hdrs {
224		// No leading or trailing whitespaces.
225		sanitizedHeader := strings.TrimSpace(hdr)
226
227		var header, value string
228		// Only keep canonical headers, discard any others.
229		headerMatches := canonicalHeaderRegexp.FindStringSubmatch(sanitizedHeader)
230		if len(headerMatches) == 0 {
231			continue
232		}
233		header = headerMatches[1]
234		value = headerMatches[2]
235
236		header = strings.ToLower(strings.TrimSpace(header))
237		value = strings.TrimSpace(value)
238
239		if excludedCanonicalHeaders[header] {
240			// Do not keep any deliberately excluded canonical headers when signing.
241			continue
242		}
243
244		if len(value) > 0 {
245			// Remove duplicate headers by appending the values of duplicates
246			// in their order of appearance.
247			headerMap[header] = append(headerMap[header], value)
248		}
249	}
250
251	var sanitizedHeaders []string
252	for header, values := range headerMap {
253		// There should be no spaces around the colon separating the header name
254		// from the header value or around the values themselves. The values
255		// should be separated by commas.
256		//
257		// NOTE: The semantics for headers without a value are not clear.
258		// However from specifications these should be edge-cases anyway and we
259		// should assume that there will be no canonical headers using empty
260		// values. Any such headers are discarded at the regexp stage above.
261		sanitizedHeaders = append(sanitizedHeaders, fmt.Sprintf("%s:%s", header, strings.Join(values, ",")))
262	}
263	sort.Strings(sanitizedHeaders)
264	return sanitizedHeaders
265}
266
267// v4SanitizeHeaders applies the specifications for canonical extension headers
268// at https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers.
269//
270// V4 does a couple things differently from V2:
271// - Headers get sorted by key, instead of by key:value. We do this in
272//   signedURLV4.
273// - There's no canonical regexp: we simply split headers on :.
274// - We don't exclude canonical headers.
275// - We replace leading and trailing spaces in header values, like v2, but also
276//   all intermediate space duplicates get stripped. That is, there's only ever
277//   a single consecutive space.
278func v4SanitizeHeaders(hdrs []string) []string {
279	headerMap := map[string][]string{}
280	for _, hdr := range hdrs {
281		// No leading or trailing whitespaces.
282		sanitizedHeader := strings.TrimSpace(hdr)
283
284		var key, value string
285		headerMatches := strings.Split(sanitizedHeader, ":")
286		if len(headerMatches) < 2 {
287			continue
288		}
289
290		key = headerMatches[0]
291		value = headerMatches[1]
292
293		key = strings.ToLower(strings.TrimSpace(key))
294		value = strings.TrimSpace(value)
295		value = string(spaceRegex.ReplaceAll([]byte(value), []byte(" ")))
296		value = string(tabRegex.ReplaceAll([]byte(value), []byte("\t")))
297
298		if len(value) > 0 {
299			// Remove duplicate headers by appending the values of duplicates
300			// in their order of appearance.
301			headerMap[key] = append(headerMap[key], value)
302		}
303	}
304
305	var sanitizedHeaders []string
306	for header, values := range headerMap {
307		// There should be no spaces around the colon separating the header name
308		// from the header value or around the values themselves. The values
309		// should be separated by commas.
310		//
311		// NOTE: The semantics for headers without a value are not clear.
312		// However from specifications these should be edge-cases anyway and we
313		// should assume that there will be no canonical headers using empty
314		// values. Any such headers are discarded at the regexp stage above.
315		sanitizedHeaders = append(sanitizedHeaders, fmt.Sprintf("%s:%s", header, strings.Join(values, ",")))
316	}
317	return sanitizedHeaders
318}
319
320// SignedURL returns a URL for the specified object. Signed URLs allow
321// the users access to a restricted resource for a limited time without having a
322// Google account or signing in. For more information about the signed
323// URLs, see https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs.
324func SignedURL(bucket, name string, opts *SignedURLOptions) (string, error) {
325	now := utcNow()
326	if err := validateOptions(opts, now); err != nil {
327		return "", err
328	}
329
330	switch opts.Scheme {
331	case SigningSchemeV2:
332		opts.Headers = v2SanitizeHeaders(opts.Headers)
333		return signedURLV2(bucket, name, opts)
334	case SigningSchemeV4:
335		opts.Headers = v4SanitizeHeaders(opts.Headers)
336		return signedURLV4(bucket, name, opts, now)
337	default: // SigningSchemeDefault
338		opts.Headers = v2SanitizeHeaders(opts.Headers)
339		return signedURLV2(bucket, name, opts)
340	}
341}
342
343func validateOptions(opts *SignedURLOptions, now time.Time) error {
344	if opts == nil {
345		return errors.New("storage: missing required SignedURLOptions")
346	}
347	if opts.GoogleAccessID == "" {
348		return errors.New("storage: missing required GoogleAccessID")
349	}
350	if (opts.PrivateKey == nil) == (opts.SignBytes == nil) {
351		return errors.New("storage: exactly one of PrivateKey or SignedBytes must be set")
352	}
353	if opts.Method == "" {
354		return errors.New("storage: missing required method option")
355	}
356	if opts.Expires.IsZero() {
357		return errors.New("storage: missing required expires option")
358	}
359	if opts.MD5 != "" {
360		md5, err := base64.StdEncoding.DecodeString(opts.MD5)
361		if err != nil || len(md5) != 16 {
362			return errors.New("storage: invalid MD5 checksum")
363		}
364	}
365	if opts.Scheme == SigningSchemeV4 {
366		cutoff := now.Add(604801 * time.Second) // 7 days + 1 second
367		if !opts.Expires.Before(cutoff) {
368			return errors.New("storage: expires must be within seven days from now")
369		}
370	}
371	return nil
372}
373
374const (
375	iso8601      = "20060102T150405Z"
376	yearMonthDay = "20060102"
377)
378
379// utcNow returns the current time in UTC and is a variable to allow for
380// reassignment in tests to provide deterministic signed URL values.
381var utcNow = func() time.Time {
382	return time.Now().UTC()
383}
384
385// extractHeaderNames takes in a series of key:value headers and returns the
386// header names only.
387func extractHeaderNames(kvs []string) []string {
388	var res []string
389	for _, header := range kvs {
390		nameValue := strings.Split(header, ":")
391		res = append(res, nameValue[0])
392	}
393	return res
394}
395
396// signedURLV4 creates a signed URL using the sigV4 algorithm.
397func signedURLV4(bucket, name string, opts *SignedURLOptions, now time.Time) (string, error) {
398	buf := &bytes.Buffer{}
399	fmt.Fprintf(buf, "%s\n", opts.Method)
400	u := &url.URL{Path: bucket}
401	if name != "" {
402		u.Path += "/" + name
403	}
404
405	// Note: we have to add a / here because GCS does so auto-magically, despite
406	// Go's EscapedPath not doing so (and we have to exactly match their
407	// canonical query).
408	fmt.Fprintf(buf, "/%s\n", u.EscapedPath())
409
410	headerNames := append(extractHeaderNames(opts.Headers), "host")
411	if opts.ContentType != "" {
412		headerNames = append(headerNames, "content-type")
413	}
414	if opts.MD5 != "" {
415		headerNames = append(headerNames, "content-md5")
416	}
417	sort.Strings(headerNames)
418	signedHeaders := strings.Join(headerNames, ";")
419	timestamp := now.Format(iso8601)
420	credentialScope := fmt.Sprintf("%s/auto/storage/goog4_request", now.Format(yearMonthDay))
421	canonicalQueryString := url.Values{
422		"X-Goog-Algorithm":     {"GOOG4-RSA-SHA256"},
423		"X-Goog-Credential":    {fmt.Sprintf("%s/%s", opts.GoogleAccessID, credentialScope)},
424		"X-Goog-Date":          {timestamp},
425		"X-Goog-Expires":       {fmt.Sprintf("%d", int(opts.Expires.Sub(now).Seconds()))},
426		"X-Goog-SignedHeaders": {signedHeaders},
427	}
428	fmt.Fprintf(buf, "%s\n", canonicalQueryString.Encode())
429
430	u.Host = "storage.googleapis.com"
431
432	var headersWithValue []string
433	headersWithValue = append(headersWithValue, "host:"+u.Host)
434	headersWithValue = append(headersWithValue, opts.Headers...)
435	if opts.ContentType != "" {
436		headersWithValue = append(headersWithValue, "content-type:"+strings.TrimSpace(opts.ContentType))
437	}
438	if opts.MD5 != "" {
439		headersWithValue = append(headersWithValue, "content-md5:"+strings.TrimSpace(opts.MD5))
440	}
441	canonicalHeaders := strings.Join(sortHeadersByKey(headersWithValue), "\n")
442	fmt.Fprintf(buf, "%s\n\n", canonicalHeaders)
443	fmt.Fprintf(buf, "%s\n", signedHeaders)
444	fmt.Fprint(buf, "UNSIGNED-PAYLOAD")
445
446	sum := sha256.Sum256(buf.Bytes())
447	hexDigest := hex.EncodeToString(sum[:])
448	signBuf := &bytes.Buffer{}
449	fmt.Fprint(signBuf, "GOOG4-RSA-SHA256\n")
450	fmt.Fprintf(signBuf, "%s\n", timestamp)
451	fmt.Fprintf(signBuf, "%s\n", credentialScope)
452	fmt.Fprintf(signBuf, "%s", hexDigest)
453
454	signBytes := opts.SignBytes
455	if opts.PrivateKey != nil {
456		key, err := parseKey(opts.PrivateKey)
457		if err != nil {
458			return "", err
459		}
460		signBytes = func(b []byte) ([]byte, error) {
461			sum := sha256.Sum256(b)
462			return rsa.SignPKCS1v15(
463				rand.Reader,
464				key,
465				crypto.SHA256,
466				sum[:],
467			)
468		}
469	}
470	b, err := signBytes(signBuf.Bytes())
471	if err != nil {
472		return "", err
473	}
474	signature := hex.EncodeToString(b)
475	canonicalQueryString.Set("X-Goog-Signature", string(signature))
476	u.Scheme = "https"
477	u.RawQuery = canonicalQueryString.Encode()
478	return u.String(), nil
479}
480
481// takes a list of headerKey:headervalue1,headervalue2,etc and sorts by header
482// key.
483func sortHeadersByKey(hdrs []string) []string {
484	headersMap := map[string]string{}
485	var headersKeys []string
486	for _, h := range hdrs {
487		parts := strings.Split(h, ":")
488		k := parts[0]
489		v := parts[1]
490		headersMap[k] = v
491		headersKeys = append(headersKeys, k)
492	}
493	sort.Strings(headersKeys)
494	var sorted []string
495	for _, k := range headersKeys {
496		v := headersMap[k]
497		sorted = append(sorted, fmt.Sprintf("%s:%s", k, v))
498	}
499	return sorted
500}
501
502func signedURLV2(bucket, name string, opts *SignedURLOptions) (string, error) {
503	signBytes := opts.SignBytes
504	if opts.PrivateKey != nil {
505		key, err := parseKey(opts.PrivateKey)
506		if err != nil {
507			return "", err
508		}
509		signBytes = func(b []byte) ([]byte, error) {
510			sum := sha256.Sum256(b)
511			return rsa.SignPKCS1v15(
512				rand.Reader,
513				key,
514				crypto.SHA256,
515				sum[:],
516			)
517		}
518	}
519
520	u := &url.URL{
521		Path: fmt.Sprintf("/%s/%s", bucket, name),
522	}
523
524	buf := &bytes.Buffer{}
525	fmt.Fprintf(buf, "%s\n", opts.Method)
526	fmt.Fprintf(buf, "%s\n", opts.MD5)
527	fmt.Fprintf(buf, "%s\n", opts.ContentType)
528	fmt.Fprintf(buf, "%d\n", opts.Expires.Unix())
529	if len(opts.Headers) > 0 {
530		fmt.Fprintf(buf, "%s\n", strings.Join(opts.Headers, "\n"))
531	}
532	fmt.Fprintf(buf, "%s", u.String())
533
534	b, err := signBytes(buf.Bytes())
535	if err != nil {
536		return "", err
537	}
538	encoded := base64.StdEncoding.EncodeToString(b)
539	u.Scheme = "https"
540	u.Host = "storage.googleapis.com"
541	q := u.Query()
542	q.Set("GoogleAccessId", opts.GoogleAccessID)
543	q.Set("Expires", fmt.Sprintf("%d", opts.Expires.Unix()))
544	q.Set("Signature", string(encoded))
545	u.RawQuery = q.Encode()
546	return u.String(), nil
547}
548
549// ObjectHandle provides operations on an object in a Google Cloud Storage bucket.
550// Use BucketHandle.Object to get a handle.
551type ObjectHandle struct {
552	c              *Client
553	bucket         string
554	object         string
555	acl            ACLHandle
556	gen            int64 // a negative value indicates latest
557	conds          *Conditions
558	encryptionKey  []byte // AES-256 key
559	userProject    string // for requester-pays buckets
560	readCompressed bool   // Accept-Encoding: gzip
561}
562
563// ACL provides access to the object's access control list.
564// This controls who can read and write this object.
565// This call does not perform any network operations.
566func (o *ObjectHandle) ACL() *ACLHandle {
567	return &o.acl
568}
569
570// Generation returns a new ObjectHandle that operates on a specific generation
571// of the object.
572// By default, the handle operates on the latest generation. Not
573// all operations work when given a specific generation; check the API
574// endpoints at https://cloud.google.com/storage/docs/json_api/ for details.
575func (o *ObjectHandle) Generation(gen int64) *ObjectHandle {
576	o2 := *o
577	o2.gen = gen
578	return &o2
579}
580
581// If returns a new ObjectHandle that applies a set of preconditions.
582// Preconditions already set on the ObjectHandle are ignored.
583// Operations on the new handle will return an error if the preconditions are not
584// satisfied. See https://cloud.google.com/storage/docs/generations-preconditions
585// for more details.
586func (o *ObjectHandle) If(conds Conditions) *ObjectHandle {
587	o2 := *o
588	o2.conds = &conds
589	return &o2
590}
591
592// Key returns a new ObjectHandle that uses the supplied encryption
593// key to encrypt and decrypt the object's contents.
594//
595// Encryption key must be a 32-byte AES-256 key.
596// See https://cloud.google.com/storage/docs/encryption for details.
597func (o *ObjectHandle) Key(encryptionKey []byte) *ObjectHandle {
598	o2 := *o
599	o2.encryptionKey = encryptionKey
600	return &o2
601}
602
603// Attrs returns meta information about the object.
604// ErrObjectNotExist will be returned if the object is not found.
605func (o *ObjectHandle) Attrs(ctx context.Context) (attrs *ObjectAttrs, err error) {
606	ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.Attrs")
607	defer func() { trace.EndSpan(ctx, err) }()
608
609	if err := o.validate(); err != nil {
610		return nil, err
611	}
612	call := o.c.raw.Objects.Get(o.bucket, o.object).Projection("full").Context(ctx)
613	if err := applyConds("Attrs", o.gen, o.conds, call); err != nil {
614		return nil, err
615	}
616	if o.userProject != "" {
617		call.UserProject(o.userProject)
618	}
619	if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil {
620		return nil, err
621	}
622	var obj *raw.Object
623	setClientHeader(call.Header())
624	err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
625	if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
626		return nil, ErrObjectNotExist
627	}
628	if err != nil {
629		return nil, err
630	}
631	return newObject(obj), nil
632}
633
634// Update updates an object with the provided attributes.
635// All zero-value attributes are ignored.
636// ErrObjectNotExist will be returned if the object is not found.
637func (o *ObjectHandle) Update(ctx context.Context, uattrs ObjectAttrsToUpdate) (oa *ObjectAttrs, err error) {
638	ctx = trace.StartSpan(ctx, "cloud.google.com/go/storage.Object.Update")
639	defer func() { trace.EndSpan(ctx, err) }()
640
641	if err := o.validate(); err != nil {
642		return nil, err
643	}
644	var attrs ObjectAttrs
645	// Lists of fields to send, and set to null, in the JSON.
646	var forceSendFields, nullFields []string
647	if uattrs.ContentType != nil {
648		attrs.ContentType = optional.ToString(uattrs.ContentType)
649		// For ContentType, sending the empty string is a no-op.
650		// Instead we send a null.
651		if attrs.ContentType == "" {
652			nullFields = append(nullFields, "ContentType")
653		} else {
654			forceSendFields = append(forceSendFields, "ContentType")
655		}
656	}
657	if uattrs.ContentLanguage != nil {
658		attrs.ContentLanguage = optional.ToString(uattrs.ContentLanguage)
659		// For ContentLanguage it's an error to send the empty string.
660		// Instead we send a null.
661		if attrs.ContentLanguage == "" {
662			nullFields = append(nullFields, "ContentLanguage")
663		} else {
664			forceSendFields = append(forceSendFields, "ContentLanguage")
665		}
666	}
667	if uattrs.ContentEncoding != nil {
668		attrs.ContentEncoding = optional.ToString(uattrs.ContentEncoding)
669		forceSendFields = append(forceSendFields, "ContentEncoding")
670	}
671	if uattrs.ContentDisposition != nil {
672		attrs.ContentDisposition = optional.ToString(uattrs.ContentDisposition)
673		forceSendFields = append(forceSendFields, "ContentDisposition")
674	}
675	if uattrs.CacheControl != nil {
676		attrs.CacheControl = optional.ToString(uattrs.CacheControl)
677		forceSendFields = append(forceSendFields, "CacheControl")
678	}
679	if uattrs.EventBasedHold != nil {
680		attrs.EventBasedHold = optional.ToBool(uattrs.EventBasedHold)
681		forceSendFields = append(forceSendFields, "EventBasedHold")
682	}
683	if uattrs.TemporaryHold != nil {
684		attrs.TemporaryHold = optional.ToBool(uattrs.TemporaryHold)
685		forceSendFields = append(forceSendFields, "TemporaryHold")
686	}
687	if uattrs.Metadata != nil {
688		attrs.Metadata = uattrs.Metadata
689		if len(attrs.Metadata) == 0 {
690			// Sending the empty map is a no-op. We send null instead.
691			nullFields = append(nullFields, "Metadata")
692		} else {
693			forceSendFields = append(forceSendFields, "Metadata")
694		}
695	}
696	if uattrs.ACL != nil {
697		attrs.ACL = uattrs.ACL
698		// It's an error to attempt to delete the ACL, so
699		// we don't append to nullFields here.
700		forceSendFields = append(forceSendFields, "Acl")
701	}
702	rawObj := attrs.toRawObject(o.bucket)
703	rawObj.ForceSendFields = forceSendFields
704	rawObj.NullFields = nullFields
705	call := o.c.raw.Objects.Patch(o.bucket, o.object, rawObj).Projection("full").Context(ctx)
706	if err := applyConds("Update", o.gen, o.conds, call); err != nil {
707		return nil, err
708	}
709	if o.userProject != "" {
710		call.UserProject(o.userProject)
711	}
712	if uattrs.PredefinedACL != "" {
713		call.PredefinedAcl(uattrs.PredefinedACL)
714	}
715	if err := setEncryptionHeaders(call.Header(), o.encryptionKey, false); err != nil {
716		return nil, err
717	}
718	var obj *raw.Object
719	setClientHeader(call.Header())
720	err = runWithRetry(ctx, func() error { obj, err = call.Do(); return err })
721	if e, ok := err.(*googleapi.Error); ok && e.Code == http.StatusNotFound {
722		return nil, ErrObjectNotExist
723	}
724	if err != nil {
725		return nil, err
726	}
727	return newObject(obj), nil
728}
729
730// BucketName returns the name of the bucket.
731func (o *ObjectHandle) BucketName() string {
732	return o.bucket
733}
734
735// ObjectName returns the name of the object.
736func (o *ObjectHandle) ObjectName() string {
737	return o.object
738}
739
740// ObjectAttrsToUpdate is used to update the attributes of an object.
741// Only fields set to non-nil values will be updated.
742// Set a field to its zero value to delete it.
743//
744// For example, to change ContentType and delete ContentEncoding and
745// Metadata, use
746//    ObjectAttrsToUpdate{
747//        ContentType: "text/html",
748//        ContentEncoding: "",
749//        Metadata: map[string]string{},
750//    }
751type ObjectAttrsToUpdate struct {
752	EventBasedHold     optional.Bool
753	TemporaryHold      optional.Bool
754	ContentType        optional.String
755	ContentLanguage    optional.String
756	ContentEncoding    optional.String
757	ContentDisposition optional.String
758	CacheControl       optional.String
759	Metadata           map[string]string // set to map[string]string{} to delete
760	ACL                []ACLRule
761
762	// If not empty, applies a predefined set of access controls. ACL must be nil.
763	// See https://cloud.google.com/storage/docs/json_api/v1/objects/patch.
764	PredefinedACL string
765}
766
767// Delete deletes the single specified object.
768func (o *ObjectHandle) Delete(ctx context.Context) error {
769	if err := o.validate(); err != nil {
770		return err
771	}
772	call := o.c.raw.Objects.Delete(o.bucket, o.object).Context(ctx)
773	if err := applyConds("Delete", o.gen, o.conds, call); err != nil {
774		return err
775	}
776	if o.userProject != "" {
777		call.UserProject(o.userProject)
778	}
779	// Encryption doesn't apply to Delete.
780	setClientHeader(call.Header())
781	err := runWithRetry(ctx, func() error { return call.Do() })
782	switch e := err.(type) {
783	case nil:
784		return nil
785	case *googleapi.Error:
786		if e.Code == http.StatusNotFound {
787			return ErrObjectNotExist
788		}
789	}
790	return err
791}
792
793// ReadCompressed when true causes the read to happen without decompressing.
794func (o *ObjectHandle) ReadCompressed(compressed bool) *ObjectHandle {
795	o2 := *o
796	o2.readCompressed = compressed
797	return &o2
798}
799
800// NewWriter returns a storage Writer that writes to the GCS object
801// associated with this ObjectHandle.
802//
803// A new object will be created unless an object with this name already exists.
804// Otherwise any previous object with the same name will be replaced.
805// The object will not be available (and any previous object will remain)
806// until Close has been called.
807//
808// Attributes can be set on the object by modifying the returned Writer's
809// ObjectAttrs field before the first call to Write. If no ContentType
810// attribute is specified, the content type will be automatically sniffed
811// using net/http.DetectContentType.
812//
813// It is the caller's responsibility to call Close when writing is done. To
814// stop writing without saving the data, cancel the context.
815func (o *ObjectHandle) NewWriter(ctx context.Context) *Writer {
816	return &Writer{
817		ctx:         ctx,
818		o:           o,
819		donec:       make(chan struct{}),
820		ObjectAttrs: ObjectAttrs{Name: o.object},
821		ChunkSize:   googleapi.DefaultUploadChunkSize,
822	}
823}
824
825func (o *ObjectHandle) validate() error {
826	if o.bucket == "" {
827		return errors.New("storage: bucket name is empty")
828	}
829	if o.object == "" {
830		return errors.New("storage: object name is empty")
831	}
832	if !utf8.ValidString(o.object) {
833		return fmt.Errorf("storage: object name %q is not valid UTF-8", o.object)
834	}
835	return nil
836}
837
838// parseKey converts the binary contents of a private key file to an
839// *rsa.PrivateKey. It detects whether the private key is in a PEM container or
840// not. If so, it extracts the private key from PEM container before
841// conversion. It only supports PEM containers with no passphrase.
842func parseKey(key []byte) (*rsa.PrivateKey, error) {
843	if block, _ := pem.Decode(key); block != nil {
844		key = block.Bytes
845	}
846	parsedKey, err := x509.ParsePKCS8PrivateKey(key)
847	if err != nil {
848		parsedKey, err = x509.ParsePKCS1PrivateKey(key)
849		if err != nil {
850			return nil, err
851		}
852	}
853	parsed, ok := parsedKey.(*rsa.PrivateKey)
854	if !ok {
855		return nil, errors.New("oauth2: private key is invalid")
856	}
857	return parsed, nil
858}
859
860// toRawObject copies the editable attributes from o to the raw library's Object type.
861func (o *ObjectAttrs) toRawObject(bucket string) *raw.Object {
862	var ret string
863	if !o.RetentionExpirationTime.IsZero() {
864		ret = o.RetentionExpirationTime.Format(time.RFC3339)
865	}
866	return &raw.Object{
867		Bucket:                  bucket,
868		Name:                    o.Name,
869		EventBasedHold:          o.EventBasedHold,
870		TemporaryHold:           o.TemporaryHold,
871		RetentionExpirationTime: ret,
872		ContentType:             o.ContentType,
873		ContentEncoding:         o.ContentEncoding,
874		ContentLanguage:         o.ContentLanguage,
875		CacheControl:            o.CacheControl,
876		ContentDisposition:      o.ContentDisposition,
877		StorageClass:            o.StorageClass,
878		Acl:                     toRawObjectACL(o.ACL),
879		Metadata:                o.Metadata,
880	}
881}
882
883// ObjectAttrs represents the metadata for a Google Cloud Storage (GCS) object.
884type ObjectAttrs struct {
885	// Bucket is the name of the bucket containing this GCS object.
886	// This field is read-only.
887	Bucket string
888
889	// Name is the name of the object within the bucket.
890	// This field is read-only.
891	Name string
892
893	// ContentType is the MIME type of the object's content.
894	ContentType string
895
896	// ContentLanguage is the content language of the object's content.
897	ContentLanguage string
898
899	// CacheControl is the Cache-Control header to be sent in the response
900	// headers when serving the object data.
901	CacheControl string
902
903	// EventBasedHold specifies whether an object is under event-based hold. New
904	// objects created in a bucket whose DefaultEventBasedHold is set will
905	// default to that value.
906	EventBasedHold bool
907
908	// TemporaryHold specifies whether an object is under temporary hold. While
909	// this flag is set to true, the object is protected against deletion and
910	// overwrites.
911	TemporaryHold bool
912
913	// RetentionExpirationTime is a server-determined value that specifies the
914	// earliest time that the object's retention period expires.
915	// This is a read-only field.
916	RetentionExpirationTime time.Time
917
918	// ACL is the list of access control rules for the object.
919	ACL []ACLRule
920
921	// If not empty, applies a predefined set of access controls. It should be set
922	// only when writing, copying or composing an object. When copying or composing,
923	// it acts as the destinationPredefinedAcl parameter.
924	// PredefinedACL is always empty for ObjectAttrs returned from the service.
925	// See https://cloud.google.com/storage/docs/json_api/v1/objects/insert
926	// for valid values.
927	PredefinedACL string
928
929	// Owner is the owner of the object. This field is read-only.
930	//
931	// If non-zero, it is in the form of "user-<userId>".
932	Owner string
933
934	// Size is the length of the object's content. This field is read-only.
935	Size int64
936
937	// ContentEncoding is the encoding of the object's content.
938	ContentEncoding string
939
940	// ContentDisposition is the optional Content-Disposition header of the object
941	// sent in the response headers.
942	ContentDisposition string
943
944	// MD5 is the MD5 hash of the object's content. This field is read-only,
945	// except when used from a Writer. If set on a Writer, the uploaded
946	// data is rejected if its MD5 hash does not match this field.
947	MD5 []byte
948
949	// CRC32C is the CRC32 checksum of the object's content using
950	// the Castagnoli93 polynomial. This field is read-only, except when
951	// used from a Writer. If set on a Writer and Writer.SendCRC32C
952	// is true, the uploaded data is rejected if its CRC32c hash does not
953	// match this field.
954	CRC32C uint32
955
956	// MediaLink is an URL to the object's content. This field is read-only.
957	MediaLink string
958
959	// Metadata represents user-provided metadata, in key/value pairs.
960	// It can be nil if no metadata is provided.
961	Metadata map[string]string
962
963	// Generation is the generation number of the object's content.
964	// This field is read-only.
965	Generation int64
966
967	// Metageneration is the version of the metadata for this
968	// object at this generation. This field is used for preconditions
969	// and for detecting changes in metadata. A metageneration number
970	// is only meaningful in the context of a particular generation
971	// of a particular object. This field is read-only.
972	Metageneration int64
973
974	// StorageClass is the storage class of the object.
975	// This value defines how objects in the bucket are stored and
976	// determines the SLA and the cost of storage. Typical values are
977	// "MULTI_REGIONAL", "REGIONAL", "NEARLINE", "COLDLINE", "STANDARD"
978	// and "DURABLE_REDUCED_AVAILABILITY".
979	// It defaults to "STANDARD", which is equivalent to "MULTI_REGIONAL"
980	// or "REGIONAL" depending on the bucket's location settings.
981	StorageClass string
982
983	// Created is the time the object was created. This field is read-only.
984	Created time.Time
985
986	// Deleted is the time the object was deleted.
987	// If not deleted, it is the zero value. This field is read-only.
988	Deleted time.Time
989
990	// Updated is the creation or modification time of the object.
991	// For buckets with versioning enabled, changing an object's
992	// metadata does not change this property. This field is read-only.
993	Updated time.Time
994
995	// CustomerKeySHA256 is the base64-encoded SHA-256 hash of the
996	// customer-supplied encryption key for the object. It is empty if there is
997	// no customer-supplied encryption key.
998	// See // https://cloud.google.com/storage/docs/encryption for more about
999	// encryption in Google Cloud Storage.
1000	CustomerKeySHA256 string
1001
1002	// Cloud KMS key name, in the form
1003	// projects/P/locations/L/keyRings/R/cryptoKeys/K, used to encrypt this object,
1004	// if the object is encrypted by such a key.
1005	//
1006	// Providing both a KMSKeyName and a customer-supplied encryption key (via
1007	// ObjectHandle.Key) will result in an error when writing an object.
1008	KMSKeyName string
1009
1010	// Prefix is set only for ObjectAttrs which represent synthetic "directory
1011	// entries" when iterating over buckets using Query.Delimiter. See
1012	// ObjectIterator.Next. When set, no other fields in ObjectAttrs will be
1013	// populated.
1014	Prefix string
1015
1016	// Etag is the HTTP/1.1 Entity tag for the object.
1017	// This field is read-only.
1018	Etag string
1019}
1020
1021// convertTime converts a time in RFC3339 format to time.Time.
1022// If any error occurs in parsing, the zero-value time.Time is silently returned.
1023func convertTime(t string) time.Time {
1024	var r time.Time
1025	if t != "" {
1026		r, _ = time.Parse(time.RFC3339, t)
1027	}
1028	return r
1029}
1030
1031func newObject(o *raw.Object) *ObjectAttrs {
1032	if o == nil {
1033		return nil
1034	}
1035	owner := ""
1036	if o.Owner != nil {
1037		owner = o.Owner.Entity
1038	}
1039	md5, _ := base64.StdEncoding.DecodeString(o.Md5Hash)
1040	crc32c, _ := decodeUint32(o.Crc32c)
1041	var sha256 string
1042	if o.CustomerEncryption != nil {
1043		sha256 = o.CustomerEncryption.KeySha256
1044	}
1045	return &ObjectAttrs{
1046		Bucket:                  o.Bucket,
1047		Name:                    o.Name,
1048		ContentType:             o.ContentType,
1049		ContentLanguage:         o.ContentLanguage,
1050		CacheControl:            o.CacheControl,
1051		EventBasedHold:          o.EventBasedHold,
1052		TemporaryHold:           o.TemporaryHold,
1053		RetentionExpirationTime: convertTime(o.RetentionExpirationTime),
1054		ACL:                     toObjectACLRules(o.Acl),
1055		Owner:                   owner,
1056		ContentEncoding:         o.ContentEncoding,
1057		ContentDisposition:      o.ContentDisposition,
1058		Size:                    int64(o.Size),
1059		MD5:                     md5,
1060		CRC32C:                  crc32c,
1061		MediaLink:               o.MediaLink,
1062		Metadata:                o.Metadata,
1063		Generation:              o.Generation,
1064		Metageneration:          o.Metageneration,
1065		StorageClass:            o.StorageClass,
1066		CustomerKeySHA256:       sha256,
1067		KMSKeyName:              o.KmsKeyName,
1068		Created:                 convertTime(o.TimeCreated),
1069		Deleted:                 convertTime(o.TimeDeleted),
1070		Updated:                 convertTime(o.Updated),
1071		Etag:                    o.Etag,
1072	}
1073}
1074
1075// Decode a uint32 encoded in Base64 in big-endian byte order.
1076func decodeUint32(b64 string) (uint32, error) {
1077	d, err := base64.StdEncoding.DecodeString(b64)
1078	if err != nil {
1079		return 0, err
1080	}
1081	if len(d) != 4 {
1082		return 0, fmt.Errorf("storage: %q does not encode a 32-bit value", d)
1083	}
1084	return uint32(d[0])<<24 + uint32(d[1])<<16 + uint32(d[2])<<8 + uint32(d[3]), nil
1085}
1086
1087// Encode a uint32 as Base64 in big-endian byte order.
1088func encodeUint32(u uint32) string {
1089	b := []byte{byte(u >> 24), byte(u >> 16), byte(u >> 8), byte(u)}
1090	return base64.StdEncoding.EncodeToString(b)
1091}
1092
1093// Query represents a query to filter objects from a bucket.
1094type Query struct {
1095	// Delimiter returns results in a directory-like fashion.
1096	// Results will contain only objects whose names, aside from the
1097	// prefix, do not contain delimiter. Objects whose names,
1098	// aside from the prefix, contain delimiter will have their name,
1099	// truncated after the delimiter, returned in prefixes.
1100	// Duplicate prefixes are omitted.
1101	// Optional.
1102	Delimiter string
1103
1104	// Prefix is the prefix filter to query objects
1105	// whose names begin with this prefix.
1106	// Optional.
1107	Prefix string
1108
1109	// Versions indicates whether multiple versions of the same
1110	// object will be included in the results.
1111	Versions bool
1112}
1113
1114// Conditions constrain methods to act on specific generations of
1115// objects.
1116//
1117// The zero value is an empty set of constraints. Not all conditions or
1118// combinations of conditions are applicable to all methods.
1119// See https://cloud.google.com/storage/docs/generations-preconditions
1120// for details on how these operate.
1121type Conditions struct {
1122	// Generation constraints.
1123	// At most one of the following can be set to a non-zero value.
1124
1125	// GenerationMatch specifies that the object must have the given generation
1126	// for the operation to occur.
1127	// If GenerationMatch is zero, it has no effect.
1128	// Use DoesNotExist to specify that the object does not exist in the bucket.
1129	GenerationMatch int64
1130
1131	// GenerationNotMatch specifies that the object must not have the given
1132	// generation for the operation to occur.
1133	// If GenerationNotMatch is zero, it has no effect.
1134	GenerationNotMatch int64
1135
1136	// DoesNotExist specifies that the object must not exist in the bucket for
1137	// the operation to occur.
1138	// If DoesNotExist is false, it has no effect.
1139	DoesNotExist bool
1140
1141	// Metadata generation constraints.
1142	// At most one of the following can be set to a non-zero value.
1143
1144	// MetagenerationMatch specifies that the object must have the given
1145	// metageneration for the operation to occur.
1146	// If MetagenerationMatch is zero, it has no effect.
1147	MetagenerationMatch int64
1148
1149	// MetagenerationNotMatch specifies that the object must not have the given
1150	// metageneration for the operation to occur.
1151	// If MetagenerationNotMatch is zero, it has no effect.
1152	MetagenerationNotMatch int64
1153}
1154
1155func (c *Conditions) validate(method string) error {
1156	if *c == (Conditions{}) {
1157		return fmt.Errorf("storage: %s: empty conditions", method)
1158	}
1159	if !c.isGenerationValid() {
1160		return fmt.Errorf("storage: %s: multiple conditions specified for generation", method)
1161	}
1162	if !c.isMetagenerationValid() {
1163		return fmt.Errorf("storage: %s: multiple conditions specified for metageneration", method)
1164	}
1165	return nil
1166}
1167
1168func (c *Conditions) isGenerationValid() bool {
1169	n := 0
1170	if c.GenerationMatch != 0 {
1171		n++
1172	}
1173	if c.GenerationNotMatch != 0 {
1174		n++
1175	}
1176	if c.DoesNotExist {
1177		n++
1178	}
1179	return n <= 1
1180}
1181
1182func (c *Conditions) isMetagenerationValid() bool {
1183	return c.MetagenerationMatch == 0 || c.MetagenerationNotMatch == 0
1184}
1185
1186// applyConds modifies the provided call using the conditions in conds.
1187// call is something that quacks like a *raw.WhateverCall.
1188func applyConds(method string, gen int64, conds *Conditions, call interface{}) error {
1189	cval := reflect.ValueOf(call)
1190	if gen >= 0 {
1191		if !setConditionField(cval, "Generation", gen) {
1192			return fmt.Errorf("storage: %s: generation not supported", method)
1193		}
1194	}
1195	if conds == nil {
1196		return nil
1197	}
1198	if err := conds.validate(method); err != nil {
1199		return err
1200	}
1201	switch {
1202	case conds.GenerationMatch != 0:
1203		if !setConditionField(cval, "IfGenerationMatch", conds.GenerationMatch) {
1204			return fmt.Errorf("storage: %s: ifGenerationMatch not supported", method)
1205		}
1206	case conds.GenerationNotMatch != 0:
1207		if !setConditionField(cval, "IfGenerationNotMatch", conds.GenerationNotMatch) {
1208			return fmt.Errorf("storage: %s: ifGenerationNotMatch not supported", method)
1209		}
1210	case conds.DoesNotExist:
1211		if !setConditionField(cval, "IfGenerationMatch", int64(0)) {
1212			return fmt.Errorf("storage: %s: DoesNotExist not supported", method)
1213		}
1214	}
1215	switch {
1216	case conds.MetagenerationMatch != 0:
1217		if !setConditionField(cval, "IfMetagenerationMatch", conds.MetagenerationMatch) {
1218			return fmt.Errorf("storage: %s: ifMetagenerationMatch not supported", method)
1219		}
1220	case conds.MetagenerationNotMatch != 0:
1221		if !setConditionField(cval, "IfMetagenerationNotMatch", conds.MetagenerationNotMatch) {
1222			return fmt.Errorf("storage: %s: ifMetagenerationNotMatch not supported", method)
1223		}
1224	}
1225	return nil
1226}
1227
1228func applySourceConds(gen int64, conds *Conditions, call *raw.ObjectsRewriteCall) error {
1229	if gen >= 0 {
1230		call.SourceGeneration(gen)
1231	}
1232	if conds == nil {
1233		return nil
1234	}
1235	if err := conds.validate("CopyTo source"); err != nil {
1236		return err
1237	}
1238	switch {
1239	case conds.GenerationMatch != 0:
1240		call.IfSourceGenerationMatch(conds.GenerationMatch)
1241	case conds.GenerationNotMatch != 0:
1242		call.IfSourceGenerationNotMatch(conds.GenerationNotMatch)
1243	case conds.DoesNotExist:
1244		call.IfSourceGenerationMatch(0)
1245	}
1246	switch {
1247	case conds.MetagenerationMatch != 0:
1248		call.IfSourceMetagenerationMatch(conds.MetagenerationMatch)
1249	case conds.MetagenerationNotMatch != 0:
1250		call.IfSourceMetagenerationNotMatch(conds.MetagenerationNotMatch)
1251	}
1252	return nil
1253}
1254
1255// setConditionField sets a field on a *raw.WhateverCall.
1256// We can't use anonymous interfaces because the return type is
1257// different, since the field setters are builders.
1258func setConditionField(call reflect.Value, name string, value interface{}) bool {
1259	m := call.MethodByName(name)
1260	if !m.IsValid() {
1261		return false
1262	}
1263	m.Call([]reflect.Value{reflect.ValueOf(value)})
1264	return true
1265}
1266
1267// conditionsQuery returns the generation and conditions as a URL query
1268// string suitable for URL.RawQuery.  It assumes that the conditions
1269// have been validated.
1270func conditionsQuery(gen int64, conds *Conditions) string {
1271	// URL escapes are elided because integer strings are URL-safe.
1272	var buf []byte
1273
1274	appendParam := func(s string, n int64) {
1275		if len(buf) > 0 {
1276			buf = append(buf, '&')
1277		}
1278		buf = append(buf, s...)
1279		buf = strconv.AppendInt(buf, n, 10)
1280	}
1281
1282	if gen >= 0 {
1283		appendParam("generation=", gen)
1284	}
1285	if conds == nil {
1286		return string(buf)
1287	}
1288	switch {
1289	case conds.GenerationMatch != 0:
1290		appendParam("ifGenerationMatch=", conds.GenerationMatch)
1291	case conds.GenerationNotMatch != 0:
1292		appendParam("ifGenerationNotMatch=", conds.GenerationNotMatch)
1293	case conds.DoesNotExist:
1294		appendParam("ifGenerationMatch=", 0)
1295	}
1296	switch {
1297	case conds.MetagenerationMatch != 0:
1298		appendParam("ifMetagenerationMatch=", conds.MetagenerationMatch)
1299	case conds.MetagenerationNotMatch != 0:
1300		appendParam("ifMetagenerationNotMatch=", conds.MetagenerationNotMatch)
1301	}
1302	return string(buf)
1303}
1304
1305// composeSourceObj wraps a *raw.ComposeRequestSourceObjects, but adds the methods
1306// that modifyCall searches for by name.
1307type composeSourceObj struct {
1308	src *raw.ComposeRequestSourceObjects
1309}
1310
1311func (c composeSourceObj) Generation(gen int64) {
1312	c.src.Generation = gen
1313}
1314
1315func (c composeSourceObj) IfGenerationMatch(gen int64) {
1316	// It's safe to overwrite ObjectPreconditions, since its only field is
1317	// IfGenerationMatch.
1318	c.src.ObjectPreconditions = &raw.ComposeRequestSourceObjectsObjectPreconditions{
1319		IfGenerationMatch: gen,
1320	}
1321}
1322
1323func setEncryptionHeaders(headers http.Header, key []byte, copySource bool) error {
1324	if key == nil {
1325		return nil
1326	}
1327	// TODO(jbd): Ask the API team to return a more user-friendly error
1328	// and avoid doing this check at the client level.
1329	if len(key) != 32 {
1330		return errors.New("storage: not a 32-byte AES-256 key")
1331	}
1332	var cs string
1333	if copySource {
1334		cs = "copy-source-"
1335	}
1336	headers.Set("x-goog-"+cs+"encryption-algorithm", "AES256")
1337	headers.Set("x-goog-"+cs+"encryption-key", base64.StdEncoding.EncodeToString(key))
1338	keyHash := sha256.Sum256(key)
1339	headers.Set("x-goog-"+cs+"encryption-key-sha256", base64.StdEncoding.EncodeToString(keyHash[:]))
1340	return nil
1341}
1342
1343// ServiceAccount fetches the email address of the given project's Google Cloud Storage service account.
1344func (c *Client) ServiceAccount(ctx context.Context, projectID string) (string, error) {
1345	r := c.raw.Projects.ServiceAccount.Get(projectID)
1346	res, err := r.Context(ctx).Do()
1347	if err != nil {
1348		return "", err
1349	}
1350	return res.EmailAddress, nil
1351}
1352