<commit_before>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	"fmt"
	"io"
	"os"
	"sync"

	"github.com/syndtr/goleveldb/leveldb/errors"
	"github.com/syndtr/goleveldb/leveldb/journal"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/storage"
)

// ErrManifestCorrupted records manifest corruption. This error will be
// wrapped with errors.ErrCorrupted.
type ErrManifestCorrupted struct {
	Field  string
	Reason string
}

func (e *ErrManifestCorrupted) Error() string {
	return fmt.Sprintf("leveldb: manifest corrupted (field '%s'): %s", e.Field, e.Reason)
}

func newErrManifestCorrupted(fd storage.FileDesc, field, reason string) error {
	return errors.NewErrCorrupted(fd, &ErrManifestCorrupted{field, reason})
}

// session represent a persistent database session.
type session struct {
	// Need 64-bit alignment.
	stNextFileNum    int64 // current unused file number
	stJournalNum     int64 // current journal file number; need external synchronization
	stPrevJournalNum int64 // prev journal file number; no longer used; for compatibility with older version of leveldb
	stTempFileNum    int64
	stSeqNum         uint64 // last mem compacted seq; need external synchronization

	stor     *iStorage
	storLock storage.Locker
	o        *cachedOptions
	icmp     *iComparer
	tops     *tOps

	manifest       *journal.Writer
	manifestWriter storage.Writer
	manifestFd     storage.FileDesc

	stCompPtrs  []internalKey // compaction pointers; need external synchronization
	stVersion   *version      // current version
	ntVersionID int64         // next version id to assign
	refCh       chan *vTask
	relCh       chan *vTask
	deltaCh     chan *vDelta
	abandon     chan int64
	closeC      chan struct{}
	closeW      sync.WaitGroup
	vmu         sync.Mutex

	// Testing fields
	fileRefCh chan chan map[int64]int // channel used to pass current reference stat
}

// Creates new initialized session instance.
func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
	if stor == nil {
		return nil, os.ErrInvalid
	}
	storLock, err := stor.Lock()
	if err != nil {
		return
	}
	s = &session{
		stor:      newIStorage(stor),
		storLock:  storLock,
		refCh:     make(chan *vTask),
		relCh:     make(chan *vTask),
		deltaCh:   make(chan *vDelta),
		abandon:   make(chan int64),
		fileRefCh: make(chan chan map[int64]int),
		closeC:    make(chan struct{}),
	}
	s.setOptions(o)
	s.tops = newTableOps(s)

	s.closeW.Add(1)
	go s.refLoop()
	s.setVersion(nil, newVersion(s))
	s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
	return
}

// Close session.
func (s *session) close() {
	s.tops.close()
	if s.manifest != nil {
		s.manifest.Close()
	}
	if s.manifestWriter != nil {
		s.manifestWriter.Close()
	}
	s.manifest = nil
	s.manifestWriter = nil
	s.setVersion(nil, &version{s: s, closing: true, id: s.ntVersionID})

	// Close all background goroutines
	close(s.closeC)
	s.closeW.Wait()
}

// Release session lock.
func (s *session) release() {
	s.storLock.Unlock()
}

// Create a new database session; need external synchronization.
func (s *session) create() error {
	// create manifest
	return s.newManifest(nil, nil)
}

// Recover a database session; need external synchronization.
func (s *session) recover() (err error) {
	defer func() {
		if os.IsNotExist(err) {
			// Don't return os.ErrNotExist if the underlying storage contains
			// other files that belong to LevelDB. So the DB won't get trashed.
			if fds, _ := s.stor.List(storage.TypeAll); len(fds) > 0 {
				err = &errors.ErrCorrupted{Err: errors.New("database entry point either missing or corrupted")}
			}
		}
	}()

	fd, err := s.stor.GetMeta()
	if err != nil {
		return
	}

	reader, err := s.stor.Open(fd)
	if err != nil {
		return
	}
	defer reader.Close()

	var (
		// Options.
		strict = s.o.GetStrict(opt.StrictManifest)

		jr      = journal.NewReader(reader, dropper{s, fd}, strict, true)
		rec     = &sessionRecord{}
		staging = s.stVersion.newStaging()
	)
	for {
		var r io.Reader
		r, err = jr.Next()
		if err != nil {
			if err == io.EOF {
				err = nil
				break
			}
			return errors.SetFd(err, fd)
		}

		err = rec.decode(r)
		if err == nil {
			// save compact pointers
			for _, r := range rec.compPtrs {
				s.setCompPtr(r.level, r.ikey)
			}
			// commit record to version staging
			staging.commit(rec)
		} else {
			err = errors.SetFd(err, fd)
			if strict || !errors.IsCorrupted(err) {
				return
			}
			s.logf("manifest error: %v (skipped)", errors.SetFd(err, fd))
		}
		rec.resetCompPtrs()
		rec.resetAddedTables()
		rec.resetDeletedTables()
	}

	switch {
	case !rec.has(recComparer):
		return newErrManifestCorrupted(fd, "comparer", "missing")
	case rec.comparer != s.icmp.uName():
		return newErrManifestCorrupted(fd, "comparer", fmt.Sprintf("mismatch: want '%s', got '%s'", s.icmp.uName(), rec.comparer))
	case !rec.has(recNextFileNum):
		return newErrManifestCorrupted(fd, "next-file-num", "missing")
	case !rec.has(recJournalNum):
		return newErrManifestCorrupted(fd, "journal-file-num", "missing")
	case !rec.has(recSeqNum):
		return newErrManifestCorrupted(fd, "seq-num", "missing")
	}

	s.manifestFd = fd
	s.setVersion(rec, staging.finish(false))
	s.setNextFileNum(rec.nextFileNum)
	s.recordCommited(rec)
	return nil
}

// Commit session; need external synchronization.
func (s *session) commit(r *sessionRecord, trivial bool) (err error) {
	v := s.version()
	defer v.release()

	// spawn new version based on current version
	nv := v.spawn(r, trivial)

	// abandon useless version id to prevent blocking version processing loop.
	defer func() {
		if err != nil {
			s.abandon <- nv.id
			s.logf("commit@abandon useless vid D%d", nv.id)
		}
	}()

	if s.manifest == nil || s.manifest.Size() >= s.o.GetMaxManifestFileSize() {
		// manifest journal writer not yet created, create one
		err = s.newManifest(r, nv)
	} else {
		err = s.flushManifest(r)
	}

	// finally, apply new version if no error rise
	if err == nil {
		s.setVersion(r, nv)
	}

	return
}
<commit_msg>leveldb: fix table file leaks when manifest is rotated (#409)<commit_after>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	"fmt"
	"io"
	"os"
	"sync"

	"github.com/syndtr/goleveldb/leveldb/errors"
	"github.com/syndtr/goleveldb/leveldb/journal"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/storage"
)

// ErrManifestCorrupted records manifest corruption. This error will be
// wrapped with errors.ErrCorrupted.
type ErrManifestCorrupted struct {
	Field  string
	Reason string
}

func (e *ErrManifestCorrupted) Error() string {
	return fmt.Sprintf("leveldb: manifest corrupted (field '%s'): %s", e.Field, e.Reason)
}

func newErrManifestCorrupted(fd storage.FileDesc, field, reason string) error {
	return errors.NewErrCorrupted(fd, &ErrManifestCorrupted{field, reason})
}

// session represent a persistent database session.
type session struct {
	// Need 64-bit alignment.
	stNextFileNum    int64 // current unused file number
	stJournalNum     int64 // current journal file number; need external synchronization
	stPrevJournalNum int64 // prev journal file number; no longer used; for compatibility with older version of leveldb
	stTempFileNum    int64
	stSeqNum         uint64 // last mem compacted seq; need external synchronization

	stor     *iStorage
	storLock storage.Locker
	o        *cachedOptions
	icmp     *iComparer
	tops     *tOps

	manifest       *journal.Writer
	manifestWriter storage.Writer
	manifestFd     storage.FileDesc

	stCompPtrs  []internalKey // compaction pointers; need external synchronization
	stVersion   *version      // current version
	ntVersionID int64         // next version id to assign
	refCh       chan *vTask
	relCh       chan *vTask
	deltaCh     chan *vDelta
	abandon     chan int64
	closeC      chan struct{}
	closeW      sync.WaitGroup
	vmu         sync.Mutex

	// Testing fields
	fileRefCh chan chan map[int64]int // channel used to pass current reference stat
}

// Creates new initialized session instance.
func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
	if stor == nil {
		return nil, os.ErrInvalid
	}
	storLock, err := stor.Lock()
	if err != nil {
		return
	}
	s = &session{
		stor:      newIStorage(stor),
		storLock:  storLock,
		refCh:     make(chan *vTask),
		relCh:     make(chan *vTask),
		deltaCh:   make(chan *vDelta),
		abandon:   make(chan int64),
		fileRefCh: make(chan chan map[int64]int),
		closeC:    make(chan struct{}),
	}
	s.setOptions(o)
	s.tops = newTableOps(s)

	s.closeW.Add(1)
	go s.refLoop()
	s.setVersion(nil, newVersion(s))
	s.log("log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed")
	return
}

// Close session.
func (s *session) close() {
	s.tops.close()
	if s.manifest != nil {
		s.manifest.Close()
	}
	if s.manifestWriter != nil {
		s.manifestWriter.Close()
	}
	s.manifest = nil
	s.manifestWriter = nil
	s.setVersion(nil, &version{s: s, closing: true, id: s.ntVersionID})

	// Close all background goroutines
	close(s.closeC)
	s.closeW.Wait()
}

// Release session lock.
func (s *session) release() {
	s.storLock.Unlock()
}

// Create a new database session; need external synchronization.
func (s *session) create() error {
	// create manifest
	return s.newManifest(nil, nil)
}

// Recover a database session; need external synchronization.
func (s *session) recover() (err error) {
	defer func() {
		if os.IsNotExist(err) {
			// Don't return os.ErrNotExist if the underlying storage contains
			// other files that belong to LevelDB. So the DB won't get trashed.
			if fds, _ := s.stor.List(storage.TypeAll); len(fds) > 0 {
				err = &errors.ErrCorrupted{Err: errors.New("database entry point either missing or corrupted")}
			}
		}
	}()

	fd, err := s.stor.GetMeta()
	if err != nil {
		return
	}

	reader, err := s.stor.Open(fd)
	if err != nil {
		return
	}
	defer reader.Close()

	var (
		// Options.
		strict = s.o.GetStrict(opt.StrictManifest)

		jr      = journal.NewReader(reader, dropper{s, fd}, strict, true)
		rec     = &sessionRecord{}
		staging = s.stVersion.newStaging()
	)
	for {
		var r io.Reader
		r, err = jr.Next()
		if err != nil {
			if err == io.EOF {
				err = nil
				break
			}
			return errors.SetFd(err, fd)
		}

		err = rec.decode(r)
		if err == nil {
			// save compact pointers
			for _, r := range rec.compPtrs {
				s.setCompPtr(r.level, r.ikey)
			}
			// commit record to version staging
			staging.commit(rec)
		} else {
			err = errors.SetFd(err, fd)
			if strict || !errors.IsCorrupted(err) {
				return
			}
			s.logf("manifest error: %v (skipped)", errors.SetFd(err, fd))
		}
		rec.resetCompPtrs()
		rec.resetAddedTables()
		rec.resetDeletedTables()
	}

	switch {
	case !rec.has(recComparer):
		return newErrManifestCorrupted(fd, "comparer", "missing")
	case rec.comparer != s.icmp.uName():
		return newErrManifestCorrupted(fd, "comparer", fmt.Sprintf("mismatch: want '%s', got '%s'", s.icmp.uName(), rec.comparer))
	case !rec.has(recNextFileNum):
		return newErrManifestCorrupted(fd, "next-file-num", "missing")
	case !rec.has(recJournalNum):
		return newErrManifestCorrupted(fd, "journal-file-num", "missing")
	case !rec.has(recSeqNum):
		return newErrManifestCorrupted(fd, "seq-num", "missing")
	}

	s.manifestFd = fd
	s.setVersion(rec, staging.finish(false))
	s.setNextFileNum(rec.nextFileNum)
	s.recordCommited(rec)
	return nil
}

