%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /home/waritko/go/src/github.com/odeke-em/drive/src/
Upload File :
Create Path :
Current File : //home/waritko/go/src/github.com/odeke-em/drive/src/share.go

// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package drive

import (
	"fmt"
	"strings"
	"sync"

	"github.com/odeke-em/log"
)

type AccountType int

const (
	UnknownAccountType AccountType = 1 << iota
	Anyone
	User
	Domain
	Group
)

type Role int

const (
	UnknownRole Role = 1 << iota
	Owner
	Reader
	Writer
	Commenter
)

const (
	NoopOnShare = 1 << iota
	Notify
	WithLink
)

type shareChange struct {
	emailMessage string
	emails       []string
	roles        []Role
	accountTypes []AccountType
	files        []*File
	revoke       bool
	notify       bool
	// withLink turns off public file indexing so that
	// the file will only be shared to those with the link
	withLink bool
}

type permission struct {
	fileId      string
	value       string
	message     string
	role        Role
	accountType AccountType

	notify   bool
	withLink bool
}

func (r *Role) String() string {
	switch *r {
	case Owner:
		return "owner"
	case Reader:
		return "reader"
	case Writer:
		return "writer"
	case Commenter:
		return "commenter"
	}
	return "unknown"
}

func unknownRole(r string) bool {
	return strings.ToLower(r) == "unknown"
}

func (a *AccountType) String() string {
	switch *a {
	case Anyone:
		return "anyone"
	case User:
		return "user"
	case Domain:
		return "domain"
	case Group:
		return "group"
	}
	return "unknown"
}

func unknownAccountType(a string) bool {
	return strings.ToLower(a) == "unknown"
}

func stringToRole() func(string) Role {
	roleMap := make(map[string]Role)
	roles := []Role{UnknownRole, Owner, Reader, Writer, Commenter}
	for _, role := range roles {
		roleMap[role.String()] = role
	}
	return func(s string) Role {
		r, ok := roleMap[strings.ToLower(s)]
		if !ok {
			return Reader
		}
		return r
	}
}

func stringToAccountType() func(string) AccountType {
	accountMap := make(map[string]AccountType)
	accounts := []AccountType{UnknownAccountType, Anyone, User, Domain, Group}
	for _, account := range accounts {
		accountMap[account.String()] = account
	}
	return func(s string) AccountType {
		a, ok := accountMap[strings.ToLower(s)]
		if !ok {
			return User
		}
		return a
	}
}

var reverseRoleResolve = stringToRole()
var reverseAccountTypeResolve = stringToAccountType()

func reverseRolesResolver(roleArgv ...string) (roles []Role) {
	alreadySeen := map[Role]bool{}
	for _, roleStr := range roleArgv {
		role := reverseRoleResolve(roleStr)
		if _, seen := alreadySeen[role]; !seen {
			roles = append(roles, role)
			alreadySeen[role] = true
		}
	}

	return roles
}

func reverseAccountTypesResolver(accArgv ...string) (accountTypes []AccountType) {
	for _, accStr := range accArgv {
		accountTypes = append(accountTypes, reverseAccountTypeResolve(accStr))
	}

	return accountTypes
}

func (g *Commands) resolveRemotePaths(relToRootPaths []string, byId bool) (files []*File) {
	var wg sync.WaitGroup

	resolver := g.rem.FindByPath
	if byId {
		resolver = g.rem.FindById
	}

	wg.Add(len(relToRootPaths))
	for _, relToRoot := range relToRootPaths {
		go func(p string, wgg *sync.WaitGroup) {
			defer wgg.Done()
			file, err := resolver(p)
			if err != nil || file == nil {
				return
			}
			files = append(files, file)
		}(relToRoot, &wg)
	}
	wg.Wait()
	return files
}

func emailsToIds(g *Commands, emails []string) map[string]string {
	emailToIds := make(map[string]string)
	var wg sync.WaitGroup
	wg.Add(len(emails))
	for _, email := range emails {
		go func(email string, wgg *sync.WaitGroup) {
			defer wgg.Done()
			emailId, err := g.rem.idForEmail(email)
			if err == nil {
				emailToIds[email] = emailId
			}
		}(email, &wg)
	}
	wg.Wait()
	return emailToIds
}

func (c *Commands) Unshare(byId bool) (err error) {
	return c.share(true, byId)
}

func (c *Commands) Share(byId bool) (err error) {
	return c.share(false, byId)
}

