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