// Commit session; need external synchronization.
func (s *session) commit(r *sessionRecord, trivial bool) (err error) {
	v := s.version()
	defer v.release()

	// spawn new version based on current version
	nv := v.spawn(r, trivial)

	// abandon useless version id to prevent blocking version processing loop.
	defer func() {
		if err != nil {
			s.abandon <- nv.id
			s.logf("commit@abandon useless vid D%d", nv.id)
		}
	}()

	if s.manifest == nil {
		// manifest journal writer not yet created, create one
		err = s.newManifest(r, nv)
	} else if s.manifest.Size() >= s.o.GetMaxManifestFileSize() {
		// pass nil sessionRecord to avoid over-reference table file
		err = s.newManifest(nil, nv)
	} else {
		err = s.flushManifest(r)
	}

	// finally, apply new version if no error rise
	if err == nil {
		s.setVersion(r, nv)
	}

	return
}
<|endoftext|>


<commit_before>// Copyright © 2014 Daniele Tricoli <eriol@mornie.org>.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package irc // import "eriol.xyz/perpetua/irc"

import (
	"crypto/tls"
	"fmt"
	"log"
	"regexp"
	"strings"

	"github.com/thoj/go-ircevent"

	"eriol.xyz/perpetua/config"
	"eriol.xyz/perpetua/db"
)

const version = "perpetua quote bot " + config.Version

var connection *irc.Connection
var conf *config.Config
var store *db.Store

// Localizated quote and about tokens used to detect the kind of query for
// the bot.
var i18n = map[string]map[string][]string{
	"en": map[string][]string{
		"quote": []string{"quote", "what does it say"},
		"about": []string{"about"},
	},
	"it": map[string][]string{
		"quote": []string{
			"cita",
			"che dice",
			"cosa dice",
			"che cosa dice"},
		"about": []string{
			"su",
			"sul",
			"sulla",
			"sullo",
			"sui",
			"sugli",
			"sulle"},
	},
}

// Join keys from i18n using "|": used inside the regex to perform an
// OR of all keys.
func i18nKeyJoin(lang, key string) string {
	return strings.Join(i18n[lang][key], "|")
}

func connect() {
	connection = irc.IRC(conf.IRC.Nickname, conf.IRC.User)
	connection.Version = version
	connection.UseTLS = conf.Server.UseTLS
	if conf.Server.SkipVerify == true {
		connection.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	}

	err := connection.Connect(fmt.Sprintf("%s:%d",
		conf.Server.Hostname,
		conf.Server.Port))

	if err != nil {
		log.Fatal(err)
	}
}

func handleEvents() {
	connection.AddCallback("001", doWelcome)
	connection.AddCallback("JOIN", doJoin)
	connection.AddCallback("PRIVMSG", doPrivmsg)
}

func doWelcome(event *irc.Event) {
	for _, channel := range conf.IRC.Channels {
		event.Connection.Join(channel)
		event.Connection.Log.Println("Joined to " + channel)
	}
}

func doJoin(event *irc.Event) {
	channel := event.Arguments[0]

	if event.Nick == conf.IRC.Nickname {
		event.Connection.Privmsg(channel, "Hello! I'm "+version)
	} else {
		event.Connection.Privmsg(channel,
			fmt.Sprintf("Hello %s! I'm %s. Do you want a quote?",
				event.Nick,
				version))
	}
}

func doPrivmsg(event *irc.Event) {
	channel := event.Arguments[0]
	var quote string

	// Don't speak in private!
	if channel == conf.IRC.Nickname {
		return
	}
	command, person, extra, argument := parseMessage(event.Message())

	if command != "" && person != "" {

		quote = store.GetQuote(person, channel)

		if extra != "" && argument != "" {
			quote = store.GetQuoteAbout(person, argument, channel)
		}

		event.Connection.Privmsg(channel, quote)
	}
}

func parseMessage(message string) (command, person, extra, argument string) {
	var names []string
	lang := conf.I18N.Lang

	reArgument := regexp.MustCompile(conf.IRC.Nickname +
		`:?` +
		`\s+` +
		`(?P<command>` + i18nKeyJoin(lang, "quote") + `)` +
		`\s+` +
		`(?P<person>[\w\s-'\p{Latin}]+)` +
		`(?:\s+)` +
		`(?P<extra>` + i18nKeyJoin(lang, "about") + `)` +
		`(?:\s+)` +
		`(?P<argument>[\w\s-'\p{Latin}]+)`)

	re := regexp.MustCompile(conf.IRC.Nickname +
		`:?` +
		`\s+` +
		`(?P<command>` + i18nKeyJoin(lang, "quote") + `)` +
		`\s+` +
		`(?P<person>[\w\s-'\p{Latin}]+)`)

	res := reArgument.FindStringSubmatch(message)

	if res == nil {
		res = re.FindStringSubmatch(message)
		names = re.SubexpNames()
	} else {
		names = reArgument.SubexpNames()
	}

	m := map[string]string{}
	for i, n := range res {
		m[names[i]] = n
	}

	return m["command"], m["person"], m["extra"], m["argument"]
}

func Client(c *config.Config, db *db.Store) {
	conf = c
	store = db
	connect()
	handleEvents()
	connection.Loop()
}
<commit_msg>Remove global connection variable! :dancers:<commit_after>// Copyright © 2014 Daniele Tricoli <eriol@mornie.org>.
// All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package irc // import "eriol.xyz/perpetua/irc"

import (
	"crypto/tls"
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/thoj/go-ircevent"

	"eriol.xyz/perpetua/config"
	"eriol.xyz/perpetua/db"
)

const version = "perpetua quote bot " + config.Version

var conf *config.Config
var store *db.Store

// Localizated quote and about tokens used to detect the kind of query for
// the bot.
var i18n = map[string]map[string][]string{
	"en": map[string][]string{
		"quote": []string{"quote", "what does it say"},
		"about": []string{"about"},
	},
	"it": map[string][]string{
		"quote": []string{
			"cita",
			"che dice",
			"cosa dice",
			"che cosa dice"},
		"about": []string{
			"su",
			"sul",
			"sulla",
			"sullo",
			"sui",
			"sugli",
			"sulle"},
	},
}

// Join keys from i18n using "|": used inside the regex to perform an
// OR of all keys.
func i18nKeyJoin(lang, key string) string {
	return strings.Join(i18n[lang][key], "|")
}

func connect() (connection *irc.Connection, err error) {
	connection = irc.IRC(conf.IRC.Nickname, conf.IRC.User)
	connection.Version = version
	connection.UseTLS = conf.Server.UseTLS
	if conf.Server.SkipVerify == true {
		connection.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	}

	if err := connection.Connect(fmt.Sprintf("%s:%d",
		conf.Server.Hostname,
		conf.Server.Port)); err != nil {
		return nil, err
	}

	return connection, nil
}

func doWelcome(event *irc.Event) {
	for _, channel := range conf.IRC.Channels {
		event.Connection.Join(channel)
		event.Connection.Log.Println("Joined to " + channel)
	}
}

func doJoin(event *irc.Event) {
	channel := event.Arguments[0]

	if event.Nick == conf.IRC.Nickname {
		event.Connection.Privmsg(channel, "Hello! I'm "+version)
	} else {
		event.Connection.Privmsg(channel,
			fmt.Sprintf("Hello %s! I'm %s. Do you want a quote?",
				event.Nick,
				version))
	}
}

func doPrivmsg(event *irc.Event) {
	channel := event.Arguments[0]
	var quote string

	// Don't speak in private!
	if channel == conf.IRC.Nickname {
		return
	}
	command, person, extra, argument := parseMessage(event.Message())

	if command != "" && person != "" {

		quote = store.GetQuote(person, channel)

		if extra != "" && argument != "" {
			quote = store.GetQuoteAbout(person, argument, channel)
		}

		event.Connection.Privmsg(channel, quote)
	}
}

func parseMessage(message string) (command, person, extra, argument string) {
	var names []string
	lang := conf.I18N.Lang

	reArgument := regexp.MustCompile(conf.IRC.Nickname +
		`:?` +
		`\s+` +
		`(?P<command>` + i18nKeyJoin(lang, "quote") + `)` +
		`\s+` +
		`(?P<person>[\w\s-'\p{Latin}]+)` +
		`(?:\s+)` +
		`(?P<extra>` + i18nKeyJoin(lang, "about") + `)` +
		`(?:\s+)` +
		`(?P<argument>[\w\s-'\p{Latin}]+)`)

	re := regexp.MustCompile(conf.IRC.Nickname +
		`:?` +
		`\s+` +
		`(?P<command>` + i18nKeyJoin(lang, "quote") + `)` +
		`\s+` +
		`(?P<person>[\w\s-'\p{Latin}]+)`)

	res := reArgument.FindStringSubmatch(message)

	if res == nil {
		res = re.FindStringSubmatch(message)
		names = re.SubexpNames()
	} else {
		names = reArgument.SubexpNames()
	}

	m := map[string]string{}
	for i, n := range res {
		m[names[i]] = n
	}

	return m["command"], m["person"], m["extra"], m["argument"]
}

func Client(c *config.Config, db *db.Store) (err error) {
	conf = c
	store = db

	connection, err := connect()
	if err != nil {
		return errors.New("Can't connect")
	}

	connection.AddCallback("001", doWelcome)
	connection.AddCallback("JOIN", doJoin)
	connection.AddCallback("PRIVMSG", doPrivmsg)

	connection.Loop()

	return nil
}
<|endoftext|>


<commit_before>package user

import (
	"bytes"
	htmlTemplate "html/template"
	"net/http"
	"net/url"
	"time"
	"xorkevin.dev/governor"
)

const (
	kindResetEmail = "email"
	kindResetPass  = "pass"
)

type (
	emailEmailChange struct {
		Userid    string
		Key       string
		URL       string
		FirstName string
		LastName  string
		Username  string
	}

	queryEmailEmailChange struct {
		Userid    string
		Key       string
		FirstName string
		LastName  string
		Username  string
	}

	emailEmailChangeNotify struct {
		FirstName string
		LastName  string
		Username  string
	}
)

const (
	emailChangeTemplate       = "emailchange"
	emailChangeNotifyTemplate = "emailchangenotify"
)

func (e *emailEmailChange) Query() queryEmailEmailChange {
	return queryEmailEmailChange{
		Userid:    url.QueryEscape(e.Userid),
		Key:       url.QueryEscape(e.Key),
		FirstName: url.QueryEscape(e.FirstName),
		LastName:  url.QueryEscape(e.LastName),
		Username:  url.QueryEscape(e.Username),
	}
}

func (e *emailEmailChange) computeURL(base string, tpl *htmlTemplate.Template) error {
	b := &bytes.Buffer{}
	if err := tpl.Execute(b, e.Query()); err != nil {
		return governor.NewError("Failed executing email change url template", http.StatusInternalServerError, err)
	}
	e.URL = base + b.String()
	return nil
}

// UpdateEmail creates a pending user email update
func (s *service) UpdateEmail(userid string, newEmail string, password string) error {
	if _, err := s.users.GetByEmail(newEmail); err != nil {
		if governor.ErrorStatus(err) != http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
	} else {
		return governor.NewErrorUser("Email is already in use", http.StatusBadRequest, err)
	}
	m, err := s.users.GetByID(userid)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		return err
	}
	if m.Email == newEmail {
		return governor.NewErrorUser("Emails cannot be the same", http.StatusBadRequest, err)
	}
	if ok, err := s.users.ValidatePass(password, m); err != nil {
		return err
	} else if !ok {
		return governor.NewErrorUser("Incorrect password", http.StatusForbidden, nil)
	}

	needInsert := false
	mr, err := s.resets.GetByID(m.Userid, kindResetEmail)
	if err != nil {
		if governor.ErrorStatus(err) != http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		needInsert = true
		mr = s.resets.New(m.Userid, kindResetEmail)
	}
	code, err := s.resets.RehashCode(mr)
	if err != nil {
		return err
	}
	mr.Params = newEmail
	if needInsert {
		if err := s.resets.Insert(mr); err != nil {
			if governor.ErrorStatus(err) == http.StatusBadRequest {
				return governor.NewErrorUser("", 0, err)
			}
			return err
		}
	} else {
		if err := s.resets.Update(mr); err != nil {
			return err
		}
	}

	emdata := emailEmailChange{
		Userid:    userid,
		Key:       code,
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := emdata.computeURL(s.emailurlbase, s.tplemailchange); err != nil {
		return err
	}
	if err := s.mailer.Send("", "", []string{newEmail}, emailChangeTemplate, emdata); err != nil {
		return governor.NewError("Failed to send new email verification", http.StatusInternalServerError, err)
	}
	return nil
}

