Repository: rclone/rclone
File: backend/dropbox/dropbox.go (Go)

Code before:

// Open an object for read
func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (in io.ReadCloser, err error) {
	if o.fs.opt.SharedFiles {
		if len(options) != 0 {
			return nil, errors.New("OpenOptions not supported for shared files")
		}
		arg := sharing.GetSharedLinkMetadataArg{
			Url: o.url,
		}
		err = o.fs.pacer.Call(func() (bool, error) {
			_, in, err = o.fs.sharing.GetSharedLinkFile(&arg)
			return shouldRetry(ctx, err)
		})
		if err != nil {
			return nil, err
		}
		return
	}

	if o.non_downloadable {
		arg := files.ExportArg{Path: o.id, ExportFormat: o.export_format}
		err = o.fs.pacer.Call(func() (bool, error) {
			var exportResult *files.ExportResult
			exportResult, in, err = o.fs.srv.Export(&arg)
			o.bytes = int64(exportResult.ExportMetadata.Size)
			o.hash = exportResult.ExportMetadata.ExportHash
			return shouldRetry(ctx, err)
		})
		return
	}

	fs.FixRangeOption(options, o.bytes)
	headers := fs.OpenOptionHeaders(options)
	arg := files.DownloadArg{
		Path:         o.id,
		ExtraHeaders: headers,
	}
	err = o.fs.pacer.Call(func() (bool, error) {
		_, in, err = o.fs.srv.Download(&arg)
		return shouldRetry(ctx, err)
	})

	switch e := err.(type) {
	case files.DownloadAPIError:
		// Don't attempt to retry copyright violation errors
		if e.EndpointError != nil && e.EndpointError.Path != nil && e.EndpointError.Path.Tag == files.LookupErrorRestrictedContent {
			return nil, fserrors.NoRetryError(err)
		}
	}


Review comment (suggestion):
These should go after the `o.fs.pacer.Call` when you are sure it has suceeded otherwise you'll certainly case a `nil` pointer exception.

Code after:
func (f *Fs) Hashes() hash.Set {
	return hash.Set(DbHashType)
}

// Shutdown the backend, closing any background tasks and any
// cached connections.
func (f *Fs) Shutdown(ctx context.Context) error {
	f.batcher.Shutdown()
	return nil
}

func (f *Fs) chooseExportFormat(info *files.FileMetadata) (exportAPIFormat, exportExtension) {
	// Find API export formats Dropbox supports for this file
	// Sometimes Dropbox lists a format in ExportAs but not ExportOptions, so check both
	ei := info.ExportInfo
	dropboxFormatStrings := append([]string{ei.ExportAs}, ei.ExportOptions...)

	// Find which extensions these correspond to
	exportExtensions := map[exportExtension]exportAPIFormat{}
	var dropboxPreferredAPIFormat exportAPIFormat
	var dropboxPreferredExtension exportExtension
	for _, format := range dropboxFormatStrings {
		apiFormat := exportAPIFormat(format)
		// Only consider formats we know about
		if ext, ok := exportKnownAPIFormats[apiFormat]; ok {
			if dropboxPreferredAPIFormat == "" {
				dropboxPreferredAPIFormat = apiFormat
				dropboxPreferredExtension = ext
			}
			exportExtensions[ext] = apiFormat
		}
	}

	// See if the user picked a valid extension
	for _, ext := range f.exportExts {
		if apiFormat, ok := exportExtensions[ext]; ok {
			return apiFormat, ext
		}
	}

	// If no matches, prefer the first valid format Dropbox lists
	return dropboxPreferredAPIFormat, dropboxPreferredExtension
}

// ------------------------------------------------------------

func (et exportType) listable() bool {
	return et != exportHide
}

// something we should _try_ to export