func showPromptShareChanges(logy *log.Logger, change *shareChange) Agreement {
	if len(change.files) < 1 {
		return NotApplicable
	}

	verb := "unshare"
	shareInfo := "Revoke access"
	extraShareInfo := ""
	if !change.revoke {
		shareInfo = "Provide access"
		if len(change.emails) < 1 {
			return NotApplicable
		}

		verb = "share"
		if change.notify && len(change.emailMessage) >= 1 {
			extraShareInfo = fmt.Sprintf("Message:\n\t\033[33m%s\033[00m\n", change.emailMessage)
		}
	}

	if len(change.accountTypes) >= 1 {
		logy.Logf("%s for accountType(s)\n", shareInfo)
		for _, accountType := range change.accountTypes {
			logy.Logf("\t\033[32m%s\033[00m\n", accountType.String())
		}

		logy.Logln(extraShareInfo)
	}

	if len(change.roles) >= 1 {
		logy.Logln("For roles(s)")
		for _, role := range change.roles {
			logy.Logf("\t\033[33m%s\033[00m\n", role.String())
		}
	}

	if len(change.emails) >= 1 {
		logy.Logf("\nAddressees:\n")
		for _, email := range change.emails {
			if email == "" {
				email = "Anyone with the link"
			}
			logy.Logf("\t\033[92m+\033[00m %s\n", email)
		}
	}

	logy.Logf("\nFile(s) to %s:\n", verb)
	for _, file := range change.files {
		if file == nil {
			continue
		}
		logy.Logf("\t\033[92m+\033[00m %s\n", file.Name)
	}

	logy.Logln()
	return promptForChanges()
}

func (c *Commands) playShareChanges(change *shareChange) (err error) {
	if c.opts.canPrompt() {
		if status := showPromptShareChanges(c.log, change); !accepted(status) {
			return status.Error()
		}
	}

	fnName := "unshare"
	fn := c.rem.revokePermissions

	if !change.revoke {
		fnName = "share"
		fn = func(perm *permission) error {
			_, err := c.rem.insertPermissions(perm)
			return err
		}
	}

	successes := 0

	for _, file := range change.files {
		for _, accountType := range change.accountTypes {
			for _, email := range change.emails {
				if accountType == Anyone && email != "" {
					// It doesn't make sense to share a file with
					// permission "anyone" yet provide an email
					continue
				}

				for _, role := range change.roles {
					perm := permission{
						fileId:      file.Id,
						value:       email,
						role:        role,
						accountType: accountType,

						notify:   change.notify,
						message:  change.emailMessage,
						withLink: change.withLink,
					}

					if ferr := fn(&perm); ferr != nil {
						err = reComposeError(err, fmt.Sprintf("%s err %s: %v\n", fnName, file.Name, ferr))
					} else {
						successes += 1
						if c.opts.Verbose {
							c.log.Logf("successful %s for %s with email %q, role %q accountType %q\n",
								fnName, file.Name, email, role.String(), accountType.String())
						}
					}
				}
			}
		}
	}

	if err != nil {
		return err
	}

	if successes < 1 {
		return noMatchesFoundErr(fmt.Errorf("no matches found!"))
	}

	return nil
}

func (c *Commands) share(revoke, byId bool) (err error) {
	files := c.resolveRemotePaths(c.opts.Sources, byId)

	var emails []string
	var emailMessage string

	roles := []Role{}

	// By default, if they don't specify any
	// account types to invoke, let's assume User.
	// See Issue https://github.com/odeke-em/drive/issues/747.
	accountTypes := []AccountType{User}

	if revoke {
		// In case of unsharing, when a user doesn't specify the
		// roles, the addressee should be removed from all roles
		roles = append(roles, Reader, Writer, Commenter)
	} else {
		roles = append(roles, Reader)
	}

	meta := *c.opts.Meta

	if meta != nil {
		emailList, eOk := meta[EmailsKey]
		if eOk {
			emails = emailList
			if false {
				emailIdMap := emailsToIds(c, emailList)
				c.log.Logln(emailIdMap)
			}
		}

		roleList, rOk := meta[RoleKey]
		if rOk && len(roleList) >= 1 {
			roles = reverseRolesResolver(roleList...)
		}

		accountTypeList, aOk := meta[AccountTypeKey]
		if aOk && len(accountTypeList) >= 1 {
			accountTypes = reverseAccountTypesResolver(accountTypeList...)
		}

		emailMessageList, emOk := meta[EmailMessageKey]
		if emOk && len(emailMessageList) >= 1 {
			emailMessage = strings.Join(emailMessageList, "\n")
		}
	}

	if revoke && len(emails) < 1 {
		// Account for case for unshare where certain types do not include the emails
		// e.g revoking access to the entire domain, or user groups yet no email
		// can be found
		emails = append(emails, "")
	}

	anyoneAlreadySet := false
	// Now here we have to match up emails with roles
	// See Issue #568. If we've requested say `anyone`, this role needs no email
	// address, so we should add an empty "" to the list of emails
	for _, accountType := range accountTypes {
		if accountType == Anyone {
			anyoneAlreadySet = true
			emails = append(emails, "")
		}
	}

	change := shareChange{
		files:  files,
		revoke: revoke,
		roles:  roles,
		emails: emails,

		accountTypes: accountTypes,
		emailMessage: emailMessage,

		notify:   (c.opts.TypeMask & Notify) == Notify,
		withLink: (c.opts.TypeMask & WithLink) == WithLink,
	}

	if len(change.emails) < 1 && change.withLink { // They've basically requested a share to everyone but with link
		change.emails = append(change.emails, "")
		if !anyoneAlreadySet {
			change.accountTypes = append(change.accountTypes, Anyone)
		}
	}

	return c.playShareChanges(&change)
}

Zerion Mini Shell 1.0