// CommitEmail commits an email update from the cache
func (s *service) CommitEmail(userid string, key string, password string) error {
	mr, err := s.resets.GetByID(userid, kindResetEmail)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("New email verification expired", http.StatusBadRequest, err)
		}
		return governor.NewError("Failed to update email", http.StatusInternalServerError, err)
	}

	if time.Now().Round(0).Unix() > mr.CodeTime+s.passwordResetTime {
		return governor.NewErrorUser("New email verification expired", http.StatusBadRequest, err)
	}
	if ok, err := s.resets.ValidateCode(key, mr); err != nil {
		return governor.NewError("Failed to update email", http.StatusInternalServerError, err)
	} else if !ok {
		return governor.NewErrorUser("Invalid code", http.StatusForbidden, nil)
	}

	m, err := s.users.GetByID(userid)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		return err
	}

	if ok, err := s.users.ValidatePass(password, m); err != nil {
		return err
	} else if !ok {
		return governor.NewErrorUser("Incorrect password", http.StatusForbidden, nil)
	}

	m.Email = mr.Params

	if err := s.resets.Delete(userid, kindResetPass); err != nil {
		return err
	}

	if err = s.users.Update(m); err != nil {
		if governor.ErrorStatus(err) == http.StatusBadRequest {
			return governor.NewErrorUser("Email is already in use by another account", 0, err)
		}
		return err
	}

	emdatanotify := emailEmailChangeNotify{
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := s.mailer.Send("", "", []string{m.Email}, emailChangeNotifyTemplate, emdatanotify); err != nil {
		s.logger.Error("Failed to send old email change notification", map[string]string{
			"error":      err.Error(),
			"actiontype": "commitemailoldmail",
		})
	}
	return nil
}

type (
	emailPassChange struct {
		FirstName string
		LastName  string
		Username  string
	}

	emailForgotPass struct {
		Userid    string
		Key       string
		URL       string
		FirstName string
		LastName  string
		Username  string
	}

	queryEmailForgotPass struct {
		Userid    string
		Key       string
		FirstName string
		LastName  string
		Username  string
	}

	emailPassReset struct {
		FirstName string
		LastName  string
		Username  string
	}
)

func (e *emailForgotPass) Query() queryEmailForgotPass {
	return queryEmailForgotPass{
		Userid:    url.QueryEscape(e.Userid),
		Key:       url.QueryEscape(e.Key),
		FirstName: url.QueryEscape(e.FirstName),
		LastName:  url.QueryEscape(e.LastName),
		Username:  url.QueryEscape(e.Username),
	}
}

func (e *emailForgotPass) computeURL(base string, tpl *htmlTemplate.Template) error {
	b := &bytes.Buffer{}
	if err := tpl.Execute(b, e.Query()); err != nil {
		return governor.NewError("Failed executing forgot pass url template", http.StatusInternalServerError, err)
	}
	e.URL = base + b.String()
	return nil
}

const (
	passChangeTemplate = "passchange"
	forgotPassTemplate = "forgotpass"
	passResetTemplate  = "passreset"
)

// UpdatePassword updates the password
func (s *service) UpdatePassword(userid string, newPassword string, oldPassword string) error {
	m, err := s.users.GetByID(userid)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		return err
	}
	if ok, err := s.users.ValidatePass(oldPassword, m); err != nil {
		return err
	} else if !ok {
		return governor.NewErrorUser("Incorrect password", http.StatusForbidden, err)
	}
	if err := s.users.RehashPass(m, newPassword); err != nil {
		return err
	}

	if err = s.users.Update(m); err != nil {
		return err
	}

	emdata := emailPassChange{
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := s.mailer.Send("", "", []string{m.Email}, passChangeTemplate, emdata); err != nil {
		s.logger.Error("Failed to send password change notification email", map[string]string{
			"error":      err.Error(),
			"actiontype": "updatepasswordmail",
		})
	}
	return nil
}

// ForgotPassword invokes the forgot password reset procedure
func (s *service) ForgotPassword(useroremail string) error {
	m := s.users.NewEmptyPtr()
	if isEmail(useroremail) {
		mu, err := s.users.GetByEmail(useroremail)
		if err != nil {
			if governor.ErrorStatus(err) == http.StatusNotFound {
				return nil
			}
			return err
		}
		m = mu
	} else {
		mu, err := s.users.GetByUsername(useroremail)
		if err != nil {
			if governor.ErrorStatus(err) == http.StatusNotFound {
				return nil
			}
			return err
		}
		m = mu
	}

	needInsert := false
	mr, err := s.resets.GetByID(m.Userid, kindResetPass)
	if err != nil {
		if governor.ErrorStatus(err) != http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		needInsert = true
		mr = s.resets.New(m.Userid, kindResetPass)
	}
	code, err := s.resets.RehashCode(mr)
	if err != nil {
		return err
	}
	if needInsert {
		if err := s.resets.Insert(mr); err != nil {
			if governor.ErrorStatus(err) == http.StatusBadRequest {
				return governor.NewErrorUser("", 0, err)
			}
			return err
		}
	} else {
		if err := s.resets.Update(mr); err != nil {
			return err
		}
	}

	emdata := emailForgotPass{
		Userid:    m.Userid,
		Key:       code,
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := emdata.computeURL(s.emailurlbase, s.tplforgotpass); err != nil {
		return err
	}
	if err := s.mailer.Send("", "", []string{m.Email}, forgotPassTemplate, emdata); err != nil {
		return governor.NewError("Failed to send password reset email", http.StatusInternalServerError, err)
	}
	return nil
}

// ResetPassword completes the forgot password procedure
func (s *service) ResetPassword(userid string, key string, newPassword string) error {
	mr, err := s.resets.GetByID(userid, kindResetPass)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("Password reset expired", http.StatusBadRequest, err)
		}
		return governor.NewError("Failed to reset password", http.StatusInternalServerError, err)
	}

	if time.Now().Round(0).Unix() > mr.CodeTime+s.passwordResetTime {
		return governor.NewErrorUser("Password reset expired", http.StatusBadRequest, err)
	}
	if ok, err := s.resets.ValidateCode(key, mr); err != nil {
		return governor.NewError("Failed to reset password", http.StatusInternalServerError, err)
	} else if !ok {
		return governor.NewErrorUser("Invalid code", http.StatusForbidden, nil)
	}

	m, err := s.users.GetByID(userid)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		return err
	}

	if err := s.users.RehashPass(m, newPassword); err != nil {
		return err
	}

	if err := s.resets.Delete(userid, kindResetPass); err != nil {
		return err
	}

	if err := s.users.Update(m); err != nil {
		return err
	}

	emdata := emailPassReset{
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := s.mailer.Send("", "", []string{m.Email}, passResetTemplate, emdata); err != nil {
		s.logger.Error("Failed to send password change notification email", map[string]string{
			"error":      err.Error(),
			"actiontype": "resetpasswordmail",
		})
	}
	return nil
}
<commit_msg>Bugfix kindResetEmail<commit_after>package user

import (
	"bytes"
	htmlTemplate "html/template"
	"net/http"
	"net/url"
	"time"
	"xorkevin.dev/governor"
)

const (
	kindResetEmail = "email"
	kindResetPass  = "pass"
)

type (
	emailEmailChange struct {
		Userid    string
		Key       string
		URL       string
		FirstName string
		LastName  string
		Username  string
	}

	queryEmailEmailChange struct {
		Userid    string
		Key       string
		FirstName string
		LastName  string
		Username  string
	}

	emailEmailChangeNotify struct {
		FirstName string
		LastName  string
		Username  string
	}
)

const (
	emailChangeTemplate       = "emailchange"
	emailChangeNotifyTemplate = "emailchangenotify"
)

func (e *emailEmailChange) Query() queryEmailEmailChange {
	return queryEmailEmailChange{
		Userid:    url.QueryEscape(e.Userid),
		Key:       url.QueryEscape(e.Key),
		FirstName: url.QueryEscape(e.FirstName),
		LastName:  url.QueryEscape(e.LastName),
		Username:  url.QueryEscape(e.Username),
	}
}

func (e *emailEmailChange) computeURL(base string, tpl *htmlTemplate.Template) error {
	b := &bytes.Buffer{}
	if err := tpl.Execute(b, e.Query()); err != nil {
		return governor.NewError("Failed executing email change url template", http.StatusInternalServerError, err)
	}
	e.URL = base + b.String()
	return nil
}

// UpdateEmail creates a pending user email update
func (s *service) UpdateEmail(userid string, newEmail string, password string) error {
	if _, err := s.users.GetByEmail(newEmail); err != nil {
		if governor.ErrorStatus(err) != http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
	} else {
		return governor.NewErrorUser("Email is already in use", http.StatusBadRequest, err)
	}
	m, err := s.users.GetByID(userid)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		return err
	}
	if m.Email == newEmail {
		return governor.NewErrorUser("Emails cannot be the same", http.StatusBadRequest, err)
	}
	if ok, err := s.users.ValidatePass(password, m); err != nil {
		return err
	} else if !ok {
		return governor.NewErrorUser("Incorrect password", http.StatusForbidden, nil)
	}

	needInsert := false
	mr, err := s.resets.GetByID(m.Userid, kindResetEmail)
	if err != nil {
		if governor.ErrorStatus(err) != http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		needInsert = true
		mr = s.resets.New(m.Userid, kindResetEmail)
	}
	code, err := s.resets.RehashCode(mr)
	if err != nil {
		return err
	}
	mr.Params = newEmail
	if needInsert {
		if err := s.resets.Insert(mr); err != nil {
			if governor.ErrorStatus(err) == http.StatusBadRequest {
				return governor.NewErrorUser("", 0, err)
			}
			return err
		}
	} else {
		if err := s.resets.Update(mr); err != nil {
			return err
		}
	}

	emdata := emailEmailChange{
		Userid:    userid,
		Key:       code,
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := emdata.computeURL(s.emailurlbase, s.tplemailchange); err != nil {
		return err
	}
	if err := s.mailer.Send("", "", []string{newEmail}, emailChangeTemplate, emdata); err != nil {
		return governor.NewError("Failed to send new email verification", http.StatusInternalServerError, err)
	}
	return nil
}

// CommitEmail commits an email update from the cache
func (s *service) CommitEmail(userid string, key string, password string) error {
	mr, err := s.resets.GetByID(userid, kindResetEmail)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("New email verification expired", http.StatusBadRequest, err)
		}
		return governor.NewError("Failed to update email", http.StatusInternalServerError, err)
	}

	if time.Now().Round(0).Unix() > mr.CodeTime+s.passwordResetTime {
		return governor.NewErrorUser("New email verification expired", http.StatusBadRequest, err)
	}
	if ok, err := s.resets.ValidateCode(key, mr); err != nil {
		return governor.NewError("Failed to update email", http.StatusInternalServerError, err)
	} else if !ok {
		return governor.NewErrorUser("Invalid code", http.StatusForbidden, nil)
	}

	m, err := s.users.GetByID(userid)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		return err
	}

	if ok, err := s.users.ValidatePass(password, m); err != nil {
		return err
	} else if !ok {
		return governor.NewErrorUser("Incorrect password", http.StatusForbidden, nil)
	}

	m.Email = mr.Params

	if err := s.resets.Delete(userid, kindResetEmail); err != nil {
		return err
	}

	if err = s.users.Update(m); err != nil {
		if governor.ErrorStatus(err) == http.StatusBadRequest {
			return governor.NewErrorUser("Email is already in use by another account", 0, err)
		}
		return err
	}

	emdatanotify := emailEmailChangeNotify{
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := s.mailer.Send("", "", []string{m.Email}, emailChangeNotifyTemplate, emdatanotify); err != nil {
		s.logger.Error("Failed to send old email change notification", map[string]string{
			"error":      err.Error(),
			"actiontype": "commitemailoldmail",
		})
	}
	return nil
}

type (
	emailPassChange struct {
		FirstName string
		LastName  string
		Username  string
	}

	emailForgotPass struct {
		Userid    string
		Key       string
		URL       string
		FirstName string
		LastName  string
		Username  string
	}

	queryEmailForgotPass struct {
		Userid    string
		Key       string
		FirstName string
		LastName  string
		Username  string
	}

	emailPassReset struct {
		FirstName string
		LastName  string
		Username  string
	}
)

func (e *emailForgotPass) Query() queryEmailForgotPass {
	return queryEmailForgotPass{
		Userid:    url.QueryEscape(e.Userid),
		Key:       url.QueryEscape(e.Key),
		FirstName: url.QueryEscape(e.FirstName),
		LastName:  url.QueryEscape(e.LastName),
		Username:  url.QueryEscape(e.Username),
	}
}

func (e *emailForgotPass) computeURL(base string, tpl *htmlTemplate.Template) error {
	b := &bytes.Buffer{}
	if err := tpl.Execute(b, e.Query()); err != nil {
		return governor.NewError("Failed executing forgot pass url template", http.StatusInternalServerError, err)
	}
	e.URL = base + b.String()
	return nil
}

const (
	passChangeTemplate = "passchange"
	forgotPassTemplate = "forgotpass"
	passResetTemplate  = "passreset"
)

// UpdatePassword updates the password
func (s *service) UpdatePassword(userid string, newPassword string, oldPassword string) error {
	m, err := s.users.GetByID(userid)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		return err
	}
	if ok, err := s.users.ValidatePass(oldPassword, m); err != nil {
		return err
	} else if !ok {
		return governor.NewErrorUser("Incorrect password", http.StatusForbidden, err)
	}
	if err := s.users.RehashPass(m, newPassword); err != nil {
		return err
	}

	if err = s.users.Update(m); err != nil {
		return err
	}

	emdata := emailPassChange{
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := s.mailer.Send("", "", []string{m.Email}, passChangeTemplate, emdata); err != nil {
		s.logger.Error("Failed to send password change notification email", map[string]string{
			"error":      err.Error(),
			"actiontype": "updatepasswordmail",
		})
	}
	return nil
}

// ForgotPassword invokes the forgot password reset procedure
func (s *service) ForgotPassword(useroremail string) error {
	m := s.users.NewEmptyPtr()
	if isEmail(useroremail) {
		mu, err := s.users.GetByEmail(useroremail)
		if err != nil {
			if governor.ErrorStatus(err) == http.StatusNotFound {
				return nil
			}
			return err
		}
		m = mu
	} else {
		mu, err := s.users.GetByUsername(useroremail)
		if err != nil {
			if governor.ErrorStatus(err) == http.StatusNotFound {
				return nil
			}
			return err
		}
		m = mu
	}

	needInsert := false
	mr, err := s.resets.GetByID(m.Userid, kindResetPass)
	if err != nil {
		if governor.ErrorStatus(err) != http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		needInsert = true
		mr = s.resets.New(m.Userid, kindResetPass)
	}
	code, err := s.resets.RehashCode(mr)
	if err != nil {
		return err
	}
	if needInsert {
		if err := s.resets.Insert(mr); err != nil {
			if governor.ErrorStatus(err) == http.StatusBadRequest {
				return governor.NewErrorUser("", 0, err)
			}
			return err
		}
	} else {
		if err := s.resets.Update(mr); err != nil {
			return err
		}
	}

	emdata := emailForgotPass{
		Userid:    m.Userid,
		Key:       code,
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := emdata.computeURL(s.emailurlbase, s.tplforgotpass); err != nil {
		return err
	}
	if err := s.mailer.Send("", "", []string{m.Email}, forgotPassTemplate, emdata); err != nil {
		return governor.NewError("Failed to send password reset email", http.StatusInternalServerError, err)
	}
	return nil
}

// ResetPassword completes the forgot password procedure
func (s *service) ResetPassword(userid string, key string, newPassword string) error {
	mr, err := s.resets.GetByID(userid, kindResetPass)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("Password reset expired", http.StatusBadRequest, err)
		}
		return governor.NewError("Failed to reset password", http.StatusInternalServerError, err)
	}

	if time.Now().Round(0).Unix() > mr.CodeTime+s.passwordResetTime {
		return governor.NewErrorUser("Password reset expired", http.StatusBadRequest, err)
	}
	if ok, err := s.resets.ValidateCode(key, mr); err != nil {
		return governor.NewError("Failed to reset password", http.StatusInternalServerError, err)
	} else if !ok {
		return governor.NewErrorUser("Invalid code", http.StatusForbidden, nil)
	}

	m, err := s.users.GetByID(userid)
	if err != nil {
		if governor.ErrorStatus(err) == http.StatusNotFound {
			return governor.NewErrorUser("", 0, err)
		}
		return err
	}

	if err := s.users.RehashPass(m, newPassword); err != nil {
		return err
	}

	if err := s.resets.Delete(userid, kindResetPass); err != nil {
		return err
	}

	if err := s.users.Update(m); err != nil {
		return err
	}

	emdata := emailPassReset{
		FirstName: m.FirstName,
		LastName:  m.LastName,
		Username:  m.Username,
	}
	if err := s.mailer.Send("", "", []string{m.Email}, passResetTemplate, emdata); err != nil {
		s.logger.Error("Failed to send password change notification email", map[string]string{
			"error":      err.Error(),
			"actiontype": "resetpasswordmail",
		})
	}
	return nil
}
<|endoftext|>


<commit_before>package mounttable

import (
	"net"

	"v.io/core/veyron2"
	"v.io/core/veyron2/context"
	"v.io/core/veyron2/ipc"
	"v.io/core/veyron2/naming"
	"v.io/core/veyron2/options"
	"v.io/core/veyron2/vlog"
)

func StartServers(ctx *context.T, listenSpec ipc.ListenSpec, mountName, nhName, aclFile string) (string, func(), error) {
	var stopFuncs []func() error
	stop := func() {
		for i := len(stopFuncs) - 1; i >= 0; i-- {
			stopFuncs[i]()
		}
	}

	mtServer, err := veyron2.NewServer(ctx, options.ServesMountTable(true))
	if err != nil {
		vlog.Errorf("veyron2.NewServer failed: %v", err)
		return "", nil, err
	}
	stopFuncs = append(stopFuncs, mtServer.Stop)
	mt, err := NewMountTableDispatcher(aclFile)
	if err != nil {
		vlog.Errorf("NewMountTable failed: %v", err)
		stop()
		return "", nil, err
	}
	mtEndpoints, err := mtServer.Listen(listenSpec)
	if err != nil {
		vlog.Errorf("mtServer.Listen failed: %v", err)
		stop()
		return "", nil, err
	}
	mtEndpoint := mtEndpoints[0]
	if err := mtServer.ServeDispatcher(mountName, mt); err != nil {
		vlog.Errorf("ServeDispatcher() failed: %v", err)
		stop()
		return "", nil, err
	}

	mtName := mtEndpoint.Name()
	vlog.Infof("Mount table service at: %q endpoint: %s", mountName, mtName)

	if len(nhName) > 0 {
		neighborhoodListenSpec := listenSpec
		// The ListenSpec code ensures that we have a valid address here.
		host, port, _ := net.SplitHostPort(listenSpec.Addrs[0].Address)
		if port != "" {
			neighborhoodListenSpec.Addrs[0].Address = net.JoinHostPort(host, "0")
		}
		nhServer, err := veyron2.NewServer(ctx, options.ServesMountTable(true))
		if err != nil {
			vlog.Errorf("veyron2.NewServer failed: %v", err)
			stop()
			return "", nil, err
		}
		stopFuncs = append(stopFuncs, nhServer.Stop)
		if _, err := nhServer.Listen(neighborhoodListenSpec); err != nil {
			vlog.Errorf("nhServer.Listen failed: %v", err)
			stop()
			return "", nil, err
		}

		nh, err := NewLoopbackNeighborhoodDispatcher(nhName, mtName)
		if err != nil {
			vlog.Errorf("NewNeighborhoodServer failed: %v", err)
			stop()
			return "", nil, err
		}
		if err := nhServer.ServeDispatcher(naming.JoinAddressName(mtName, "nh"), nh); err != nil {
			vlog.Errorf("nhServer.ServeDispatcher failed to register neighborhood: %v", err)
			stop()
			return "", nil, err
		}
	}
	return mtName, stop, nil
}
<commit_msg>veyron/services/mounttable/lib: Use correct NewNeighborhoodDispatcher<commit_after>package mounttable

import (
	"net"

	"v.io/core/veyron2"
	"v.io/core/veyron2/context"
	"v.io/core/veyron2/ipc"
	"v.io/core/veyron2/naming"
	"v.io/core/veyron2/options"
	"v.io/core/veyron2/vlog"
)

func StartServers(ctx *context.T, listenSpec ipc.ListenSpec, mountName, nhName, aclFile string) (string, func(), error) {
	var stopFuncs []func() error
	stop := func() {
		for i := len(stopFuncs) - 1; i >= 0; i-- {
			stopFuncs[i]()
		}
	}

	mtServer, err := veyron2.NewServer(ctx, options.ServesMountTable(true))
	if err != nil {
		vlog.Errorf("veyron2.NewServer failed: %v", err)
		return "", nil, err
	}
	stopFuncs = append(stopFuncs, mtServer.Stop)
	mt, err := NewMountTableDispatcher(aclFile)
	if err != nil {
		vlog.Errorf("NewMountTable failed: %v", err)
		stop()
		return "", nil, err
	}
	mtEndpoints, err := mtServer.Listen(listenSpec)
	if err != nil {
		vlog.Errorf("mtServer.Listen failed: %v", err)
		stop()
		return "", nil, err
	}
	mtEndpoint := mtEndpoints[0]
	if err := mtServer.ServeDispatcher(mountName, mt); err != nil {
		vlog.Errorf("ServeDispatcher() failed: %v", err)
		stop()
		return "", nil, err
	}

	mtName := mtEndpoint.Name()
	vlog.Infof("Mount table service at: %q endpoint: %s", mountName, mtName)

	if len(nhName) > 0 {
		neighborhoodListenSpec := listenSpec
		// The ListenSpec code ensures that we have a valid address here.
		host, port, _ := net.SplitHostPort(listenSpec.Addrs[0].Address)
		if port != "" {
			neighborhoodListenSpec.Addrs[0].Address = net.JoinHostPort(host, "0")
		}
		nhServer, err := veyron2.NewServer(ctx, options.ServesMountTable(true))
		if err != nil {
			vlog.Errorf("veyron2.NewServer failed: %v", err)
			stop()
			return "", nil, err
		}
		stopFuncs = append(stopFuncs, nhServer.Stop)
		if _, err := nhServer.Listen(neighborhoodListenSpec); err != nil {
			vlog.Errorf("nhServer.Listen failed: %v", err)
			stop()
			return "", nil, err
		}

		addresses := []string{}
		for _, ep := range mtEndpoints {
			addresses = append(addresses, ep.Name())
		}
		nh, err := NewNeighborhoodDispatcher(nhName, addresses...)
		if err != nil {
			vlog.Errorf("NewNeighborhoodServer failed: %v", err)
			stop()
			return "", nil, err
		}
		if err := nhServer.ServeDispatcher(naming.JoinAddressName(mtName, "nh"), nh); err != nil {
			vlog.Errorf("nhServer.ServeDispatcher failed to register neighborhood: %v", err)
			stop()
			return "", nil, err
		}
	}
	return mtName, stop, nil
}
<|endoftext|>


<commit_before>//
// Copyright 2018 Google Inc.
//
// 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 internal

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
)

// tokenJSON is the struct representing the HTTP response from OAuth2
// providers returning a token in JSON form.
type tokenJSON struct {
	AccessToken  string         `json:"access_token"`
	TokenType    string         `json:"token_type"`
	RefreshToken string         `json:"refresh_token"`
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
}

func (e *tokenJSON) expiry() (t time.Time) {
	if v := e.ExpiresIn; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	if v := e.Expires; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	return
}

type expirationTime int32

func (e *expirationTime) UnmarshalJSON(b []byte) error {
	var n json.Number
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*e = expirationTime(i)
	return nil
}

var brokenAuthHeaderProviders = []string{
	"https://accounts.google.com/",
	"https://api.codeswholesale.com/oauth/token",
	"https://api.dropbox.com/",
	"https://api.dropboxapi.com/",
	"https://api.instagram.com/",
	"https://api.netatmo.net/",
	"https://api.odnoklassniki.ru/",
	"https://api.pushbullet.com/",
	"https://api.soundcloud.com/",
	"https://api.twitch.tv/",
	"https://app.box.com/",
	"https://connect.stripe.com/",
	"https://graph.facebook.com", // see https://github.com/golang/oauth2/issues/214
	"https://login.microsoftonline.com/",
	"https://login.salesforce.com/",
	"https://login.windows.net",
	"https://login.live.com/",
	"https://oauth.sandbox.trainingpeaks.com/",
	"https://oauth.trainingpeaks.com/",
	"https://oauth.vk.com/",
	"https://openapi.baidu.com/",
	"https://slack.com/",
	"https://test-sandbox.auth.corp.google.com",
	"https://test.salesforce.com/",
	"https://user.gini.net/",
	"https://www.douban.com/",
	"https://www.googleapis.com/",
	"https://www.linkedin.com/",
	"https://www.strava.com/oauth/",
	"https://www.wunderlist.com/oauth/",
	"https://api.patreon.com/",
	"https://sandbox.codeswholesale.com/oauth/token",
	"https://api.sipgate.com/v1/authorization/oauth",
}

// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
var brokenAuthHeaderDomains = []string{
	".force.com",
	".myshopify.com",
	".okta.com",
	".oktapreview.com",
}

func RegisterBrokenAuthHeaderProvider(tokenURL string) {
	brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
	for _, s := range brokenAuthHeaderProviders {
		if strings.HasPrefix(tokenURL, s) {
			// Some sites fail to implement the OAuth2 spec fully.
			return false
		}
	}

	if u, err := url.Parse(tokenURL); err == nil {
		for _, s := range brokenAuthHeaderDomains {
			if strings.HasSuffix(u.Host, s) {
				return false
			}
		}
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}

func retrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
	bustedAuth := !providerAuthHeaderWorks(tokenURL)
	if bustedAuth {
		if clientID != "" {
			v.Set("client_id", clientID)
		}
		if clientSecret != "" {
			v.Set("client_secret", clientSecret)
		}
	}
	req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth {
		req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
	}
	r, err := ctxhttp.Do(ctx, http.DefaultClient, req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, &RetrieveError{
			Response: r,
			Body:     body,
		}
	}

	var token *Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
			Raw:          vals,
		}
		e := vals.Get("expires_in")
		if e == "" {
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
			// returns expires_in field in expires. Remove the fallback to expires,
			// when Facebook fixes their implementation.
			e = vals.Get("expires")
		}
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:
		var tj tokenJSON
		if err = json.Unmarshal(body, &tj); err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  tj.AccessToken,
			TokenType:    tj.TokenType,
			RefreshToken: tj.RefreshToken,
			Expiry:       tj.expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if token.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	if token.AccessToken == "" {
		return token, errors.New("oauth2: server response missing access_token")
	}
	return token, nil
}

type RetrieveError struct {
	Response *http.Response
	Body     []byte
}

func (r *RetrieveError) Error() string {
	return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
}
<commit_msg>Add test-www.sandbox.googleapis.com into whitelist (#57)<commit_after>//
// Copyright 2018 Google Inc.
//
// 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 internal

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
)

// tokenJSON is the struct representing the HTTP response from OAuth2
// providers returning a token in JSON form.
type tokenJSON struct {
	AccessToken  string         `json:"access_token"`
	TokenType    string         `json:"token_type"`
	RefreshToken string         `json:"refresh_token"`
	ExpiresIn    expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
	Expires      expirationTime `json:"expires"`    // broken Facebook spelling of expires_in
}

func (e *tokenJSON) expiry() (t time.Time) {
	if v := e.ExpiresIn; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	if v := e.Expires; v != 0 {
		return time.Now().Add(time.Duration(v) * time.Second)
	}
	return
}

type expirationTime int32

func (e *expirationTime) UnmarshalJSON(b []byte) error {
	var n json.Number
	err := json.Unmarshal(b, &n)
	if err != nil {
		return err
	}
	i, err := n.Int64()
	if err != nil {
		return err
	}
	*e = expirationTime(i)
	return nil
}

var brokenAuthHeaderProviders = []string{
	"https://accounts.google.com/",
	"https://api.codeswholesale.com/oauth/token",
	"https://api.dropbox.com/",
	"https://api.dropboxapi.com/",
	"https://api.instagram.com/",
	"https://api.netatmo.net/",
	"https://api.odnoklassniki.ru/",
	"https://api.pushbullet.com/",
	"https://api.soundcloud.com/",
	"https://api.twitch.tv/",
	"https://app.box.com/",
	"https://connect.stripe.com/",
	"https://graph.facebook.com", // see https://github.com/golang/oauth2/issues/214
	"https://login.microsoftonline.com/",
	"https://login.salesforce.com/",
	"https://login.windows.net",
	"https://login.live.com/",
	"https://oauth.sandbox.trainingpeaks.com/",
	"https://oauth.trainingpeaks.com/",
	"https://oauth.vk.com/",
	"https://openapi.baidu.com/",
	"https://slack.com/",
	"https://test-sandbox.auth.corp.google.com",
	"https://test-www.sandbox.googleapis.com",
	"https://test.salesforce.com/",
	"https://user.gini.net/",
	"https://www.douban.com/",
	"https://www.googleapis.com/",
	"https://www.linkedin.com/",
	"https://www.strava.com/oauth/",
	"https://www.wunderlist.com/oauth/",
	"https://api.patreon.com/",
	"https://sandbox.codeswholesale.com/oauth/token",
	"https://api.sipgate.com/v1/authorization/oauth",
}

// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
var brokenAuthHeaderDomains = []string{
	".force.com",
	".myshopify.com",
	".okta.com",
	".oktapreview.com",
}

func RegisterBrokenAuthHeaderProvider(tokenURL string) {
	brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
func providerAuthHeaderWorks(tokenURL string) bool {
	for _, s := range brokenAuthHeaderProviders {
		if strings.HasPrefix(tokenURL, s) {
			// Some sites fail to implement the OAuth2 spec fully.
			return false
		}
	}

	if u, err := url.Parse(tokenURL); err == nil {
		for _, s := range brokenAuthHeaderDomains {
			if strings.HasSuffix(u.Host, s) {
				return false
			}
		}
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}

func retrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
	bustedAuth := !providerAuthHeaderWorks(tokenURL)
	if bustedAuth {
		if clientID != "" {
			v.Set("client_id", clientID)
		}
		if clientSecret != "" {
			v.Set("client_secret", clientSecret)
		}
	}
	req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth {
		req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
	}
	r, err := ctxhttp.Do(ctx, http.DefaultClient, req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if code := r.StatusCode; code < 200 || code > 299 {
		return nil, &RetrieveError{
			Response: r,
			Body:     body,
		}
	}

	var token *Token
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  vals.Get("access_token"),
			TokenType:    vals.Get("token_type"),
			RefreshToken: vals.Get("refresh_token"),
			Raw:          vals,
		}
		e := vals.Get("expires_in")
		if e == "" {
			// TODO(jbd): Facebook's OAuth2 implementation is broken and
			// returns expires_in field in expires. Remove the fallback to expires,
			// when Facebook fixes their implementation.
			e = vals.Get("expires")
		}
		expires, _ := strconv.Atoi(e)
		if expires != 0 {
			token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
		}
	default:
		var tj tokenJSON
		if err = json.Unmarshal(body, &tj); err != nil {
			return nil, err
		}
		token = &Token{
			AccessToken:  tj.AccessToken,
			TokenType:    tj.TokenType,
			RefreshToken: tj.RefreshToken,
			Expiry:       tj.expiry(),
			Raw:          make(map[string]interface{}),
		}
		json.Unmarshal(body, &token.Raw) // no error checks for optional fields
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if token.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	if token.AccessToken == "" {
		return token, errors.New("oauth2: server response missing access_token")
	}
	return token, nil
}

type RetrieveError struct {
	Response *http.Response
	Body     []byte
}

func (r *RetrieveError) Error() string {
	return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Google Inc.
//
// 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 gapidapk

import (
	"context"
	"fmt"
	"io/ioutil"
	"path"
	"sync"
	"time"

	"github.com/google/gapid/core/app/layout"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/android"
	"github.com/google/gapid/core/os/android/adb"
	"github.com/google/gapid/core/os/android/apk"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/core/os/device/bind"
)

const (
	installAttempts = 5
	checkFrequency  = time.Second * 5
)

var ensureInstalledMutex sync.Mutex

// APK represents the installed GAPIR APK.
type APK struct {
	*android.InstalledPackage
	path string
}

type lastInstallCheckKey struct{ *device.ABI }
type lastInstallCheckRes struct {
	time time.Time
	apk  *APK
}

func ensureInstalled(ctx context.Context, d adb.Device, abi *device.ABI) (*APK, error) {
	ctx = log.V{"abi": abi.Name}.Bind(ctx)

	// Was this recently checked?
	reg, checkKey := bind.GetRegistry(ctx), lastInstallCheckKey{abi}
	if res, ok := reg.DeviceProperty(ctx, d, checkKey).(lastInstallCheckRes); ok {
		if time.Since(res.time) < checkFrequency {
			return res.apk, nil
		}
	}

	// Check the device actually supports the requested ABI.
	if !d.Instance().Configuration.SupportsABI(abi) {
		return nil, log.Errf(ctx, nil, "Device does not support requested abi: %v", abi.Name)
	}

	name := pkgName(abi)

	log.I(ctx, "Examining gapid.apk on host...")
	apkPath, err := layout.GapidApk(ctx, abi)
	if err != nil {
		return nil, log.Err(ctx, err, "Finding gapid.apk on host")
	}

	ctx = log.V{"gapid.apk": apkPath.System()}.Bind(ctx)
	apkData, err := ioutil.ReadFile(apkPath.System())
	if err != nil {
		return nil, log.Err(ctx, err, "Opening gapid.apk")
	}

	apkFiles, err := apk.Read(ctx, apkData)
	if err != nil {
		return nil, log.Err(ctx, err, "Reading gapid.apk")
	}

	apkManifest, err := apk.GetManifest(ctx, apkFiles)
	if err != nil {
		return nil, log.Err(ctx, err, "Reading gapid.apk manifest")
	}

	ctx = log.V{
		"target-version-name": apkManifest.VersionName,
		"target-version-code": apkManifest.VersionCode,
	}.Bind(ctx)

	for attempts := installAttempts; attempts > 0; attempts-- {
		log.I(ctx, "Looking for gapid.apk...")
		gapid, err := d.InstalledPackage(ctx, name)
		if err != nil {
			log.I(ctx, "Installing gapid.apk...")
			if err := d.InstallAPK(ctx, apkPath.System(), false, true); err != nil {
				return nil, log.Err(ctx, err, "Installing gapid.apk")
			}
			continue
		}

		ctx = log.V{
			"installed-version-name": gapid.VersionName,
			"installed-version-code": gapid.VersionCode,
		}.Bind(ctx)

		if gapid.VersionCode != apkManifest.VersionCode ||
			gapid.VersionName != apkManifest.VersionName {
			log.I(ctx, "Uninstalling existing gapid.apk as version has changed.")
			gapid.Uninstall(ctx)
			continue
		}

		apkPath, err := gapid.Path(ctx)
		if err != nil {
			return nil, log.Err(ctx, err, "Obtaining GAPID package path")
		}
		log.I(ctx, "Found gapid package...")

		out := &APK{gapid, path.Dir(apkPath)}
		reg.SetDeviceProperty(ctx, d, checkKey, lastInstallCheckRes{time.Now(), out})
		return out, nil
	}

	return nil, log.Err(ctx, nil, "Unable to install GAPID")
}

// EnsureInstalled ensures that gapid.apk with the specified ABI is installed on
// d with the same version as the APK on the host, and returns the installed APK.
// If abi is nil or UnknownABI, all the ABI available on the host will be tried
// for d, and the preferred ABI of the device will be tried first. Once an ABI
// is found compatible with the device, the APK of that ABI will be ensured to
// be installed.
func EnsureInstalled(ctx context.Context, d adb.Device, abi *device.ABI) (*APK, error) {
	ensureInstalledMutex.Lock()
	defer ensureInstalledMutex.Unlock()

	ctx = log.Enter(ctx, "gapidapk.EnsureInstalled")
	if abi.SameAs(device.UnknownABI) {
		abisToTry := []*device.ABI{d.Instance().GetConfiguration().PreferredABI(nil)}
		abisToTry = append(abisToTry, d.Instance().GetConfiguration().GetABIs()...)
		for _, a := range abisToTry {
			tempCtx := log.Enter(ctx, fmt.Sprintf("Try ABI: %s", a.Name))
			apk, err := ensureInstalled(tempCtx, d, a)
			if err == nil {
				return apk, nil
			}
			log.I(tempCtx, err.Error())
		}
	} else {
		return ensureInstalled(ctx, d, abi)
	}
	return nil, log.Err(ctx, nil, "Unable to install GAPID")
}

// LibsPath returns the path on the Android device to the GAPID libs directory.
// gapid.apk must be installed for this path to be valid.
func (a APK) LibsPath(abi *device.ABI) string {
	switch {
	case abi.SameAs(device.AndroidARMv7a):
		return a.path + "/lib/arm"
	case abi.SameAs(device.AndroidARM64v8a):
		return a.path + "/lib/arm64"
	}
	return a.path + "/lib/" + abi.Name
}

// LibGAPIIPath returns the path on the Android device to libgapii.so.
// gapid.apk must be installed for this path to be valid.
func (a APK) LibGAPIIPath(abi *device.ABI) string {
	return a.LibsPath(abi) + "/libgapii.so"
}

// LibInterceptorPath returns the path on the Android device to
// libinterceptor.so.
// gapid.apk must be installed for this path to be valid.
func (a APK) LibInterceptorPath(abi *device.ABI) string {
	return a.LibsPath(abi) + "/libinterceptor.so"
}

func pkgName(abi *device.ABI) string {
	switch {
	case abi.SameAs(device.AndroidARMv7a):
		return "com.google.android.gapid.armeabiv7a"
	case abi.SameAs(device.AndroidARM64v8a):
		return "com.google.android.gapid.arm64v8a"
	default:
		return fmt.Sprintf("com.google.android.gapid.%v", abi.Name)
	}
}
<commit_msg>gapidapk: Make more package related stuff public<commit_after>// Copyright (C) 2017 Google Inc.
//
// 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 gapidapk

import (
	"context"
	"fmt"
	"io/ioutil"
	"path"
	"sync"
	"time"

	"github.com/google/gapid/core/app/layout"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/android"
	"github.com/google/gapid/core/os/android/adb"
	"github.com/google/gapid/core/os/android/apk"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/core/os/device/bind"
)

const (
	installAttempts = 5
	checkFrequency  = time.Second * 5
)

var ensureInstalledMutex sync.Mutex

// APK represents the installed GAPIR APK.
type APK struct {
	*android.InstalledPackage
	path string
}

type lastInstallCheckKey struct{ *device.ABI }
type lastInstallCheckRes struct {
	time time.Time
	apk  *APK
}

func ensureInstalled(ctx context.Context, d adb.Device, abi *device.ABI) (*APK, error) {
	ctx = log.V{"abi": abi.Name}.Bind(ctx)

	// Was this recently checked?
	reg, checkKey := bind.GetRegistry(ctx), lastInstallCheckKey{abi}
	if res, ok := reg.DeviceProperty(ctx, d, checkKey).(lastInstallCheckRes); ok {
		if time.Since(res.time) < checkFrequency {
			return res.apk, nil
		}
	}

	// Check the device actually supports the requested ABI.
	if !d.Instance().Configuration.SupportsABI(abi) {
		return nil, log.Errf(ctx, nil, "Device does not support requested abi: %v", abi.Name)
	}

	name := PackageName(abi)

	log.I(ctx, "Examining gapid.apk on host...")
	apkPath, err := layout.GapidApk(ctx, abi)
	if err != nil {
		return nil, log.Err(ctx, err, "Finding gapid.apk on host")
	}

	ctx = log.V{"gapid.apk": apkPath.System()}.Bind(ctx)
	apkData, err := ioutil.ReadFile(apkPath.System())
	if err != nil {
		return nil, log.Err(ctx, err, "Opening gapid.apk")
	}

	apkFiles, err := apk.Read(ctx, apkData)
	if err != nil {
		return nil, log.Err(ctx, err, "Reading gapid.apk")
	}

	apkManifest, err := apk.GetManifest(ctx, apkFiles)
	if err != nil {
		return nil, log.Err(ctx, err, "Reading gapid.apk manifest")
	}

	ctx = log.V{
		"target-version-name": apkManifest.VersionName,
		"target-version-code": apkManifest.VersionCode,
	}.Bind(ctx)

	for attempts := installAttempts; attempts > 0; attempts-- {
		log.I(ctx, "Looking for gapid.apk...")
		gapid, err := d.InstalledPackage(ctx, name)
		if err != nil {
			log.I(ctx, "Installing gapid.apk...")
			if err := d.InstallAPK(ctx, apkPath.System(), false, true); err != nil {
				return nil, log.Err(ctx, err, "Installing gapid.apk")
			}
			continue
		}

		ctx = log.V{
			"installed-version-name": gapid.VersionName,
			"installed-version-code": gapid.VersionCode,
		}.Bind(ctx)

		if gapid.VersionCode != apkManifest.VersionCode ||
			gapid.VersionName != apkManifest.VersionName {
			log.I(ctx, "Uninstalling existing gapid.apk as version has changed.")
			gapid.Uninstall(ctx)
			continue
		}

		apkPath, err := gapid.Path(ctx)
		if err != nil {
			return nil, log.Err(ctx, err, "Obtaining GAPID package path")
		}
		log.I(ctx, "Found gapid package...")

		out := &APK{gapid, path.Dir(apkPath)}
		reg.SetDeviceProperty(ctx, d, checkKey, lastInstallCheckRes{time.Now(), out})
		return out, nil
	}

	return nil, log.Err(ctx, nil, "Unable to install GAPID")
}

// EnsureInstalled ensures that gapid.apk with the specified ABI is installed on
// d with the same version as the APK on the host, and returns the installed APK.
// If abi is nil or UnknownABI, all the ABI available on the host will be tried
// for d, and the preferred ABI of the device will be tried first. Once an ABI
// is found compatible with the device, the APK of that ABI will be ensured to
// be installed.
func EnsureInstalled(ctx context.Context, d adb.Device, abi *device.ABI) (*APK, error) {
	ensureInstalledMutex.Lock()
	defer ensureInstalledMutex.Unlock()

	ctx = log.Enter(ctx, "gapidapk.EnsureInstalled")
	if abi.SameAs(device.UnknownABI) {
		abisToTry := []*device.ABI{d.Instance().GetConfiguration().PreferredABI(nil)}
		abisToTry = append(abisToTry, d.Instance().GetConfiguration().GetABIs()...)
		for _, a := range abisToTry {
			tempCtx := log.Enter(ctx, fmt.Sprintf("Try ABI: %s", a.Name))
			apk, err := ensureInstalled(tempCtx, d, a)
			if err == nil {
				return apk, nil
			}
			log.I(tempCtx, err.Error())
		}
	} else {
		return ensureInstalled(ctx, d, abi)
	}
	return nil, log.Err(ctx, nil, "Unable to install GAPID")
}

// LibsPath returns the path on the Android device to the GAPID libs directory.
// gapid.apk must be installed for this path to be valid.
func (a APK) LibsPath(abi *device.ABI) string {
	switch {
	case abi.SameAs(device.AndroidARMv7a):
		return a.path + "/lib/arm"
	case abi.SameAs(device.AndroidARM64v8a):
		return a.path + "/lib/arm64"
	}
	return a.path + "/lib/" + abi.Name
}

// LibGAPIIPath returns the path on the Android device to the GAPII dynamic
// library file.
// gapid.apk must be installed for this path to be valid.
func (a APK) LibGAPIIPath(abi *device.ABI) string {
	return a.LibsPath(abi) + "/" + LibGAPIIName
}

// LibInterceptorPath returns the path on the Android device to the
// interceptor dynamic library file.
// gapid.apk must be installed for this path to be valid.
func (a APK) LibInterceptorPath(abi *device.ABI) string {
	return a.LibsPath(abi) + "/" + LibInterceptorName
}

const (
	// LibGAPIIName is the name of the GAPII dynamic library file.
	LibGAPIIName = "libgapii.so"

	// LibInterceptorName is the name of the interceptor dynamic library file.
	LibInterceptorName = "libinterceptor.so"
)

// PackageName returns the full package name of the GAPID apk for the given ABI.
func PackageName(abi *device.ABI) string {
	switch {
	case abi.SameAs(device.AndroidARMv7a):
		return "com.google.android.gapid.armeabiv7a"
	case abi.SameAs(device.AndroidARM64v8a):
		return "com.google.android.gapid.arm64v8a"
	default:
		return fmt.Sprintf("com.google.android.gapid.%v", abi.Name)
	}
}
<|endoftext|>


<commit_before>// 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 gcs

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"unicode/utf8"

	"github.com/jacobsa/gcloud/httputil"
	"golang.org/x/net/context"
	"google.golang.org/api/googleapi"
	storagev1 "google.golang.org/api/storage/v1"
	"google.golang.org/cloud/storage"
)

func toRawAcls(in []storage.ACLRule) []*storagev1.ObjectAccessControl {
	out := make([]*storagev1.ObjectAccessControl, len(in))
	for i, rule := range in {
		out[i] = &storagev1.ObjectAccessControl{
			Entity: string(rule.Entity),
			Role:   string(rule.Role),
		}
	}

	return out
}

func toRawObject(
	bucketName string,
	in *storage.ObjectAttrs) (out *storagev1.Object, err error) {
	out = &storagev1.Object{
		Bucket:          bucketName,
		Name:            in.Name,
		ContentType:     in.ContentType,
		ContentLanguage: in.ContentLanguage,
		ContentEncoding: in.ContentEncoding,
		CacheControl:    in.CacheControl,
		Acl:             toRawAcls(in.ACL),
		Metadata:        in.Metadata,
	}

	return
}

// Create the JSON for an "object resource", for use in an Objects.insert body.
func serializeMetadata(
	bucketName string,
	attrs *storage.ObjectAttrs) (out []byte, err error) {
	// Convert to storagev1.Object.
	rawObject, err := toRawObject(bucketName, attrs)
	if err != nil {
		err = fmt.Errorf("toRawObject: %v", err)
		return
	}

	// Serialize.
	out, err = json.Marshal(rawObject)
	if err != nil {
		err = fmt.Errorf("json.Marshal: %v", err)
		return
	}

	return
}

func startResumableUpload(
	httpClient *http.Client,
	bucketName string,
	ctx context.Context,
	req *CreateObjectRequest) (uploadURL string, err error) {
	// Construct an appropriate URL.
	//
	// The documentation (http://goo.gl/IJSlVK) is extremely vague about how this
	// is supposed to work. As of 2015-03-26, it simply gives an example:
	//
	//     POST https://www.googleapis.com/upload/storage/v1/b/<bucket>/o
	//
	// In Google-internal bug 19718068, it was clarified that the intent is that
	// the bucket name be encoded into a single path segment, as defined by RFC
	// 3986.
	bucketSegment := httputil.EncodePathSegment(bucketName)
	opaque := fmt.Sprintf(
		"//www.googleapis.com/upload/storage/v1/b/%s/o",
		bucketSegment)

	url := &url.URL{
		Scheme:   "https",
		Opaque:   opaque,
		RawQuery: "uploadType=resumable&projection=full",
	}

	if req.GenerationPrecondition != nil {
		url.RawQuery = fmt.Sprintf(
			"%s&ifGenerationMatch=%v",
			url.RawQuery,
			*req.GenerationPrecondition)
	}

	// Serialize the object metadata to JSON, for the request body.
	metadataJson, err := serializeMetadata(bucketName, &req.Attrs)
	if err != nil {
		err = fmt.Errorf("serializeMetadata: %v", err)
		return
	}

	// Create the HTTP request.
	httpReq, err := http.NewRequest(
		"POST",
		url.String(),
		bytes.NewReader(metadataJson))

	if err != nil {
		err = fmt.Errorf("http.NewRequest: %v", err)
		return
	}

	// Set up HTTP request headers.
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("User-Agent", "github.com-jacobsa-gloud-gcs")
	httpReq.Header.Set("X-Upload-Content-Type", req.Attrs.ContentType)

	// Execute the HTTP request.
	httpRes, err := httpClient.Do(httpReq)
	if err != nil {
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		return
	}

	// Extract the Location header.
	uploadURL = httpRes.Header.Get("Location")
	if uploadURL == "" {
		err = fmt.Errorf("Expected a Location header.")
		return
	}

	return
}

func createObject(
	httpClient *http.Client,
	bucketName string,
	ctx context.Context,
	req *CreateObjectRequest) (o *storage.Object, err error) {
	// We encode using json.NewEncoder, which is documented to silently transform
	// invalid UTF-8 (cf. http://goo.gl/3gIUQB). So we can't rely on the server
	// to detect this for us.
	if !utf8.ValidString(req.Attrs.Name) {
		err = errors.New("Invalid object name: not valid UTF-8")
		return
	}

	// Start a resumable upload, obtaining an upload URL.
	uploadURL, err := startResumableUpload(
		httpClient,
		bucketName,
		ctx,
		req)

	if err != nil {
		err = fmt.Errorf("startResumableUpload: %v", err)
		return
	}

	// Make a follow-up request to the upload URL.
	httpReq, err := http.NewRequest("PUT", uploadURL, req.Contents)
	httpReq.Header.Set("Content-Type", req.Attrs.ContentType)

	httpRes, err := httpClient.Do(httpReq)
	if err != nil {
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		// Special case: handle precondition errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusPreconditionFailed {
				err = &PreconditionError{Err: typed}
			}
		}

		return
	}

	// Parse the response.
	var rawObject *storagev1.Object
	if err = json.NewDecoder(httpRes.Body).Decode(&rawObject); err != nil {
		return
	}

	// Convert the response.
	if o, err = fromRawObject(bucketName, rawObject); err != nil {
		return
	}

	return
}
<commit_msg>Ensure that googleapi.Error makes it up the stack from CreateObject.<commit_after>// 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 gcs

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"unicode/utf8"

	"github.com/jacobsa/gcloud/httputil"
	"golang.org/x/net/context"
	"google.golang.org/api/googleapi"
	storagev1 "google.golang.org/api/storage/v1"
	"google.golang.org/cloud/storage"
)

func toRawAcls(in []storage.ACLRule) []*storagev1.ObjectAccessControl {
	out := make([]*storagev1.ObjectAccessControl, len(in))
	for i, rule := range in {
		out[i] = &storagev1.ObjectAccessControl{
			Entity: string(rule.Entity),
			Role:   string(rule.Role),
		}
	}

	return out
}

func toRawObject(
	bucketName string,
	in *storage.ObjectAttrs) (out *storagev1.Object, err error) {
	out = &storagev1.Object{
		Bucket:          bucketName,
		Name:            in.Name,
		ContentType:     in.ContentType,
		ContentLanguage: in.ContentLanguage,
		ContentEncoding: in.ContentEncoding,
		CacheControl:    in.CacheControl,
		Acl:             toRawAcls(in.ACL),
		Metadata:        in.Metadata,
	}

	return
}

// Create the JSON for an "object resource", for use in an Objects.insert body.
func serializeMetadata(
	bucketName string,
	attrs *storage.ObjectAttrs) (out []byte, err error) {
	// Convert to storagev1.Object.
	rawObject, err := toRawObject(bucketName, attrs)
	if err != nil {
		err = fmt.Errorf("toRawObject: %v", err)
		return
	}

	// Serialize.
	out, err = json.Marshal(rawObject)
	if err != nil {
		err = fmt.Errorf("json.Marshal: %v", err)
		return
	}

	return
}

func startResumableUpload(
	httpClient *http.Client,
	bucketName string,
	ctx context.Context,
	req *CreateObjectRequest) (uploadURL string, err error) {
	// Construct an appropriate URL.
	//
	// The documentation (http://goo.gl/IJSlVK) is extremely vague about how this
	// is supposed to work. As of 2015-03-26, it simply gives an example:
	//
	//     POST https://www.googleapis.com/upload/storage/v1/b/<bucket>/o
	//
	// In Google-internal bug 19718068, it was clarified that the intent is that
	// the bucket name be encoded into a single path segment, as defined by RFC
	// 3986.
	bucketSegment := httputil.EncodePathSegment(bucketName)
	opaque := fmt.Sprintf(
		"//www.googleapis.com/upload/storage/v1/b/%s/o",
		bucketSegment)

	url := &url.URL{
		Scheme:   "https",
		Opaque:   opaque,
		RawQuery: "uploadType=resumable&projection=full",
	}

	if req.GenerationPrecondition != nil {
		url.RawQuery = fmt.Sprintf(
			"%s&ifGenerationMatch=%v",
			url.RawQuery,
			*req.GenerationPrecondition)
	}

	// Serialize the object metadata to JSON, for the request body.
	metadataJson, err := serializeMetadata(bucketName, &req.Attrs)
	if err != nil {
		err = fmt.Errorf("serializeMetadata: %v", err)
		return
	}

	// Create the HTTP request.
	httpReq, err := http.NewRequest(
		"POST",
		url.String(),
		bytes.NewReader(metadataJson))

	if err != nil {
		err = fmt.Errorf("http.NewRequest: %v", err)
		return
	}

	// Set up HTTP request headers.
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("User-Agent", "github.com-jacobsa-gloud-gcs")
	httpReq.Header.Set("X-Upload-Content-Type", req.Attrs.ContentType)

	// Execute the HTTP request.
	httpRes, err := httpClient.Do(httpReq)
	if err != nil {
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		return
	}

	// Extract the Location header.
	uploadURL = httpRes.Header.Get("Location")
	if uploadURL == "" {
		err = fmt.Errorf("Expected a Location header.")
		return
	}

	return
}

func createObject(
	httpClient *http.Client,
	bucketName string,
	ctx context.Context,
	req *CreateObjectRequest) (o *storage.Object, err error) {
	// We encode using json.NewEncoder, which is documented to silently transform
	// invalid UTF-8 (cf. http://goo.gl/3gIUQB). So we can't rely on the server
	// to detect this for us.
	if !utf8.ValidString(req.Attrs.Name) {
		err = errors.New("Invalid object name: not valid UTF-8")
		return
	}

	// Start a resumable upload, obtaining an upload URL.
	uploadURL, err := startResumableUpload(
		httpClient,
		bucketName,
		ctx,
		req)

	if err != nil {
		return
	}

	// Make a follow-up request to the upload URL.
	httpReq, err := http.NewRequest("PUT", uploadURL, req.Contents)
	httpReq.Header.Set("Content-Type", req.Attrs.ContentType)

	httpRes, err := httpClient.Do(httpReq)
	if err != nil {
		return
	}

	defer googleapi.CloseBody(httpRes)

	// Check for HTTP-level errors.
	if err = googleapi.CheckResponse(httpRes); err != nil {
		// Special case: handle precondition errors.
		if typed, ok := err.(*googleapi.Error); ok {
			if typed.Code == http.StatusPreconditionFailed {
				err = &PreconditionError{Err: typed}
			}
		}

		return
	}

	// Parse the response.
	var rawObject *storagev1.Object
	if err = json.NewDecoder(httpRes.Body).Decode(&rawObject); err != nil {
		return
	}

	// Convert the response.
	if o, err = fromRawObject(bucketName, rawObject); err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>package generate

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func guessColumnType(goType string) string {
	if goType == "int" {
		return "qb.Int()"
	} else if goType == "int64" {
		return "qb.BigInt()"
	} else if goType == "string" {
		return "qb.Varchar()"
	} else if goType == "*string" {
		return "qb.Varchar()"
	} else if goType == "bool" {
		return "qb.Boolean()"
	} else if goType == "time.Time" {
		return "qb.Timestamp()"
	} else if goType == "*time.Time" {
		return "qb.Timestamp()"
	} else if goType == "uuid.UUID" {
		return "qb.UUID()"
	}
	panic(fmt.Sprintf("Cannot guess column type for go type %s", goType))
}

func makeColumnName(name string) string {
	return ToDBName(name)
}

func getEmptyValue(goType string) string {
	if goType[0] == '*' {
		return "nil"
	} else if goType == "string" {
		return `""`
	} else if goType[0:3] == "int" {
		return "0"
	} else if goType == "time.Time" {
		return "(time.Time{})"
	} else if goType == "uuid.UUID" {
		return "(uuid.UUID{})"
	}
	panic(fmt.Sprintf("I have no empty value for type '%v'", goType))
}

func prepareFieldData(str *StructData, f *FieldData) {
	if f.ColumnName == "" {
		f.ColumnName = makeColumnName(f.Name)
	}
	if f.ColumnType == "" {
		f.ColumnType = guessColumnType(f.Type)
	}
	if f.ColumnModifiers == "" {
		if f.Tags.PrimaryKey {
			f.ColumnModifiers += ".PrimaryKey()"
		}
		if f.Tags.AutoIncrement {
			f.ColumnModifiers += ".AutoIncrement()"
			str.AutoIncrementPKey = f
		}
		if f.Tags.Null {
			f.ColumnModifiers += ".Null()"
		} else if f.Tags.NotNull {
			f.ColumnModifiers += ".NotNull()"
		} else if f.Type[0] == '*' {
			f.ColumnModifiers += ".Null()"
		} else {
			f.ColumnModifiers += ".NotNull()"
		}
	}
	if f.EmptyValue == "" && f.Tags.PrimaryKey {
		f.EmptyValue = getEmptyValue(f.Type)
	}
	if f.NameConst == "" {
		f.NameConst = fmt.Sprintf(
			"%s%s",
			str.Name, f.Name,
		)
	}
	if f.ColumnNameConst == "" {
		f.ColumnNameConst = fmt.Sprintf(
			"%s%sColumnName",
			str.Name, f.Name,
		)
	}
}

func prepareStructData(str *StructData, fd FileData) {
	str.File = fd
	str.PrivateBasename = strings.ToLower(str.Name[0:1]) + str.Name[1:]
	for i := range str.Fields {
		prepareFieldData(str, &str.Fields[i])
	}
}

func loadEmbedded(path string, structs map[string]*StructData, fd FileData) []*StructData {
	var newStructs []*StructData

	var missingStructs []string
	var hasEmbed bool

	for _, str := range structs {
		for _, name := range str.Embed {
			hasEmbed = true
			_, ok := structs[name]
			if !ok {
				missingStructs = append(missingStructs, name)
			}
		}
	}

	if !hasEmbed {
		return []*StructData{}
	}

	otherStructsByName := make(map[string]*StructData)

	if len(missingStructs) != 0 {
		otherStructs, err := ParseDir(path)
		if err != nil {
			panic(err)
		}
		for _, str := range otherStructs {
			prepareStructData(str, fd)
			otherStructsByName[str.Name] = str
		}
	}

	for _, str := range structs {
		for _, name := range str.Embed {
			embedded, ok := structs[name]
			if !ok {
				embedded, ok = otherStructsByName[name]
			}
			if ok {
				for index, fields := range embedded.Indexes {
					if _, ok := str.Indexes[index]; !ok {
						str.Indexes[index] = []int{}
					}
					for _, fieldIndex := range fields {
						str.Indexes[index] = append(str.Indexes[index], len(str.Fields)+fieldIndex)
					}
				}
				for _, field := range embedded.Fields {
					field.FromEmbedded = true
					str.Fields = append(str.Fields, field)
				}
			} else {
				fmt.Println(
					"Could not find embedded struct definition for '" + name + "'")
			}
		}
	}
	return newStructs
}

func postPrepare(filedata *FileData, structs map[string]*StructData) {
	for _, str := range structs {
		for i := range str.Fields {
			if str.Fields[i].Tags.PrimaryKey {
				str.PKeyFields = append(str.PKeyFields, &str.Fields[i])
			}
		}
		for i, f := range str.Fields {
			if f.Tags.PrimaryKey && str.Fields[i].Type == "uuid.UUID" {
				filedata.Imports["github.com/m4rw3r/uuid"] = true
			}
			for _, fkDef := range str.Fields[i].Tags.ForeignKeys {
				var (
					fk           string
					structName   string
					refFieldName string
					refStruct    *StructData
					refField     *FieldData
					onUpdate     string
					onDelete     string
				)
				if strings.Index(fkDef, " ") != -1 {
					splitted := strings.Split(fkDef, " ")
					fk = splitted[0]
					for i, w := range splitted[1 : len(splitted)-1] {
						if strings.ToUpper(w) == "ONUPDATE" {
							onUpdate = strings.ToUpper(splitted[i+2])
						}
						if strings.ToUpper(w) == "ONDELETE" {
							onDelete = strings.ToUpper(splitted[i+2])
						}
					}
				} else {
					fk = fkDef
				}
				if strings.Index(fk, ".") != -1 {
					splitted := strings.Split(fk, ".")
					structName = splitted[0]
					refFieldName = splitted[1]
				} else {
					structName = fk
				}
				refStruct = structs[structName]
				if refFieldName == "" {
					refField = refStruct.PKeyFields[0]
				} else {
					for i := range refStruct.Fields {
						if refStruct.Fields[i].Name == refFieldName {
							refField = &refStruct.Fields[i]
						}
					}
				}

				str.ForeignKeys = append(str.ForeignKeys, FKData{
					Column:    &str.Fields[i],
					RefTable:  refStruct,
					RefColumn: refField,
					OnUpdate:  onUpdate,
					OnDelete:  onDelete,
				})
			}
		}
	}
}

// ProcessFile processes a go file and generates mapper and mappedstruct
// interfaces implementations for the yago structs.
func ProcessFile(logger *log.Logger, path string, file string, pack string, output string) error {

	ext := filepath.Ext(file)
	base := strings.TrimSuffix(file, ext)

	if output == "" {
		output = filepath.Join(path, base+"_yago"+ext)
	}

	filedata := FileData{Package: pack, Imports: make(map[string]bool)}

	structs, err := ParseFile(filepath.Join(path, file))
	if err != nil {
		return err
	}

	structsByName := make(map[string]*StructData)
	for _, str := range structs {
		prepareStructData(str, filedata)
		structsByName[str.Name] = str
		if !str.NoTable {
			filedata.HasTables = true
		}
	}
	loadEmbedded(path, structsByName, filedata)
	postPrepare(&filedata, structsByName)

	outf, err := os.Create(output)
	if err != nil {
		return err
	}

	defer outf.Close()

	if err := prologTemplate.Execute(outf, &filedata); err != nil {
		return err
	}

	for _, str := range structs {
		if err := structPreambleTemplate.Execute(outf, &str); err != nil {
			return err
		}
		if str.NoTable {
			continue
		}
		if err := structTemplate.Execute(outf, &str); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Force all PKFields init before handling FKs<commit_after>package generate

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func guessColumnType(goType string) string {
	if goType == "int" {
		return "qb.Int()"
	} else if goType == "int64" {
		return "qb.BigInt()"
	} else if goType == "string" {
		return "qb.Varchar()"
	} else if goType == "*string" {
		return "qb.Varchar()"
	} else if goType == "bool" {
		return "qb.Boolean()"
	} else if goType == "time.Time" {
		return "qb.Timestamp()"
	} else if goType == "*time.Time" {
		return "qb.Timestamp()"
	} else if goType == "uuid.UUID" {
		return "qb.UUID()"
	}
	panic(fmt.Sprintf("Cannot guess column type for go type %s", goType))
}

func makeColumnName(name string) string {
	return ToDBName(name)
}

func getEmptyValue(goType string) string {
	if goType[0] == '*' {
		return "nil"
	} else if goType == "string" {
		return `""`
	} else if goType[0:3] == "int" {
		return "0"
	} else if goType == "time.Time" {
		return "(time.Time{})"
	} else if goType == "uuid.UUID" {
		return "(uuid.UUID{})"
	}
	panic(fmt.Sprintf("I have no empty value for type '%v'", goType))
}

func prepareFieldData(str *StructData, f *FieldData) {
	if f.ColumnName == "" {
		f.ColumnName = makeColumnName(f.Name)
	}
	if f.ColumnType == "" {
		f.ColumnType = guessColumnType(f.Type)
	}
	if f.ColumnModifiers == "" {
		if f.Tags.PrimaryKey {
			f.ColumnModifiers += ".PrimaryKey()"
		}
		if f.Tags.AutoIncrement {
			f.ColumnModifiers += ".AutoIncrement()"
			str.AutoIncrementPKey = f
		}
		if f.Tags.Null {
			f.ColumnModifiers += ".Null()"
		} else if f.Tags.NotNull {
			f.ColumnModifiers += ".NotNull()"
		} else if f.Type[0] == '*' {
			f.ColumnModifiers += ".Null()"
		} else {
			f.ColumnModifiers += ".NotNull()"
		}
	}
	if f.EmptyValue == "" && f.Tags.PrimaryKey {
		f.EmptyValue = getEmptyValue(f.Type)
	}
	if f.NameConst == "" {
		f.NameConst = fmt.Sprintf(
			"%s%s",
			str.Name, f.Name,
		)
	}
	if f.ColumnNameConst == "" {
		f.ColumnNameConst = fmt.Sprintf(
			"%s%sColumnName",
			str.Name, f.Name,
		)
	}
}

func prepareStructData(str *StructData, fd FileData) {
	str.File = fd
	str.PrivateBasename = strings.ToLower(str.Name[0:1]) + str.Name[1:]
	for i := range str.Fields {
		prepareFieldData(str, &str.Fields[i])
	}
}

func loadEmbedded(path string, structs map[string]*StructData, fd FileData) []*StructData {
	var newStructs []*StructData

	var missingStructs []string
	var hasEmbed bool

	for _, str := range structs {
		for _, name := range str.Embed {
			hasEmbed = true
			_, ok := structs[name]
			if !ok {
				missingStructs = append(missingStructs, name)
			}
		}
	}

	if !hasEmbed {
		return []*StructData{}
	}

	otherStructsByName := make(map[string]*StructData)

	if len(missingStructs) != 0 {
		otherStructs, err := ParseDir(path)
		if err != nil {
			panic(err)
		}
		for _, str := range otherStructs {
			prepareStructData(str, fd)
			otherStructsByName[str.Name] = str
		}
	}

	for _, str := range structs {
		for _, name := range str.Embed {
			embedded, ok := structs[name]
			if !ok {
				embedded, ok = otherStructsByName[name]
			}
			if ok {
				for index, fields := range embedded.Indexes {
					if _, ok := str.Indexes[index]; !ok {
						str.Indexes[index] = []int{}
					}
					for _, fieldIndex := range fields {
						str.Indexes[index] = append(str.Indexes[index], len(str.Fields)+fieldIndex)
					}
				}
				for _, field := range embedded.Fields {
					field.FromEmbedded = true
					str.Fields = append(str.Fields, field)
				}
			} else {
				fmt.Println(
					"Could not find embedded struct definition for '" + name + "'")
			}
		}
	}
	return newStructs
}

func postPrepare(filedata *FileData, structs map[string]*StructData) {
	for _, str := range structs {
		for i := range str.Fields {
			if str.Fields[i].Tags.PrimaryKey {
				str.PKeyFields = append(str.PKeyFields, &str.Fields[i])
			}
		}
	}
	for _, str := range structs {
		for i, f := range str.Fields {
			if f.Tags.PrimaryKey && str.Fields[i].Type == "uuid.UUID" {
				filedata.Imports["github.com/m4rw3r/uuid"] = true
			}
			for _, fkDef := range str.Fields[i].Tags.ForeignKeys {
				var (
					fk           string
					structName   string
					refFieldName string
					refStruct    *StructData
					refField     *FieldData
					onUpdate     string
					onDelete     string
				)
				if strings.Index(fkDef, " ") != -1 {
					splitted := strings.Split(fkDef, " ")
					fk = splitted[0]
					for i, w := range splitted[1 : len(splitted)-1] {
						if strings.ToUpper(w) == "ONUPDATE" {
							onUpdate = strings.ToUpper(splitted[i+2])
						}
						if strings.ToUpper(w) == "ONDELETE" {
							onDelete = strings.ToUpper(splitted[i+2])
						}
					}
				} else {
					fk = fkDef
				}
				if strings.Index(fk, ".") != -1 {
					splitted := strings.Split(fk, ".")
					structName = splitted[0]
					refFieldName = splitted[1]
				} else {
					structName = fk
				}
				refStruct = structs[structName]
				if refFieldName == "" {
					refField = refStruct.PKeyFields[0]
				} else {
					for i := range refStruct.Fields {
						if refStruct.Fields[i].Name == refFieldName {
							refField = &refStruct.Fields[i]
						}
					}
				}

				str.ForeignKeys = append(str.ForeignKeys, FKData{
					Column:    &str.Fields[i],
					RefTable:  refStruct,
					RefColumn: refField,
					OnUpdate:  onUpdate,
					OnDelete:  onDelete,
				})
			}
		}
	}
}

// ProcessFile processes a go file and generates mapper and mappedstruct
// interfaces implementations for the yago structs.
func ProcessFile(logger *log.Logger, path string, file string, pack string, output string) error {

	ext := filepath.Ext(file)
	base := strings.TrimSuffix(file, ext)

	if output == "" {
		output = filepath.Join(path, base+"_yago"+ext)
	}

	filedata := FileData{Package: pack, Imports: make(map[string]bool)}

	structs, err := ParseFile(filepath.Join(path, file))
	if err != nil {
		return err
	}

	structsByName := make(map[string]*StructData)
	for _, str := range structs {
		prepareStructData(str, filedata)
		structsByName[str.Name] = str
		if !str.NoTable {
			filedata.HasTables = true
		}
	}
	loadEmbedded(path, structsByName, filedata)
	postPrepare(&filedata, structsByName)

	outf, err := os.Create(output)
	if err != nil {
		return err
	}

	defer outf.Close()

	if err := prologTemplate.Execute(outf, &filedata); err != nil {
		return err
	}

	for _, str := range structs {
		if err := structPreambleTemplate.Execute(outf, &str); err != nil {
			return err
		}
		if str.NoTable {
			continue
		}
		if err := structTemplate.Execute(outf, &str); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>