<commit_before>package main

import (
	"archive/tar"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/gobwas/glob"
	"github.com/pkg/errors"
	"v.io/x/lib/toposort"
)

func pkg_required(name []string) ([]string, error) {
	sorter := toposort.Sorter{}

	current := append([]string{}, name...)
	newcur := []string{}

	var pkgdbs []struct {
		name string
		deps map[string]bool
	}

	for _, v := range current {
		if !db.Has(v) {
			return nil, errors.Errorf("%s is not installed", v)
		}
	}

	for v := range db.Keys(nil) {
		pkgdb, e := NewPkgdb(v, db)
		if e != nil {
			return nil, errors.WithStack(e)
		}

		t := map[string]bool{}

		for _, x := range pkgdb.Deps {
			t[x] = true
		}

		pkgdbs = append(pkgdbs, struct {
			name string
			deps map[string]bool
		}{v, t})
	}

	for {
		for _, cur := range current {
			sorter.AddNode(cur)
		}

		for _, pkgdb := range pkgdbs {
			t := pkgdb.deps

			for _, x := range current {
				if t[x] {
					newcur = append(newcur, pkgdb.name)
					sorter.AddEdge(x, pkgdb.name)
					break
				}
			}
		}

		_, cycles := sorter.Sort()
		if len(cycles) > 0 {
			return nil, errors.Errorf("detect circular deps%v", cycles)
		}

		if len(newcur) == 0 {
			break
		}

		oldcur := current
		current = newcur
		newcur = oldcur[0:0]
	}

	r := []string{}

	v, _ := sorter.Sort()
	for _, val := range v {
		r = append(r, val.(string))
	}

	log.Debugf("pkg_required[%v]: queried requiredby[%v]\n", name, r)
	return r, nil
}

func pkg_rmfiles(files []Fileinfo, baks []string) error {
	for _, x := range files {
		if x.Type != tar.TypeDir {
			os.Remove(filepath.Join(cfg.ROOT, x.Path))
		}
	}

	for _, x := range files {
		if x.Type == tar.TypeDir {
			p, e := ioutil.ReadDir(x.Path)
			if e == nil && len(p) != 0 {
				os.Remove(filepath.Join(cfg.ROOT, x.Path))
			}
		}
	}

	for _, x := range baks {
		os.Remove(filepath.Join(cfg.ROOT, x+".bak"))
	}

	return nil
}

func pkg_remove(pkgs []string) error {
	if len(pkgs) == 0 {
		return nil
	}

	var rfiles []Fileinfo
	var rbaks []string

	for _, pkg := range pkgs {
		pkgdb, e := NewPkgdb(pkg, db)
		if e != nil {
			return errors.WithStack(e)
		}

		rfiles = append(rfiles, pkgdb.Footprint...)
		rbaks = append(rbaks, pkgdb.Baks...)
	}

	if e := pkg_hook(rfiles, "pre", "remove"); e != nil {
		return e
	}

	if e := pkg_rmfiles(rfiles, rbaks); e != nil {
		return errors.Wrapf(e, "can not remove files")
	}

	if e := pkg_hook(rfiles, "post", "remove"); e != nil {
		return e
	}

	log.Noticef("remove%v: removed files", pkgs)

	for _, pkg := range pkgs {
		if e := Erase(pkg, db); e != nil {
			return errors.WithStack(e)
		}
	}

	return nil
}

func opt_remove(pkgs []string, opts map[harg]bool) error {
	if e := locksys(); e != nil {
		return errors.WithStack(e)
	}
	defer unlocksys()

	if !opts['d'] {
		var e error
		pkgs, e = pkg_required(pkgs)
		if e != nil {
			return errors.Wrapf(e, "can not query required of %v", pkgs)
		}
	}

	if len(pkgs) > 1 {
		if !ask("do you really want to remove %v\n", pkgs) {
			return nil
		}
	}

	return pkg_remove(pkgs)
}

func opt_rmnoneed(pattern string) error {
	if e := locksys(); e != nil {
		return errors.WithStack(e)
	}
	defer unlocksys()

	var pkgdbs []*Pkgdb

	m := map[string][]string{}

	for v := range db.Keys(nil) {
		pkgdb, e := NewPkgdb(v, db)
		if e != nil {
			return errors.WithStack(e)
		}

		for _, x := range pkgdb.Deps {
			m[x] = append(m[x], x)
		}

		pkgdbs = append(pkgdbs, pkgdb)
	}

	var rmlist []string
	t := map[string]bool{}

	glob := glob.MustCompile(pattern)

	for l, n := -1, 0; l != n; l, n = n, len(rmlist) {
		for _, v := range pkgdbs {
			if glob.Match(v.Name) && !t[v.Name] && len(m[v.Name]) == 0 && !v.User {
				rmlist = append(rmlist, v.Name)
				t[v.Name] = true

				newmx := []string{}
				for _, x := range m[v.Name] {
					if x != v.Name {
						newmx = append(newmx, x)
					}
				}
				m[v.Name] = newmx
			}
		}
	}

	reverse(rmlist)

	if len(rmlist) > 1 {
		if !ask("do you really want to remove %v\n", rmlist) {
			return nil
		}
	}

	if e := pkg_remove(rmlist); e != nil {
		return errors.Wrapf(e, "can not remove files")
	}

	return nil
}
<commit_msg>pkg: fixed-point iteration to remove all empty dir<commit_after>package main

import (
	"archive/tar"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/gobwas/glob"
	"github.com/pkg/errors"
	"v.io/x/lib/toposort"
)

func pkg_required(name []string) ([]string, error) {
	sorter := toposort.Sorter{}

	current := append([]string{}, name...)
	newcur := []string{}

	var pkgdbs []struct {
		name string
		deps map[string]bool
	}

	for _, v := range current {
		if !db.Has(v) {
			return nil, errors.Errorf("%s is not installed", v)
		}
	}

	for v := range db.Keys(nil) {
		pkgdb, e := NewPkgdb(v, db)
		if e != nil {
			return nil, errors.WithStack(e)
		}

		t := map[string]bool{}

		for _, x := range pkgdb.Deps {
			t[x] = true
		}

		pkgdbs = append(pkgdbs, struct {
			name string
			deps map[string]bool
		}{v, t})
	}

	for {
		for _, cur := range current {
			sorter.AddNode(cur)
		}

		for _, pkgdb := range pkgdbs {
			t := pkgdb.deps

			for _, x := range current {
				if t[x] {
					newcur = append(newcur, pkgdb.name)
					sorter.AddEdge(x, pkgdb.name)
					break
				}
			}
		}

		_, cycles := sorter.Sort()
		if len(cycles) > 0 {
			return nil, errors.Errorf("detect circular deps%v", cycles)
		}

		if len(newcur) == 0 {
			break
		}

		oldcur := current
		current = newcur
		newcur = oldcur[0:0]
	}

	r := []string{}

	v, _ := sorter.Sort()
	for _, val := range v {
		r = append(r, val.(string))
	}

	log.Debugf("pkg_required[%v]: queried requiredby[%v]\n", name, r)
	return r, nil
}

func pkg_rmfiles(files []Fileinfo, baks []string) error {
	for _, x := range files {
		if x.Type != tar.TypeDir {
			os.Remove(filepath.Join(cfg.ROOT, x.Path))
		}
	}

	for {
		uns := false
		for _, x := range files {
			if x.Type == tar.TypeDir {
				p, e := ioutil.ReadDir(x.Path)
				if e == nil && len(p) != 0 {
					uns = true
					os.Remove(filepath.Join(cfg.ROOT, x.Path))
				}
			}
		}
		if !uns {
			break
		}
	}

	for _, x := range baks {
		os.Remove(filepath.Join(cfg.ROOT, x+".bak"))
	}

	return nil
}

func pkg_remove(pkgs []string) error {
	if len(pkgs) == 0 {
		return nil
	}

	var rfiles []Fileinfo
	var rbaks []string

	for _, pkg := range pkgs {
		pkgdb, e := NewPkgdb(pkg, db)
		if e != nil {
			return errors.WithStack(e)
		}

		rfiles = append(rfiles, pkgdb.Footprint...)
		rbaks = append(rbaks, pkgdb.Baks...)
	}

	if e := pkg_hook(rfiles, "pre", "remove"); e != nil {
		return e
	}

	if e := pkg_rmfiles(rfiles, rbaks); e != nil {
		return errors.Wrapf(e, "can not remove files")
	}

	if e := pkg_hook(rfiles, "post", "remove"); e != nil {
		return e
	}

	log.Noticef("remove%v: removed files", pkgs)

	for _, pkg := range pkgs {
		if e := Erase(pkg, db); e != nil {
			return errors.WithStack(e)
		}
	}

	return nil
}

func opt_remove(pkgs []string, opts map[harg]bool) error {
	if e := locksys(); e != nil {
		return errors.WithStack(e)
	}
	defer unlocksys()

	if !opts['d'] {
		var e error
		pkgs, e = pkg_required(pkgs)
		if e != nil {
			return errors.Wrapf(e, "can not query required of %v", pkgs)
		}
	}

	if len(pkgs) > 1 {
		if !ask("do you really want to remove %v\n", pkgs) {
			return nil
		}
	}

	return pkg_remove(pkgs)
}

func opt_rmnoneed(pattern string) error {
	if e := locksys(); e != nil {
		return errors.WithStack(e)
	}
	defer unlocksys()

	var pkgdbs []*Pkgdb

	m := map[string][]string{}

	for v := range db.Keys(nil) {
		pkgdb, e := NewPkgdb(v, db)
		if e != nil {
			return errors.WithStack(e)
		}

		for _, x := range pkgdb.Deps {
			m[x] = append(m[x], x)
		}

		pkgdbs = append(pkgdbs, pkgdb)
	}

	var rmlist []string
	t := map[string]bool{}

	glob := glob.MustCompile(pattern)

	for l, n := -1, 0; l != n; l, n = n, len(rmlist) {
		for _, v := range pkgdbs {
			if glob.Match(v.Name) && !t[v.Name] && len(m[v.Name]) == 0 && !v.User {
				rmlist = append(rmlist, v.Name)
				t[v.Name] = true

				newmx := []string{}
				for _, x := range m[v.Name] {
					if x != v.Name {
						newmx = append(newmx, x)
					}
				}
				m[v.Name] = newmx
			}
		}
	}

	reverse(rmlist)

	if len(rmlist) > 1 {
		if !ask("do you really want to remove %v\n", rmlist) {
			return nil
		}
	}

	if e := pkg_remove(rmlist); e != nil {
		return errors.Wrapf(e, "can not remove files")
	}

	return nil
}
<|endoftext|>


<commit_before>package libcentrifugo

import (
	"container/heap"
	"sync"
	"time"

	"github.com/centrifugal/centrifugo/libcentrifugo/priority"
)

// memoryEngine allows to run Centrifugo without using Redis at all. All data managed inside process
// memory. With this engine you can only run single Centrifugo node. If you need to scale you should
// use Redis engine instead.
type memoryEngine struct {
	app         *application
	presenceHub *memoryPresenceHub
	historyHub  *memoryHistoryHub
}

func newMemoryEngine(app *application) *memoryEngine {
	return &memoryEngine{
		app:         app,
		presenceHub: newMemoryPresenceHub(),
		historyHub:  newMemoryHistoryHub(),
	}
}

func (e *memoryEngine) name() string {
	return "In memory – single node only"
}

func (e *memoryEngine) initialize() error {
	err := e.historyHub.initialize()
	return err
}

func (e *memoryEngine) publish(chID ChannelID, message []byte) error {
	return e.app.handleMsg(chID, message)
}

func (e *memoryEngine) subscribe(chID ChannelID) error {
	return nil
}

func (e *memoryEngine) unsubscribe(chID ChannelID) error {
	return nil
}

func (e *memoryEngine) addPresence(chID ChannelID, uid ConnID, info ClientInfo) error {
	return e.presenceHub.add(chID, uid, info)
}

func (e *memoryEngine) removePresence(chID ChannelID, uid ConnID) error {
	return e.presenceHub.remove(chID, uid)
}

func (e *memoryEngine) presence(chID ChannelID) (map[ConnID]ClientInfo, error) {
	return e.presenceHub.get(chID)
}

func (e *memoryEngine) addHistoryMessage(chID ChannelID, message Message, size, lifetime int64) error {
	return e.historyHub.add(chID, message, size, lifetime)
}

func (e *memoryEngine) history(chID ChannelID) ([]Message, error) {
	return e.historyHub.get(chID)
}

type memoryPresenceHub struct {
	sync.Mutex
	presence map[ChannelID]map[ConnID]ClientInfo
}

func newMemoryPresenceHub() *memoryPresenceHub {
	return &memoryPresenceHub{
		presence: make(map[ChannelID]map[ConnID]ClientInfo),
	}
}

func (h *memoryPresenceHub) add(chID ChannelID, uid ConnID, info ClientInfo) error {
	h.Lock()
	defer h.Unlock()

	_, ok := h.presence[chID]
	if !ok {
		h.presence[chID] = make(map[ConnID]ClientInfo)
	}
	h.presence[chID][uid] = info
	return nil
}

func (h *memoryPresenceHub) remove(chID ChannelID, uid ConnID) error {
	h.Lock()
	defer h.Unlock()

	if _, ok := h.presence[chID]; !ok {
		return nil
	}
	if _, ok := h.presence[chID][uid]; !ok {
		return nil
	}

	delete(h.presence[chID], uid)

	// clean up map if needed
	if len(h.presence[chID]) == 0 {
		delete(h.presence, chID)
	}

	return nil
}

func (h *memoryPresenceHub) get(chID ChannelID) (map[ConnID]ClientInfo, error) {
	h.Lock()
	defer h.Unlock()

	presence, ok := h.presence[chID]
	if !ok {
		// return empty map
		return map[ConnID]ClientInfo{}, nil
	}
	// FIXME: Return copy, since we release the lock
	return presence, nil
}

type historyItem struct {
	messages []Message
	expireAt int64
}

func (i historyItem) isExpired() bool {
	return i.expireAt < time.Now().Unix()
}

type memoryHistoryHub struct {
	sync.Mutex // FIXME: Change to RWLock
	history    map[ChannelID]historyItem
	queue      priority.Queue
	nextCheck  int64
}

func newMemoryHistoryHub() *memoryHistoryHub {
	return &memoryHistoryHub{
		history:   make(map[ChannelID]historyItem),
		queue:     priority.MakeQueue(),
		nextCheck: 0,
	}
}

func (h *memoryHistoryHub) initialize() error {
	go h.expire()
	return nil
}

func (h *memoryHistoryHub) expire() {
	for {
		time.Sleep(time.Second)
		h.Lock()
		if h.nextCheck == 0 || h.nextCheck > time.Now().Unix() {
			h.Unlock()
			continue
		}
		for h.queue.Len() > 0 {
			item := heap.Pop(&h.queue).(*priority.Item)
			expireAt := item.Priority
			if expireAt > time.Now().Unix() {
				heap.Push(&h.queue, item)
				break
			}
			chID := ChannelID(item.Value)
			hItem, ok := h.history[chID]
			if !ok {
				continue
			}
			if hItem.expireAt <= expireAt {
				delete(h.history, chID)
			}
		}
		h.nextCheck = h.nextCheck + 300
		h.Unlock()
	}
}

func (h *memoryHistoryHub) add(chID ChannelID, message Message, size, lifetime int64) error {
	h.Lock()
	defer h.Unlock()

	_, ok := h.history[chID]

	expireAt := time.Now().Unix() + lifetime
	heap.Push(&h.queue, &priority.Item{Value: string(chID), Priority: expireAt})

	if !ok {
		h.history[chID] = historyItem{
			messages: []Message{message},
			expireAt: expireAt,
		}
	} else {
		messages := h.history[chID].messages
		messages = append([]Message{message}, messages...)
		if int64(len(messages)) > size {
			messages = messages[0:size]
		}
		h.history[chID] = historyItem{
			messages: messages,
			expireAt: expireAt,
		}
	}

	if h.nextCheck == 0 || h.nextCheck > expireAt {
		h.nextCheck = expireAt
	}

	return nil
}

func (h *memoryHistoryHub) get(chID ChannelID) ([]Message, error) {
	h.Lock()
	defer h.Unlock()

	hItem, ok := h.history[chID]
	if !ok {
		// return empty slice
		return []Message{}, nil
	}
	if hItem.isExpired() {
		// return empty slice
		delete(h.history, chID)
		return []Message{}, nil
	}
	return hItem.messages, nil
}
<commit_msg>copy presence map, use RWMutex in presence and history hubs<commit_after>package libcentrifugo

import (
	"container/heap"
	"sync"
	"time"

	"github.com/centrifugal/centrifugo/libcentrifugo/priority"
)

// memoryEngine allows to run Centrifugo without using Redis at all. All data managed inside process
// memory. With this engine you can only run single Centrifugo node. If you need to scale you should
// use Redis engine instead.
type memoryEngine struct {
	app         *application
	presenceHub *memoryPresenceHub
	historyHub  *memoryHistoryHub
}

func newMemoryEngine(app *application) *memoryEngine {
	return &memoryEngine{
		app:         app,
		presenceHub: newMemoryPresenceHub(),
		historyHub:  newMemoryHistoryHub(),
	}
}

func (e *memoryEngine) name() string {
	return "In memory – single node only"
}

func (e *memoryEngine) initialize() error {
	err := e.historyHub.initialize()
	return err
}

func (e *memoryEngine) publish(chID ChannelID, message []byte) error {
	return e.app.handleMsg(chID, message)
}

func (e *memoryEngine) subscribe(chID ChannelID) error {
	return nil
}

func (e *memoryEngine) unsubscribe(chID ChannelID) error {
	return nil
}

func (e *memoryEngine) addPresence(chID ChannelID, uid ConnID, info ClientInfo) error {
	return e.presenceHub.add(chID, uid, info)
}

func (e *memoryEngine) removePresence(chID ChannelID, uid ConnID) error {
	return e.presenceHub.remove(chID, uid)
}

func (e *memoryEngine) presence(chID ChannelID) (map[ConnID]ClientInfo, error) {
	return e.presenceHub.get(chID)
}

func (e *memoryEngine) addHistoryMessage(chID ChannelID, message Message, size, lifetime int64) error {
	return e.historyHub.add(chID, message, size, lifetime)
}

func (e *memoryEngine) history(chID ChannelID) ([]Message, error) {
	return e.historyHub.get(chID)
}

type memoryPresenceHub struct {
	sync.RWMutex
	presence map[ChannelID]map[ConnID]ClientInfo
}

func newMemoryPresenceHub() *memoryPresenceHub {
	return &memoryPresenceHub{
		presence: make(map[ChannelID]map[ConnID]ClientInfo),
	}
}

func (h *memoryPresenceHub) add(chID ChannelID, uid ConnID, info ClientInfo) error {
	h.Lock()
	defer h.Unlock()

	_, ok := h.presence[chID]
	if !ok {
		h.presence[chID] = make(map[ConnID]ClientInfo)
	}
	h.presence[chID][uid] = info
	return nil
}

func (h *memoryPresenceHub) remove(chID ChannelID, uid ConnID) error {
	h.Lock()
	defer h.Unlock()

	if _, ok := h.presence[chID]; !ok {
		return nil
	}
	if _, ok := h.presence[chID][uid]; !ok {
		return nil
	}

	delete(h.presence[chID], uid)

	// clean up map if needed
	if len(h.presence[chID]) == 0 {
		delete(h.presence, chID)
	}

	return nil
}

func (h *memoryPresenceHub) get(chID ChannelID) (map[ConnID]ClientInfo, error) {
	h.RLock()
	defer h.RUnlock()

	presence, ok := h.presence[chID]
	if !ok {
		// return empty map
		return map[ConnID]ClientInfo{}, nil
	}

	var data map[ConnID]ClientInfo
	data = make(map[ConnID]ClientInfo, len(presence))
	for k, v := range presence {
		data[k] = v
	}
	return data, nil
}

type historyItem struct {
	messages []Message
	expireAt int64
}

func (i historyItem) isExpired() bool {
	return i.expireAt < time.Now().Unix()
}

type memoryHistoryHub struct {
	sync.RWMutex
	history   map[ChannelID]historyItem
	queue     priority.Queue
	nextCheck int64
}

func newMemoryHistoryHub() *memoryHistoryHub {
	return &memoryHistoryHub{
		history:   make(map[ChannelID]historyItem),
		queue:     priority.MakeQueue(),
		nextCheck: 0,
	}
}

func (h *memoryHistoryHub) initialize() error {
	go h.expire()
	return nil
}

func (h *memoryHistoryHub) expire() {
	for {
		time.Sleep(time.Second)
		h.Lock()
		if h.nextCheck == 0 || h.nextCheck > time.Now().Unix() {
			h.Unlock()
			continue
		}
		for h.queue.Len() > 0 {
			item := heap.Pop(&h.queue).(*priority.Item)
			expireAt := item.Priority
			if expireAt > time.Now().Unix() {
				heap.Push(&h.queue, item)
				break
			}
			chID := ChannelID(item.Value)
			hItem, ok := h.history[chID]
			if !ok {
				continue
			}
			if hItem.expireAt <= expireAt {
				delete(h.history, chID)
			}
		}
		h.nextCheck = h.nextCheck + 300
		h.Unlock()
	}
}

func (h *memoryHistoryHub) add(chID ChannelID, message Message, size, lifetime int64) error {
	h.Lock()
	defer h.Unlock()

	_, ok := h.history[chID]

	expireAt := time.Now().Unix() + lifetime
	heap.Push(&h.queue, &priority.Item{Value: string(chID), Priority: expireAt})

	if !ok {
		h.history[chID] = historyItem{
			messages: []Message{message},
			expireAt: expireAt,
		}
	} else {
		messages := h.history[chID].messages
		messages = append([]Message{message}, messages...)
		if int64(len(messages)) > size {
			messages = messages[0:size]
		}
		h.history[chID] = historyItem{
			messages: messages,
			expireAt: expireAt,
		}
	}

	if h.nextCheck == 0 || h.nextCheck > expireAt {
		h.nextCheck = expireAt
	}

	return nil
}

func (h *memoryHistoryHub) get(chID ChannelID) ([]Message, error) {
	h.RLock()
	defer h.RUnlock()

	hItem, ok := h.history[chID]
	if !ok {
		// return empty slice
		return []Message{}, nil
	}
	if hItem.isExpired() {
		// return empty slice
		delete(h.history, chID)
		return []Message{}, nil
	}
	return hItem.messages, nil
}
<|endoftext|>


<commit_before>package ultralist

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/julienschmidt/httprouter"
)

// Webapp is the main struct of this file.
type Webapp struct {
	Router *httprouter.Router
	server *http.Server
}

// Run is starting the ultralist webapp.
func (w *Webapp) Run() {
	w.server = &http.Server{Addr: ":9976"}

	http.HandleFunc("/", w.handleAuthResponse)
	http.HandleFunc("/favicon.ico", w.handleFavicon)

	w.server.ListenAndServe()
}

func (w *Webapp) handleFavicon(writer http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(writer, "")
}

func (w *Webapp) handleAuthResponse(writer http.ResponseWriter, r *http.Request) {
	cliTokens, ok := r.URL.Query()["token"]
	if ok == false {
		fmt.Println("Something went wrong... I did not get a CLI token back.")
		os.Exit(0)
	}

	backend := NewBackend()
	backend.WriteCreds(cliTokens[0])
	fmt.Println("Authorization successful!")

	http.Redirect(writer, r, w.frontendUrl(), http.StatusSeeOther)

	// sleep 1 second before shutting server down, so we can display msg on web.
	go func() {
		time.Sleep(1 * time.Second)
		w.server.Shutdown(nil)
	}()
}

func (w *Webapp) frontendUrl() string {
	envFrontendURL := os.Getenv("ULTRALIST_FRONTEND_URL")

	if envFrontendURL != "" {
		return envFrontendURL + "/loading?cli_auth_completed=true"
	}

	return "https://app.ultralist.io/loading?cli_auth_completed=true"
}
<commit_msg>update messaging<commit_after>package ultralist

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/julienschmidt/httprouter"
)

// Webapp is the main struct of this file.
type Webapp struct {
	Router *httprouter.Router
	server *http.Server
}

// Run is starting the ultralist webapp.
func (w *Webapp) Run() {
	w.server = &http.Server{Addr: ":9976"}

	http.HandleFunc("/", w.handleAuthResponse)
	http.HandleFunc("/favicon.ico", w.handleFavicon)

	w.server.ListenAndServe()
}

func (w *Webapp) handleFavicon(writer http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(writer, "")
}

func (w *Webapp) handleAuthResponse(writer http.ResponseWriter, r *http.Request) {
	cliTokens, ok := r.URL.Query()["token"]
	if ok == false {
		fmt.Println("Something went wrong... I did not get a CLI token back.")
		os.Exit(0)
	}

	backend := NewBackend()
	backend.WriteCreds(cliTokens[0])
	fmt.Println("Authorization successful! Next, run `todolist init` to sync a list.")

	http.Redirect(writer, r, w.frontendUrl(), http.StatusSeeOther)

	// sleep 1 second before shutting server down, so we can display msg on web.
	go func() {
		time.Sleep(1 * time.Second)
		w.server.Shutdown(nil)
	}()
}

func (w *Webapp) frontendUrl() string {
	envFrontendURL := os.Getenv("ULTRALIST_FRONTEND_URL")

	if envFrontendURL != "" {
		return envFrontendURL + "/loading?cli_auth_completed=true"
	}

	return "https://app.ultralist.io/loading?cli_auth_completed=true"
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

var index = `
	<!DOCTYPE html>

		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title>Upstream Server</title>

			Hello from upstream server %s

	</html
`

func main() {
	name := os.Args[1]
	port := os.Args[2]
	e := echo.New()
	e.Use(middleware.Recover())
	e.Use(middleware.Logger())
	e.GET("/", func(c echo.Context) error {
		return c.HTML(http.StatusOK, fmt.Sprintf(index, name))
	})
	e.Logger.Fatal(e.Start(port))
}
<commit_msg>Update server.go (#122)<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

var index = `
	<!DOCTYPE html>

		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<meta http-equiv="X-UA-Compatible" content="ie=edge">
		<title>Upstream Server</title>

			Hello from upstream server %s

`

func main() {
	name := os.Args[1]
	port := os.Args[2]
	e := echo.New()
	e.Use(middleware.Recover())
	e.Use(middleware.Logger())
	e.GET("/", func(c echo.Context) error {
		return c.HTML(http.StatusOK, fmt.Sprintf(index, name))
	})
	e.Logger.Fatal(e.Start(port))
}
<|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 sinks

import (
	"fmt"
	"time"

	"github.com/golang/glog"
	"k8s.io/heapster/core"
	"k8s.io/heapster/manager"
	"k8s.io/heapster/sinks/hawkular"
	"k8s.io/heapster/sinks/influxdb"
	"k8s.io/heapster/sinks/log"
	"k8s.io/heapster/sinks/metric"
)

type SinkFactory struct {
}

func (this *SinkFactory) Build(uri manager.Uri) (core.DataSink, error) {
	switch uri.Key {
	case "influxdb":
		return influxdb.CreateInfluxdbSink(&uri.Val)
	case "hawkular":
		return hawkular.NewHawkularSink(&uri.Val)
	case "log":
		return logsink.NewLogSink(), nil
	case "metric":
		return metricsink.NewMetricSink(140*time.Second, 15*time.Minute, []string{
			core.MetricCpuUsageRate.MetricDescriptor.Name,
			core.MetricMemoruUsage.MetricDescriptor.Name}), nil
	default:
		return nil, fmt.Errorf("Sink not recognized: %s", uri.Key)
	}
}

func (this *SinkFactory) BuildAll(uris manager.Uris) (*metricsink.MetricSink, []core.DataSink) {
	result := make([]core.DataSink, 0, len(uris))
	var metric *metricsink.MetricSink
	for _, uri := range uris {
		sink, err := this.Build(uri)
		if err != nil {
			glog.Errorf("Failed to create sink: %v", err)
			continue
		}
		if uri.Key == "metric" {
			metric = sink.(*metricsink.MetricSink)
		}
		result = append(result, sink)
	}
	if metric == nil {
		uri := manager.Uri{}
		uri.Set("metrics")
		sink, err := this.Build(uri)
		if err != nil {
			result = append(result, metric)
			metric = sink.(*metricsink.MetricSink)
		}
	}
	return metric, result
}

func NewSinkFactory() *SinkFactory {
	return &SinkFactory{}
}
<commit_msg>Typo fix in sink factory<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 sinks

import (
	"fmt"
	"time"

	"github.com/golang/glog"
	"k8s.io/heapster/core"
	"k8s.io/heapster/manager"
	"k8s.io/heapster/sinks/hawkular"
	"k8s.io/heapster/sinks/influxdb"
	"k8s.io/heapster/sinks/log"
	"k8s.io/heapster/sinks/metric"
)

type SinkFactory struct {
}

func (this *SinkFactory) Build(uri manager.Uri) (core.DataSink, error) {
	switch uri.Key {
	case "influxdb":
		return influxdb.CreateInfluxdbSink(&uri.Val)
	case "hawkular":
		return hawkular.NewHawkularSink(&uri.Val)
	case "log":
		return logsink.NewLogSink(), nil
	case "metric":
		return metricsink.NewMetricSink(140*time.Second, 15*time.Minute, []string{
			core.MetricCpuUsageRate.MetricDescriptor.Name,
			core.MetricMemoruUsage.MetricDescriptor.Name}), nil
	default:
		return nil, fmt.Errorf("Sink not recognized: %s", uri.Key)
	}
}

func (this *SinkFactory) BuildAll(uris manager.Uris) (*metricsink.MetricSink, []core.DataSink) {
	result := make([]core.DataSink, 0, len(uris))
	var metric *metricsink.MetricSink
	for _, uri := range uris {
		sink, err := this.Build(uri)
		if err != nil {
			glog.Errorf("Failed to create sink: %v", err)
			continue
		}
		if uri.Key == "metric" {
			metric = sink.(*metricsink.MetricSink)
		}
		result = append(result, sink)
	}
	if metric == nil {
		uri := manager.Uri{}
		uri.Set("metric")
		sink, err := this.Build(uri)
		if err != nil {
			result = append(result, metric)
			metric = sink.(*metricsink.MetricSink)
		}
	}
	return metric, result
}

func NewSinkFactory() *SinkFactory {
	return &SinkFactory{}
}
<|endoftext|>


<commit_before>package nexmo

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
)

// SMS represents the SMS API functions for sending text messages.
type SMS struct {
	client *Client
}

// SMS message types.
const (
	Text    = "text"
	Binary  = "binary"
	WAPPush = "wappush"
	Unicode = "unicode"
	VCal    = "vcal"
	VCard   = "vcard"
)

// SMS message classes.
const (
	// This type of SMS message is displayed on the mobile screen without being
	// saved in the message store or on the SIM card; unless explicitly saved
	// by the mobile user.
	Flash = iota

	// This message is to be stored in the device memory or the SIM card
	// (depending on memory availability).
	Standard

	// This message class carries SIM card data. The SIM card data must be
	// successfully transferred prior to sending acknowledgment to the service
	// center. An error message is sent to the service center if this
	// transmission is not possible.
	SIMData

	// This message is forwarded from the receiving entity to an external
	// device. The delivery acknowledgment is sent to the service center
	// regardless of whether or not the message was forwarded to the external
	// device.
	Forward
)

// Type SMSMessage defines a single SMS message.
type SMSMessage struct {
	ApiKey               string `json:"api_key"`
	ApiSecret            string `json:"api_secret"`
	From                 string `json:"from"`
	To                   string `json:"to"`
	Type                 string `json:"type"`
	Text                 string `json:"text,omitempty"`              // Optional.
	StatusReportRequired int    `json:"status-report-req,omitempty"` // Optional.
	ClientReference      string `json:"client-ref,omitempty"`        // Optional.
	NetworkCode          string `json:"network-code,omitempty"`      // Optional.
	VCard                string `json:"vcrad,omitempty"`             // Optional.
	VCal                 string `json:"vcal,omitempty"`              // Optional.
	TTL                  int    `json:"ttl,omitempty"`               // Optional.
	Class                int    `json:"message-class,omitempty"`     // Optional.
	Body                 []byte `json:"body,omitempty"`              // Required for Binary message.
	UDH                  []byte `json:"udh,omitempty"`               // Required for Binary message.
}

// MessageReport is the "status report" for a single SMS sent via the Nexmo API
type MessageReport struct {
	Status           string `json:"status"`
	MessageID        string `json:"message-id"`
	To               string `json:"to"`
	ClientReference  string `json:"client-ref"`
	RemainingBalance string `json:"remaining-balance"`
	MessagePrice     string `json:"message-price"`
	Network          string `json:"network"`
	ErrorText        string `json:"error-text"`
}

// MessageResponse contains the response from Nexmo's API after we attempt to
// send any kind of message.
// It will contain one MessageReport for every 160 chars sent.
type MessageResponse struct {
	MessageCount string          `json:"message-count"`
	Messages     []MessageReport `json:"messages"`
}

// Send the message using the specified SMS client.
func (c *SMS) Send(msg *SMSMessage) (*MessageResponse, error) {
	if len(msg.From) <= 0 {
		return nil, errors.New("Invalid From field specified")
	}

	if len(msg.To) <= 0 {
		return nil, errors.New("Invalid To field specified")
	}

	if len(msg.ClientReference) > 40 {
		return nil, errors.New("Client reference too long")
	}

	var messageResponse *MessageResponse

	switch msg.Type {
	// 0 would be default
	case Text:
		if len(msg.Text) <= 0 {
			return nil, errors.New("Invalid message text")
		}
	case Binary:
		if len(msg.UDH) == 0 || len(msg.Body) == 0 {
			return nil, errors.New("Invalid binary message")
		}
	}

	if !c.client.useOauth {
		msg.ApiKey = c.client.apiKey
		msg.ApiSecret = c.client.apiSecret
	}

	client := &http.Client{}

	var r *http.Request
	buf, err := json.Marshal(msg)
	if err != nil {
		return nil, errors.New("Invalid message struct. Cannot convert to json.")
	}
	b := bytes.NewBuffer(buf)
	r, _ = http.NewRequest("POST", apiRoot+"/sms/json", b)

	r.Header.Add("Accept", "application/json")
	r.Header.Add("Content-Type", "application/json")

	resp, err := client.Do(r)

	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)

	err = json.Unmarshal(body, &messageResponse)
	if err != nil {
		return nil, err
	}
	return messageResponse, nil
}
<commit_msg>Add response code type<commit_after>package nexmo

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
)

// SMS represents the SMS API functions for sending text messages.
type SMS struct {
	client *Client
}

// SMS message types.
const (
	Text    = "text"
	Binary  = "binary"
	WAPPush = "wappush"
	Unicode = "unicode"
	VCal    = "vcal"
	VCard   = "vcard"
)

// SMS message classes.
const (
	// This type of SMS message is displayed on the mobile screen without being
	// saved in the message store or on the SIM card; unless explicitly saved
	// by the mobile user.
	Flash = iota

	// This message is to be stored in the device memory or the SIM card
	// (depending on memory availability).
	Standard

	// This message class carries SIM card data. The SIM card data must be
	// successfully transferred prior to sending acknowledgment to the service
	// center. An error message is sent to the service center if this
	// transmission is not possible.
	SIMData

	// This message is forwarded from the receiving entity to an external
	// device. The delivery acknowledgment is sent to the service center
	// regardless of whether or not the message was forwarded to the external
	// device.
	Forward
)

// Type SMSMessage defines a single SMS message.
type SMSMessage struct {
	ApiKey               string `json:"api_key"`
	ApiSecret            string `json:"api_secret"`
	From                 string `json:"from"`
	To                   string `json:"to"`
	Type                 string `json:"type"`
	Text                 string `json:"text,omitempty"`              // Optional.
	StatusReportRequired int    `json:"status-report-req,omitempty"` // Optional.
	ClientReference      string `json:"client-ref,omitempty"`        // Optional.
	NetworkCode          string `json:"network-code,omitempty"`      // Optional.
	VCard                string `json:"vcrad,omitempty"`             // Optional.
	VCal                 string `json:"vcal,omitempty"`              // Optional.
	TTL                  int    `json:"ttl,omitempty"`               // Optional.
	Class                int    `json:"message-class,omitempty"`     // Optional.
	Body                 []byte `json:"body,omitempty"`              // Required for Binary message.
	UDH                  []byte `json:"udh,omitempty"`               // Required for Binary message.
}

type ResponseCode int

func (c ResponseCode) String() string {
	return responseCodeMap[c]
}

const (
	ResponseSuccess = iota
	ResponseThrottled
	ResponseMissingParams
	ResponseInvalidParams
	ResponseInvalidCredentials
	ResponseInternalError
	ResponseInvalidMessage
	ResponseNumberBarred
	ResponsePartnerAcctBarred
	ResponsePartnerQuotaExceeded
	ResponseRESTNotEnabled
	ResponseMessageTooLong
	ResponseCommunicationFailed
	ResponseInvalidSignature
	ResponseInvalidSenderAddress
	ResponseInvalidTTL
	ResponseFacilityNotAllowed
	ResponseInvalidMessageClass
)

var responseCodeMap = map[ResponseCode]string{
	ResponseSuccess:              "Success",
	ResponseThrottled:            "Throttled",
	ResponseMissingParams:        "Missing params",
	ResponseInvalidParams:        "Invalid params",
	ResponseInvalidCredentials:   "Invalid credentials",
	ResponseInternalError:        "Internal error",
	ResponseInvalidMessage:       "Invalid message",
	ResponseNumberBarred:         "Number barred",
	ResponsePartnerAcctBarred:    "Partner account barred",
	ResponsePartnerQuotaExceeded: "Partner quota exceeded",
	ResponseRESTNotEnabled:       "Account not enabled for REST",
	ResponseMessageTooLong:       "Message too long",
	ResponseCommunicationFailed:  "Communication failed",
	ResponseInvalidSignature:     "Invalid signature",
	ResponseInvalidSenderAddress: "Invalid sender address",
	ResponseInvalidTTL:           "Invalid TTL",
	ResponseFacilityNotAllowed:   "Facility not allowed",
	ResponseInvalidMessageClass:  "Invalid message class",
}

// MessageReport is the "status report" for a single SMS sent via the Nexmo API
type MessageReport struct {
	Status           ResponseCode `json:"status,string"`
	MessageID        string       `json:"message-id"`
	To               string       `json:"to"`
	ClientReference  string       `json:"client-ref"`
	RemainingBalance string       `json:"remaining-balance"`
	MessagePrice     string       `json:"message-price"`
	Network          string       `json:"network"`
	ErrorText        string       `json:"error-text"`
}

// MessageResponse contains the response from Nexmo's API after we attempt to
// send any kind of message.
// It will contain one MessageReport for every 160 chars sent.
type MessageResponse struct {
	MessageCount int             `json:"message-count,string"`
	Messages     []MessageReport `json:"messages"`
}

// Send the message using the specified SMS client.
func (c *SMS) Send(msg *SMSMessage) (*MessageResponse, error) {
	if len(msg.From) <= 0 {
		return nil, errors.New("Invalid From field specified")
	}

	if len(msg.To) <= 0 {
		return nil, errors.New("Invalid To field specified")
	}

	if len(msg.ClientReference) > 40 {
		return nil, errors.New("Client reference too long")
	}

	var messageResponse *MessageResponse

	switch msg.Type {
	// 0 would be default
	case Text:
		if len(msg.Text) <= 0 {
			return nil, errors.New("Invalid message text")
		}
	case Binary:
		if len(msg.UDH) == 0 || len(msg.Body) == 0 {
			return nil, errors.New("Invalid binary message")
		}
	}

	if !c.client.useOauth {
		msg.ApiKey = c.client.apiKey
		msg.ApiSecret = c.client.apiSecret
	}

	client := &http.Client{}

	var r *http.Request
	buf, err := json.Marshal(msg)
	if err != nil {
		return nil, errors.New("Invalid message struct. Cannot convert to json.")
	}
	b := bytes.NewBuffer(buf)
	r, _ = http.NewRequest("POST", apiRoot+"/sms/json", b)

	r.Header.Add("Accept", "application/json")
	r.Header.Add("Content-Type", "application/json")

	resp, err := client.Do(r)

	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)

	err = json.Unmarshal(body, &messageResponse)
	if err != nil {
		return nil, err
	}
	return messageResponse, nil
}
<|endoftext|>


<commit_before>package mallory

import (
	"errors"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os/user"
	"sync"

	"golang.org/x/crypto/ssh"
)

//
type SSH struct {
	// global config file
	Cfg *Config
	// connect URL
	URL *url.URL
	// SSH client
	Client *ssh.Client
	// SSH client config
	CliCfg *ssh.ClientConfig
	// direct fetcher
	Direct *Direct
	// atomic Dial
	dialMutex sync.RWMutex
	// dialed conns
	conns []net.Conn
	// connsMutex
	connsMutex sync.RWMutex
}

// Create and initialize
func NewSSH(c *Config) (self *SSH, err error) {
	self = &SSH{
		Cfg:    c,
		CliCfg: &ssh.ClientConfig{},
	}
	// e.g.  ssh://user:passwd@192.168.1.1:1122
	self.URL, err = url.Parse(c.File.RemoteServer)
	if err != nil {
		return
	}

	if self.URL.User != nil {
		self.CliCfg.User = self.URL.User.Username()
	} else {
		u, err := user.Current()
		if err != nil {
			return self, err
		}
		// u.Name is the full name, should not be used
		self.CliCfg.User = u.Username
	}

	// 1) try RSA keyring first
	for {
		id_rsa := c.File.PrivateKey
		pem, err := ioutil.ReadFile(id_rsa)
		if err != nil {
			L.Printf("ReadFile %s failed:%s\n", c.File.PrivateKey, err)
			break
		}
		signer, err := ssh.ParsePrivateKey(pem)
		if err != nil {
			L.Printf("ParsePrivateKey %s failed:%s\n", c.File.PrivateKey, err)
			break
		}
		self.CliCfg.Auth = append(self.CliCfg.Auth, ssh.PublicKeys(signer))
		// stop !!
		break
	}
	// 2) try password
	for {
		if self.URL.User == nil {
			break
		}
		if pass, ok := self.URL.User.Password(); ok {
			self.CliCfg.Auth = append(self.CliCfg.Auth, ssh.Password(pass))
		}
		// stop here!!
		break
	}

	if len(self.CliCfg.Auth) == 0 {
		//TODO: keyboard intercative
		err = errors.New("Invalid auth method, please add password or generate ssh keys")
		return
	}

	// first time to dial to remote server, make sure it is available
	self.Client, err = ssh.Dial("tcp", self.URL.Host, self.CliCfg)
	if err != nil {
		return
	}

	dial := func(network, addr string) (c net.Conn, err error) {
		for i := 0; i < 8; i++ {
			self.dialMutex.RLock()
			saveClient := self.Client
			if self.Client != nil && err == nil {
				c, err = self.Client.Dial(network, addr)
			}
			self.dialMutex.RUnlock()

			if self.Client != nil && err == nil {
				self.connsMutex.Lock()
				self.conns = append(self.conns, c)
				self.connsMutex.Unlock()
				break // success
			}

			self.dialMutex.Lock()
			if saveClient == self.Client { // the thread to reconnect
				if self.Client != nil {
					self.Client.Close()
				}

				self.connsMutex.RLock()
				for _, d := range self.conns {
					err1 := d.Close()
					if err1 != nil {
						L.Println(err1)
					}
				}
				self.connsMutex.RUnlock()

				self.connsMutex.Lock()
				self.conns = self.conns[:0]
				self.connsMutex.Unlock()

				L.Printf("reconnecting %s: %s\n", self.URL.Host, err)
				self.Client, err = ssh.Dial("tcp", self.URL.Host, self.CliCfg)
			}
			self.dialMutex.Unlock()
		}
		return
	}

	self.Direct = &Direct{
		Tr: &http.Transport{Dial: dial},
	}
	return
}

func (self *SSH) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	self.Direct.ServeHTTP(w, r)
}

func (self *SSH) Connect(w http.ResponseWriter, r *http.Request) {
	self.Direct.Connect(w, r)
}
<commit_msg>Revert "fix close"<commit_after>package mallory

import (
	"errors"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os/user"
	"sync"

	"golang.org/x/crypto/ssh"
)

//
type SSH struct {
	// global config file
	Cfg *Config
	// connect URL
	URL *url.URL
	// SSH client
	Client *ssh.Client
	// SSH client config
	CliCfg *ssh.ClientConfig
	// direct fetcher
	Direct *Direct
	// atomic Dial
	mutex sync.RWMutex
}

// Create and initialize
func NewSSH(c *Config) (self *SSH, err error) {
	self = &SSH{
		Cfg:    c,
		CliCfg: &ssh.ClientConfig{},
	}
	// e.g.  ssh://user:passwd@192.168.1.1:1122
	self.URL, err = url.Parse(c.File.RemoteServer)
	if err != nil {
		return
	}

	if self.URL.User != nil {
		self.CliCfg.User = self.URL.User.Username()
	} else {
		u, err := user.Current()
		if err != nil {
			return self, err
		}
		// u.Name is the full name, should not be used
		self.CliCfg.User = u.Username
	}

	// 1) try RSA keyring first
	for {
		id_rsa := c.File.PrivateKey
		pem, err := ioutil.ReadFile(id_rsa)
		if err != nil {
			L.Printf("ReadFile %s failed:%s\n", c.File.PrivateKey, err)
			break
		}
		signer, err := ssh.ParsePrivateKey(pem)
		if err != nil {
			L.Printf("ParsePrivateKey %s failed:%s\n", c.File.PrivateKey, err)
			break
		}
		self.CliCfg.Auth = append(self.CliCfg.Auth, ssh.PublicKeys(signer))
		// stop !!
		break
	}
	// 2) try password
	for {
		if self.URL.User == nil {
			break
		}
		if pass, ok := self.URL.User.Password(); ok {
			self.CliCfg.Auth = append(self.CliCfg.Auth, ssh.Password(pass))
		}
		// stop here!!
		break
	}

	if len(self.CliCfg.Auth) == 0 {
		//TODO: keyboard intercative
		err = errors.New("Invalid auth method, please add password or generate ssh keys")
		return
	}

	// first time to dial to remote server, make sure it is available
	self.Client, err = ssh.Dial("tcp", self.URL.Host, self.CliCfg)
	if err != nil {
		return
	}

	dial := func(network, addr string) (c net.Conn, err error) {
		for i := 0; i < 8; i++ {
			self.mutex.RLock()
			saveClient := self.Client
			if self.Client != nil && err == nil {
				c, err = self.Client.Dial(network, addr)
			}
			self.mutex.RUnlock()
			if self.Client != nil && err == nil {
				break // success
			}
			self.mutex.Lock()
			if saveClient == self.Client { // the thread to reconnect
				if self.Client != nil {
					self.Client.Close()
				}
				L.Printf("reconnecting %s...\n", self.URL.Host)
				self.Client, err = ssh.Dial("tcp", self.URL.Host, self.CliCfg)
			}
			self.mutex.Unlock()
		}
		return
	}

	self.Direct = &Direct{
		Tr: &http.Transport{Dial: dial},
	}
	return
}

func (self *SSH) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	self.Direct.ServeHTTP(w, r)
}

func (self *SSH) Connect(w http.ResponseWriter, r *http.Request) {
	self.Direct.Connect(w, r)
}
<|endoftext|>


<commit_before>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package id3v2

import (
	"bufio"
	"errors"
	"io"
	"os"
)

var ErrNoFile = errors.New("tag was not initialized with file")

// Tag stores all information about opened tag.
type Tag struct {
	frames    map[string]Framer
	sequences map[string]*sequence

	defaultEncoding Encoding
	reader          io.Reader
	originalSize    int64
	version         byte
}

// AddFrame adds f to tag with appropriate id. If id is "" or f is nil,
// AddFrame will not add it to tag.
//
// If you want to add attached picture, comment or unsynchronised lyrics/text
// transcription frames, better use AddAttachedPicture, AddCommentFrame
// or AddUnsynchronisedLyricsFrame methods respectively.
func (tag *Tag) AddFrame(id string, f Framer) {
	if id == "" || f == nil {
		return
	}

	if mustFrameBeInSequence(id) {
		if tag.sequences[id] == nil {
			tag.sequences[id] = getSequence()
		}
		tag.sequences[id].AddFrame(f)
	} else {
		tag.frames[id] = f
	}
}

// AddAttachedPicture adds a picture frame to tag.
func (tag *Tag) AddAttachedPicture(pf PictureFrame) {
	id := tag.CommonID("Attached picture")
	tag.AddFrame(id, pf)
}

// AddCommentFrame adds a comment frame to tag.
func (tag *Tag) AddCommentFrame(cf CommentFrame) {
	id := tag.CommonID("Comments")
	tag.AddFrame(id, cf)
}

// AddUnsynchronisedLyricsFrame adds an unsynchronised lyrics/text frame
// to tag.
func (tag *Tag) AddUnsynchronisedLyricsFrame(uslf UnsynchronisedLyricsFrame) {
	id := tag.CommonID("Unsynchronised lyrics/text transcription")
	tag.AddFrame(id, uslf)
}

// CommonID returns ID3v2.3 or ID3v2.4 (in appropriate to version of Tag) frame ID
// from given description.
// For example, CommonID("Language") will return "TLAN".
// If it can't find the ID with given description, it returns the description.
//
// All descriptions you can find in file common_ids.go
// or in id3 documentation (for fourth version: http://id3.org/id3v2.4.0-frames;
// for third version: http://id3.org/id3v2.3.0#Declared_ID3v2_frames).
func (tag *Tag) CommonID(description string) string {
	var ids map[string]string
	if tag.version == 3 {
		ids = V23CommonIDs
	} else {
		ids = V24CommonIDs
	}
	if id, ok := ids[description]; ok {
		return id
	}
	return description
}

// AllFrames returns map, that contains all frames in tag, that could be parsed.
// The key of this map is an ID of frame and value is an array of frames.
func (tag *Tag) AllFrames() map[string][]Framer {
	frames := make(map[string][]Framer)

	for id, f := range tag.frames {
		frames[id] = []Framer{f}
	}
	for id, sequence := range tag.sequences {
		frames[id] = sequence.Frames()
	}

	return frames
}

// DeleteAllFrames deletes all frames in tag.
func (tag *Tag) DeleteAllFrames() {
	if tag.frames == nil || len(tag.frames) > 0 {
		tag.frames = make(map[string]Framer)
	}
	if tag.sequences == nil || len(tag.sequences) > 0 {
		for _, s := range tag.sequences {
			putSequence(s)
		}
		tag.sequences = make(map[string]*sequence)
	}
}

// DeleteFrames deletes frames in tag with given id.
func (tag *Tag) DeleteFrames(id string) {
	delete(tag.frames, id)
	if s, ok := tag.sequences[id]; ok {
		putSequence(s)
		delete(tag.sequences, id)
	}
}

// Reset deletes all frames in tag and parses rd considering opts.
func (tag *Tag) Reset(rd io.Reader, opts Options) error {
	tag.DeleteAllFrames()
	return tag.parse(rd, opts)
}

// GetFrames returns frames with corresponding id.
// It returns nil if there is no frames with given id.
func (tag *Tag) GetFrames(id string) []Framer {
	if f, exists := tag.frames[id]; exists {
		return []Framer{f}
	} else if s, exists := tag.sequences[id]; exists {
		return s.Frames()
	}
	return nil
}

// GetLastFrame returns last frame from slice, that is returned from GetFrames function.
// GetLastFrame is suitable for frames, that can be only one in whole tag.
// For example, for text frames.
func (tag *Tag) GetLastFrame(id string) Framer {
	// Avoid an allocation of slice in GetFrames,
	// if there is anyway one frame.
	if f, exists := tag.frames[id]; exists {
		return f
	}

	fs := tag.GetFrames(id)
	if len(fs) == 0 {
		return nil
	}
	return fs[len(fs)-1]
}

// GetTextFrame returns text frame with corresponding id.
func (tag *Tag) GetTextFrame(id string) TextFrame {
	f := tag.GetLastFrame(id)
	if f == nil {
		return TextFrame{}
	}
	tf := f.(TextFrame)
	return tf
}

// Count returns the number of frames in tag.
func (tag *Tag) Count() int {
	n := len(tag.frames)
	for _, s := range tag.sequences {
		n += s.Count()
	}
	return n
}

// HasFrames checks if there is at least one frame in tag.
// It's much faster than tag.Count() > 0.
func (tag *Tag) HasFrames() bool {
	return len(tag.frames) > 0 || len(tag.sequences) > 0
}

func (tag *Tag) Title() string {
	f := tag.GetTextFrame(tag.CommonID("Title/Songname/Content description"))
	return f.Text
}

func (tag *Tag) SetTitle(title string) {
	tag.AddFrame(tag.CommonID("Title/Songname/Content description"), TextFrame{Encoding: tag.defaultEncoding, Text: title})
}

func (tag *Tag) Artist() string {
	f := tag.GetTextFrame(tag.CommonID("Lead artist/Lead performer/Soloist/Performing group"))
	return f.Text
}

func (tag *Tag) SetArtist(artist string) {
	tag.AddFrame(tag.CommonID("Lead artist/Lead performer/Soloist/Performing group"), TextFrame{Encoding: tag.defaultEncoding, Text: artist})
}

func (tag *Tag) Album() string {
	f := tag.GetTextFrame(tag.CommonID("Album/Movie/Show title"))
	return f.Text
}

func (tag *Tag) SetAlbum(album string) {
	tag.AddFrame(tag.CommonID("Album/Movie/Show title"), TextFrame{Encoding: tag.defaultEncoding, Text: album})
}

func (tag *Tag) Year() string {
	f := tag.GetTextFrame(tag.CommonID("Year"))
	return f.Text
}

func (tag *Tag) SetYear(year string) {
	tag.AddFrame(tag.CommonID("Year"), TextFrame{Encoding: tag.defaultEncoding, Text: year})
}

func (tag *Tag) Genre() string {
	f := tag.GetTextFrame(tag.CommonID("Content type"))
	return f.Text
}

func (tag *Tag) SetGenre(genre string) {
	tag.AddFrame(tag.CommonID("Content type"), TextFrame{Encoding: tag.defaultEncoding, Text: genre})
}

// iterateOverAllFrames iterates over every single frame in tag and calls
// f for them. It consumps no memory at all, unlike the tag.AllFrames().
// It returns error only if f returns error.
func (tag *Tag) iterateOverAllFrames(f func(id string, frame Framer) error) error {
	for id, frame := range tag.frames {
		if err := f(id, frame); err != nil {
			return err
		}
	}
	for id, sequence := range tag.sequences {
		for _, frame := range sequence.Frames() {
			if err := f(id, frame); err != nil {
				return err
			}
		}
	}
	return nil
}

// Size returns the size of all frames in bytes.
func (tag *Tag) Size() int {
	if !tag.HasFrames() {
		return 0
	}

	var n int
	n += tagHeaderSize // Add the size of tag header
	tag.iterateOverAllFrames(func(id string, f Framer) error {
		n += frameHeaderSize + f.Size() // Add the whole frame size
		return nil
	})

	return n
}

// Version returns current ID3v2 version of tag.
func (tag *Tag) Version() byte {
	return tag.version
}

// SetVersion sets given ID3v2 version to tag.
// If version is less than 3 or greater than 4, then this method will do nothing.
// If tag has some frames, which are deprecated or changed in given version,
// then to your notice you can delete, change or just stay them.
func (tag *Tag) SetVersion(version byte) {
	if version < 3 || version > 4 {
		return
	}
	tag.version = version
}

// Save writes tag to the file, if tag was opened with a file.
// If there are no frames in tag, Save will write
// only music part without any ID3v2 information.
// If tag was initiliazed not with file, it returns ErrNoFile.
func (tag *Tag) Save() error {
	file, ok := tag.reader.(*os.File)
	if !ok {
		return ErrNoFile
	}

	// Get original file mode.
	originalFile := file
	originalStat, err := originalFile.Stat()
	if err != nil {
		return err
	}

	// Create a temp file for mp3 file, which will contain new tag.
	name := file.Name() + "-id3v2"
	newFile, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, originalStat.Mode())
	if err != nil {
		return err
	}

	// Make sure we clean up the temp file if it's still around.
	defer os.Remove(newFile.Name())

	// Write tag in new file.
	tagSize, err := tag.WriteTo(newFile)
	if err != nil {
		return err
	}

	// Seek to a music part of original file.
	if _, err = originalFile.Seek(tag.originalSize, os.SEEK_SET); err != nil {
		return err
	}

	// Write to new file the music part.
	buf := getByteSlice(32 * 1024)
	defer putByteSlice(buf)
	if _, err = io.CopyBuffer(newFile, originalFile, buf); err != nil {
		return err
	}

	// Close files to allow replacing.
	newFile.Close()
	originalFile.Close()

	// Replace original file with new file.
	if err = os.Rename(newFile.Name(), originalFile.Name()); err != nil {
		return err
	}

	// Set tag.reader to new file with original name.
	tag.reader, err = os.Open(originalFile.Name())
	if err != nil {
		return err
	}

	// Set tag.originalSize to new frames size.
	if tagSize > tagHeaderSize {
		tag.originalSize = tagSize - tagHeaderSize
	} else {
		tag.originalSize = 0
	}

	return nil
}

// WriteTo writes whole tag in w if there is at least one frame.
// It returns the number of bytes written and error during the write.
// It returns nil as error if the write was successful.
func (tag *Tag) WriteTo(w io.Writer) (n int64, err error) {
	if w == nil {
		return 0, errors.New("w is nil")
	}

	// Count size of frames.
	framesSize := tag.Size() - tagHeaderSize
	if framesSize <= 0 {
		return 0, nil
	}

	// Write tag header.
	bw := getBufioWriter(w)
	defer putBufioWriter(bw)
	if err := writeTagHeader(bw, uint(framesSize), tag.version); err != nil {
		return 0, err
	}
	n += tagHeaderSize

	// Write frames.
	err = tag.iterateOverAllFrames(func(id string, f Framer) error {
		nn, err := writeFrame(bw, id, f)
		n += nn
		return err
	})
	if err != nil {
		return n, err
	}

	return n, bw.Flush()
}

func writeFrame(bw *bufio.Writer, id string, frame Framer) (int64, error) {
	if err := writeFrameHeader(bw, id, uint(frame.Size())); err != nil {
		return 0, err
	}

	frameSize, err := frame.WriteTo(bw)
	return frameHeaderSize + frameSize, err
}

func writeFrameHeader(bw *bufio.Writer, id string, frameSize uint) error {
	// ID
	if _, err := bw.WriteString(id); err != nil {
		return err
	}

	// Size
	if err := writeBytesSize(bw, frameSize); err != nil {
		return err
	}

	// Flags
	if _, err := bw.Write([]byte{0, 0}); err != nil {
		return err
	}
	return nil
}

// Close closes tag's file, if tag was opened with a file.
// If tag was initiliazed not with file, it returns ErrNoFile.
func (tag *Tag) Close() error {
	file, ok := tag.reader.(*os.File)
	if !ok {
		return ErrNoFile
	}
	return file.Close()
}
<commit_msg>Shorten frame names by calling CommonID<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package id3v2

import (
	"bufio"
	"errors"
	"io"
	"os"
)

var ErrNoFile = errors.New("tag was not initialized with file")

// Tag stores all information about opened tag.
type Tag struct {
	frames    map[string]Framer
	sequences map[string]*sequence

	defaultEncoding Encoding
	reader          io.Reader
	originalSize    int64
	version         byte
}

// AddFrame adds f to tag with appropriate id. If id is "" or f is nil,
// AddFrame will not add it to tag.
//
// If you want to add attached picture, comment or unsynchronised lyrics/text
// transcription frames, better use AddAttachedPicture, AddCommentFrame
// or AddUnsynchronisedLyricsFrame methods respectively.
func (tag *Tag) AddFrame(id string, f Framer) {
	if id == "" || f == nil {
		return
	}

	if mustFrameBeInSequence(id) {
		if tag.sequences[id] == nil {
			tag.sequences[id] = getSequence()
		}
		tag.sequences[id].AddFrame(f)
	} else {
		tag.frames[id] = f
	}
}

// AddAttachedPicture adds a picture frame to tag.
func (tag *Tag) AddAttachedPicture(pf PictureFrame) {
	id := tag.CommonID("Attached picture")
	tag.AddFrame(id, pf)
}

// AddCommentFrame adds a comment frame to tag.
func (tag *Tag) AddCommentFrame(cf CommentFrame) {
	id := tag.CommonID("Comments")
	tag.AddFrame(id, cf)
}

// AddUnsynchronisedLyricsFrame adds an unsynchronised lyrics/text frame
// to tag.
func (tag *Tag) AddUnsynchronisedLyricsFrame(uslf UnsynchronisedLyricsFrame) {
	id := tag.CommonID("Unsynchronised lyrics/text transcription")
	tag.AddFrame(id, uslf)
}

// CommonID returns ID3v2.3 or ID3v2.4 (in appropriate to version of Tag) frame ID
// from given description.
// For example, CommonID("Language") will return "TLAN".
// If it can't find the ID with given description, it returns the description.
//
// All descriptions you can find in file common_ids.go
// or in id3 documentation (for fourth version: http://id3.org/id3v2.4.0-frames;
// for third version: http://id3.org/id3v2.3.0#Declared_ID3v2_frames).
func (tag *Tag) CommonID(description string) string {
	var ids map[string]string
	if tag.version == 3 {
		ids = V23CommonIDs
	} else {
		ids = V24CommonIDs
	}
	if id, ok := ids[description]; ok {
		return id
	}
	return description
}

// AllFrames returns map, that contains all frames in tag, that could be parsed.
// The key of this map is an ID of frame and value is an array of frames.
func (tag *Tag) AllFrames() map[string][]Framer {
	frames := make(map[string][]Framer)

	for id, f := range tag.frames {
		frames[id] = []Framer{f}
	}
	for id, sequence := range tag.sequences {
		frames[id] = sequence.Frames()
	}

	return frames
}

// DeleteAllFrames deletes all frames in tag.
func (tag *Tag) DeleteAllFrames() {
	if tag.frames == nil || len(tag.frames) > 0 {
		tag.frames = make(map[string]Framer)
	}
	if tag.sequences == nil || len(tag.sequences) > 0 {
		for _, s := range tag.sequences {
			putSequence(s)
		}
		tag.sequences = make(map[string]*sequence)
	}
}

// DeleteFrames deletes frames in tag with given id.
func (tag *Tag) DeleteFrames(id string) {
	delete(tag.frames, id)
	if s, ok := tag.sequences[id]; ok {
		putSequence(s)
		delete(tag.sequences, id)
	}
}

// Reset deletes all frames in tag and parses rd considering opts.
func (tag *Tag) Reset(rd io.Reader, opts Options) error {
	tag.DeleteAllFrames()
	return tag.parse(rd, opts)
}

// GetFrames returns frames with corresponding id.
// It returns nil if there is no frames with given id.
func (tag *Tag) GetFrames(id string) []Framer {
	if f, exists := tag.frames[id]; exists {
		return []Framer{f}
	} else if s, exists := tag.sequences[id]; exists {
		return s.Frames()
	}
	return nil
}

// GetLastFrame returns last frame from slice, that is returned from GetFrames function.
// GetLastFrame is suitable for frames, that can be only one in whole tag.
// For example, for text frames.
func (tag *Tag) GetLastFrame(id string) Framer {
	// Avoid an allocation of slice in GetFrames,
	// if there is anyway one frame.
	if f, exists := tag.frames[id]; exists {
		return f
	}

	fs := tag.GetFrames(id)
	if len(fs) == 0 {
		return nil
	}
	return fs[len(fs)-1]
}

// GetTextFrame returns text frame with corresponding id.
func (tag *Tag) GetTextFrame(id string) TextFrame {
	f := tag.GetLastFrame(id)
	if f == nil {
		return TextFrame{}
	}
	tf := f.(TextFrame)
	return tf
}

// Count returns the number of frames in tag.
func (tag *Tag) Count() int {
	n := len(tag.frames)
	for _, s := range tag.sequences {
		n += s.Count()
	}
	return n
}

// HasFrames checks if there is at least one frame in tag.
// It's much faster than tag.Count() > 0.
func (tag *Tag) HasFrames() bool {
	return len(tag.frames) > 0 || len(tag.sequences) > 0
}

func (tag *Tag) Title() string {
	f := tag.GetTextFrame(tag.CommonID("Title"))
	return f.Text
}

func (tag *Tag) SetTitle(title string) {
	tag.AddFrame(tag.CommonID("Title"), TextFrame{Encoding: tag.defaultEncoding, Text: title})
}

func (tag *Tag) Artist() string {
	f := tag.GetTextFrame(tag.CommonID("Artist"))
	return f.Text
}

func (tag *Tag) SetArtist(artist string) {
	tag.AddFrame(tag.CommonID("Artist"), TextFrame{Encoding: tag.defaultEncoding, Text: artist})
}

func (tag *Tag) Album() string {
	f := tag.GetTextFrame(tag.CommonID("Album/Movie/Show title"))
	return f.Text
}

func (tag *Tag) SetAlbum(album string) {
	tag.AddFrame(tag.CommonID("Album/Movie/Show title"), TextFrame{Encoding: tag.defaultEncoding, Text: album})
}

func (tag *Tag) Year() string {
	f := tag.GetTextFrame(tag.CommonID("Year"))
	return f.Text
}

func (tag *Tag) SetYear(year string) {
	tag.AddFrame(tag.CommonID("Year"), TextFrame{Encoding: tag.defaultEncoding, Text: year})
}

func (tag *Tag) Genre() string {
	f := tag.GetTextFrame(tag.CommonID("Content type"))
	return f.Text
}

func (tag *Tag) SetGenre(genre string) {
	tag.AddFrame(tag.CommonID("Content type"), TextFrame{Encoding: tag.defaultEncoding, Text: genre})
}

// iterateOverAllFrames iterates over every single frame in tag and calls
// f for them. It consumps no memory at all, unlike the tag.AllFrames().
// It returns error only if f returns error.
func (tag *Tag) iterateOverAllFrames(f func(id string, frame Framer) error) error {
	for id, frame := range tag.frames {
		if err := f(id, frame); err != nil {
			return err
		}
	}
	for id, sequence := range tag.sequences {
		for _, frame := range sequence.Frames() {
			if err := f(id, frame); err != nil {
				return err
			}
		}
	}
	return nil
}

// Size returns the size of all frames in bytes.
func (tag *Tag) Size() int {
	if !tag.HasFrames() {
		return 0
	}

	var n int
	n += tagHeaderSize // Add the size of tag header
	tag.iterateOverAllFrames(func(id string, f Framer) error {
		n += frameHeaderSize + f.Size() // Add the whole frame size
		return nil
	})

	return n
}

// Version returns current ID3v2 version of tag.
func (tag *Tag) Version() byte {
	return tag.version
}

// SetVersion sets given ID3v2 version to tag.
// If version is less than 3 or greater than 4, then this method will do nothing.
// If tag has some frames, which are deprecated or changed in given version,
// then to your notice you can delete, change or just stay them.
func (tag *Tag) SetVersion(version byte) {
	if version < 3 || version > 4 {
		return
	}
	tag.version = version
}

// Save writes tag to the file, if tag was opened with a file.
// If there are no frames in tag, Save will write
// only music part without any ID3v2 information.
// If tag was initiliazed not with file, it returns ErrNoFile.
func (tag *Tag) Save() error {
	file, ok := tag.reader.(*os.File)
	if !ok {
		return ErrNoFile
	}

	// Get original file mode.
	originalFile := file
	originalStat, err := originalFile.Stat()
	if err != nil {
		return err
	}

	// Create a temp file for mp3 file, which will contain new tag.
	name := file.Name() + "-id3v2"
	newFile, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, originalStat.Mode())
	if err != nil {
		return err
	}

	// Make sure we clean up the temp file if it's still around.
	defer os.Remove(newFile.Name())

	// Write tag in new file.
	tagSize, err := tag.WriteTo(newFile)
	if err != nil {
		return err
	}

	// Seek to a music part of original file.
	if _, err = originalFile.Seek(tag.originalSize, os.SEEK_SET); err != nil {
		return err
	}

	// Write to new file the music part.
	buf := getByteSlice(32 * 1024)
	defer putByteSlice(buf)
	if _, err = io.CopyBuffer(newFile, originalFile, buf); err != nil {
		return err
	}

	// Close files to allow replacing.
	newFile.Close()
	originalFile.Close()

	// Replace original file with new file.
	if err = os.Rename(newFile.Name(), originalFile.Name()); err != nil {
		return err
	}

	// Set tag.reader to new file with original name.
	tag.reader, err = os.Open(originalFile.Name())
	if err != nil {
		return err
	}

	// Set tag.originalSize to new frames size.
	if tagSize > tagHeaderSize {
		tag.originalSize = tagSize - tagHeaderSize
	} else {
		tag.originalSize = 0
	}

	return nil
}

// WriteTo writes whole tag in w if there is at least one frame.
// It returns the number of bytes written and error during the write.
// It returns nil as error if the write was successful.
func (tag *Tag) WriteTo(w io.Writer) (n int64, err error) {
	if w == nil {
		return 0, errors.New("w is nil")
	}

	// Count size of frames.
	framesSize := tag.Size() - tagHeaderSize
	if framesSize <= 0 {
		return 0, nil
	}

	// Write tag header.
	bw := getBufioWriter(w)
	defer putBufioWriter(bw)
	if err := writeTagHeader(bw, uint(framesSize), tag.version); err != nil {
		return 0, err
	}
	n += tagHeaderSize

	// Write frames.
	err = tag.iterateOverAllFrames(func(id string, f Framer) error {
		nn, err := writeFrame(bw, id, f)
		n += nn
		return err
	})
	if err != nil {
		return n, err
	}

	return n, bw.Flush()
}

func writeFrame(bw *bufio.Writer, id string, frame Framer) (int64, error) {
	if err := writeFrameHeader(bw, id, uint(frame.Size())); err != nil {
		return 0, err
	}

	frameSize, err := frame.WriteTo(bw)
	return frameHeaderSize + frameSize, err
}

func writeFrameHeader(bw *bufio.Writer, id string, frameSize uint) error {
	// ID
	if _, err := bw.WriteString(id); err != nil {
		return err
	}

	// Size
	if err := writeBytesSize(bw, frameSize); err != nil {
		return err
	}

	// Flags
	if _, err := bw.Write([]byte{0, 0}); err != nil {
		return err
	}
	return nil
}

// Close closes tag's file, if tag was opened with a file.
// If tag was initiliazed not with file, it returns ErrNoFile.
func (tag *Tag) Close() error {
	file, ok := tag.reader.(*os.File)
	if !ok {
		return ErrNoFile
	}
	return file.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"fmt"
	"io"
	"log"
	"math/big"
	"net"
	"net/http"
	"os"
	"runtime"
	"sync"
)

// Intercept TLS (HTTPS) connections.

var certFile = flag.String("tls-cert", "", "path to certificate for serving HTTPS")
var keyFile = flag.String("tls-key", "", "path to TLS certificate key")
var sslBypassFile = newActiveFlag("tls-bypass", "", "path to list of sites that bypass SSLBump", readBypassFile)
var tlsVerbose = flag.Bool("tls-verbose", false, "log all intercepted HTTPS connections")

var tlsCert tls.Certificate
var parsedTLSCert *x509.Certificate
var tlsReady bool

var tlsBypass = map[string]bool{}
var tlsBypassRanges []*net.IPNet

// unverifiedClientConfig is a TLS configuration that doesn't verify server
// certificates.
var unverifiedClientConfig = &tls.Config{
	InsecureSkipVerify: true,
}

// loadCertificate loads the TLS certificate specified by certFile and keyFile
// into tlsCert.
func loadCertificate() {
	if *certFile != "" && *keyFile != "" {
		cert, err := tls.LoadX509KeyPair(*certFile, *keyFile)
		if err != nil {
			log.Println("Error loading TLS certificate:", err)
			return
		}
		tlsCert = cert
		parsed, err := x509.ParseCertificate(cert.Certificate[0])
		if err != nil {
			log.Println("Error parsing X509 certificate:", err)
			return
		}
		parsedTLSCert = parsed
		tlsReady = true

		go cacheCertificates()
	}
}

func shouldBypass(host string) bool {
	if tlsBypass[host] {
		return true
	}
	addr := net.ParseIP(host)
	if addr == nil {
		return false
	}
	for _, subnet := range tlsBypassRanges {
		if subnet.Contains(addr) {
			return true
		}
	}
	return false
}

// SSLBump performs a man-in-the-middle attack on conn, to filter the HTTPS
// traffic. serverAddr is the address (host:port) of the server the client was
// trying to connect to.
func SSLBump(conn net.Conn, serverAddr string) {
	defer func() {
		if err := recover(); err != nil {
			buf := make([]byte, 4096)
			buf = buf[:runtime.Stack(buf, false)]
			log.Printf("SSLBump: panic serving connection to %s: %v\n%s", serverAddr, err, buf)
			conn.Close()
		}
	}()

	if host, _, err := net.SplitHostPort(serverAddr); err == nil && shouldBypass(host) {
		// Just connect directly instead of bumping this connection.
		serverConn, err := net.Dial("tcp", serverAddr)
		if err != nil {
			log.Printf("error with pass-through of SSL connection to %s: %s", serverAddr, err)
			return
		}

		go func() {
			io.Copy(conn, serverConn)
			conn.Close()
		}()
		io.Copy(serverConn, conn)
		serverConn.Close()
		return
	}

	if *tlsVerbose {
		log.Printf("intercepting TLS connection from %s to %s", conn.RemoteAddr(), serverAddr)
	}

	cert, err := getCertificate(serverAddr)
	if err != nil {
		// Since it doesn't seem to be an HTTPS server, just connect directly.
		log.Printf("Could not generate a TLS certificate for %s (%s); letting the client connect directly", serverAddr, err)
		serverConn, err := net.Dial("tcp", serverAddr)
		if err != nil {
			log.Printf("Could not connect to %v: %v", serverAddr, err)
			return
		}

		go func() {
			io.Copy(conn, serverConn)
			conn.Close()
		}()
		io.Copy(serverConn, conn)
		serverConn.Close()
		return
	}

	config := &tls.Config{
		NextProtos:   []string{"http/1.1"},
		Certificates: []tls.Certificate{cert, tlsCert},
	}

	tlsConn := tls.Server(conn, config)
	err = tlsConn.Handshake()
	if err != nil {
		log.Printf("Error in TLS handshake for SSLBump connection from %v to %v: %v", conn.RemoteAddr(), serverAddr, err)
		return
	}

	_, port, err := net.SplitHostPort(serverAddr)
	if err != nil {
		port = ""
	}
	listener := &singleListener{conn: tlsConn}
	server := http.Server{
		Handler: proxyHandler{
			TLS:         true,
			connectPort: port,
		},
	}
	server.Serve(listener)
}

// A singleListener is a net.Listener that returns a single connection, then
// gives the error io.EOF.
type singleListener struct {
	conn net.Conn
	once sync.Once
}

func (s *singleListener) Accept() (net.Conn, error) {
	var c net.Conn
	s.once.Do(func() {
		c = s.conn
	})
	if c != nil {
		return c, nil
	}
	return nil, io.EOF
}

func (s *singleListener) Close() error {
	s.once.Do(func() {
		s.conn.Close()
	})
	return nil
}

func (s *singleListener) Addr() net.Addr {
	return s.conn.LocalAddr()
}

// maxSerial is the largest serial number to use for a certificate.
var maxSerial = big.NewInt(1<<63 - 1)

// generateCertificate connects to the server at addr, gets its TLS
// certificate, and returns a new certificate to be used when proxying
// connections to that server.
func generateCertificate(addr string) (tls.Certificate, error) {
	conn, err := tls.Dial("tcp", addr, unverifiedClientConfig)
	if err != nil {
		return tls.Certificate{}, err
	}
	defer conn.Close()
	state := conn.ConnectionState()
	serverCert := state.PeerCertificates[0]

	// Avoid duplicate serial numbers (NSS error -8054 in Chrome).
	serverCert.SerialNumber, err = rand.Int(rand.Reader, maxSerial)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("failed to generate serial number: %s", err)
	}

	priv, err := rsa.GenerateKey(rand.Reader, 1024)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("failed to generate private key: %s", err)
	}

	// Connect again, but this time check the server's certificate for validity.
	conn2, err := tls.Dial("tcp", addr, nil)
	if err == nil {
		conn2.Close()
	}
	signingCert := parsedTLSCert
	if _, ok := err.(x509.UnknownAuthorityError); ok {
		// There was a certificate error, so generate a self-signed certificate.
		signingCert = serverCert
	}

	newCertBytes, err := x509.CreateCertificate(rand.Reader, serverCert, signingCert, &priv.PublicKey, tlsCert.PrivateKey)
	if err != nil {
		return tls.Certificate{}, err
	}

	certBuf := new(bytes.Buffer)
	pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: newCertBytes})
	keyBuf := new(bytes.Buffer)
	pem.Encode(keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})

	return tls.X509KeyPair(certBuf.Bytes(), keyBuf.Bytes())
}

func readBypassFile(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return fmt.Errorf("could not open %s: %s", filename, err)
	}
	defer f.Close()
	cr := newConfigReader(f)

	for {
		line, err := cr.ReadLine()
		if err != nil {
			break
		}

		if _, subnet, err := net.ParseCIDR(line); err == nil {
			// It's a network range, like 192.168.0.1/24.
			tlsBypassRanges = append(tlsBypassRanges, subnet)
			continue
		}

		tlsBypass[line] = true
	}

	return nil
}
<commit_msg>Only make one extra connection to the origin server when we need to generate an HTTPS certificate (instead of two).<commit_after>package main

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"encoding/pem"
	"flag"
	"fmt"
	"io"
	"log"
	"math/big"
	"net"
	"net/http"
	"os"
	"runtime"
	"sync"
)

// Intercept TLS (HTTPS) connections.

var certFile = flag.String("tls-cert", "", "path to certificate for serving HTTPS")
var keyFile = flag.String("tls-key", "", "path to TLS certificate key")
var sslBypassFile = newActiveFlag("tls-bypass", "", "path to list of sites that bypass SSLBump", readBypassFile)
var tlsVerbose = flag.Bool("tls-verbose", false, "log all intercepted HTTPS connections")

var tlsCert tls.Certificate
var parsedTLSCert *x509.Certificate
var tlsReady bool

var tlsBypass = map[string]bool{}
var tlsBypassRanges []*net.IPNet

// unverifiedClientConfig is a TLS configuration that doesn't verify server
// certificates.
var unverifiedClientConfig = &tls.Config{
	InsecureSkipVerify: true,
}

// loadCertificate loads the TLS certificate specified by certFile and keyFile
// into tlsCert.
func loadCertificate() {
	if *certFile != "" && *keyFile != "" {
		cert, err := tls.LoadX509KeyPair(*certFile, *keyFile)
		if err != nil {
			log.Println("Error loading TLS certificate:", err)
			return
		}
		tlsCert = cert
		parsed, err := x509.ParseCertificate(cert.Certificate[0])
		if err != nil {
			log.Println("Error parsing X509 certificate:", err)
			return
		}
		parsedTLSCert = parsed
		tlsReady = true

		go cacheCertificates()
	}
}

func shouldBypass(host string) bool {
	if tlsBypass[host] {
		return true
	}
	addr := net.ParseIP(host)
	if addr == nil {
		return false
	}
	for _, subnet := range tlsBypassRanges {
		if subnet.Contains(addr) {
			return true
		}
	}
	return false
}

// SSLBump performs a man-in-the-middle attack on conn, to filter the HTTPS
// traffic. serverAddr is the address (host:port) of the server the client was
// trying to connect to.
func SSLBump(conn net.Conn, serverAddr string) {
	defer func() {
		if err := recover(); err != nil {
			buf := make([]byte, 4096)
			buf = buf[:runtime.Stack(buf, false)]
			log.Printf("SSLBump: panic serving connection to %s: %v\n%s", serverAddr, err, buf)
			conn.Close()
		}
	}()

	if host, _, err := net.SplitHostPort(serverAddr); err == nil && shouldBypass(host) {
		// Just connect directly instead of bumping this connection.
		serverConn, err := net.Dial("tcp", serverAddr)
		if err != nil {
			log.Printf("error with pass-through of SSL connection to %s: %s", serverAddr, err)
			return
		}

		go func() {
			io.Copy(conn, serverConn)
			conn.Close()
		}()
		io.Copy(serverConn, conn)
		serverConn.Close()
		return
	}

	if *tlsVerbose {
		log.Printf("intercepting TLS connection from %s to %s", conn.RemoteAddr(), serverAddr)
	}

	cert, err := getCertificate(serverAddr)
	if err != nil {
		// Since it doesn't seem to be an HTTPS server, just connect directly.
		log.Printf("Could not generate a TLS certificate for %s (%s); letting the client connect directly", serverAddr, err)
		serverConn, err := net.Dial("tcp", serverAddr)
		if err != nil {
			log.Printf("Could not connect to %v: %v", serverAddr, err)
			return
		}

		go func() {
			io.Copy(conn, serverConn)
			conn.Close()
		}()
		io.Copy(serverConn, conn)
		serverConn.Close()
		return
	}

	config := &tls.Config{
		NextProtos:   []string{"http/1.1"},
		Certificates: []tls.Certificate{cert, tlsCert},
	}

	tlsConn := tls.Server(conn, config)
	err = tlsConn.Handshake()
	if err != nil {
		log.Printf("Error in TLS handshake for SSLBump connection from %v to %v: %v", conn.RemoteAddr(), serverAddr, err)
		return
	}

	_, port, err := net.SplitHostPort(serverAddr)
	if err != nil {
		port = ""
	}
	listener := &singleListener{conn: tlsConn}
	server := http.Server{
		Handler: proxyHandler{
			TLS:         true,
			connectPort: port,
		},
	}
	server.Serve(listener)
}

// A singleListener is a net.Listener that returns a single connection, then
// gives the error io.EOF.
type singleListener struct {
	conn net.Conn
	once sync.Once
}

func (s *singleListener) Accept() (net.Conn, error) {
	var c net.Conn
	s.once.Do(func() {
		c = s.conn
	})
	if c != nil {
		return c, nil
	}
	return nil, io.EOF
}

func (s *singleListener) Close() error {
	s.once.Do(func() {
		s.conn.Close()
	})
	return nil
}

func (s *singleListener) Addr() net.Addr {
	return s.conn.LocalAddr()
}

// maxSerial is the largest serial number to use for a certificate.
var maxSerial = big.NewInt(1<<63 - 1)

// generateCertificate connects to the server at addr, gets its TLS
// certificate, and returns a new certificate to be used when proxying
// connections to that server.
func generateCertificate(addr string) (tls.Certificate, error) {
	conn, err := tls.Dial("tcp", addr, unverifiedClientConfig)
	if err != nil {
		return tls.Certificate{}, err
	}
	defer conn.Close()
	state := conn.ConnectionState()
	serverCert := state.PeerCertificates[0]

	// Avoid duplicate serial numbers (NSS error -8054 in Chrome).
	serverCert.SerialNumber, err = rand.Int(rand.Reader, maxSerial)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("failed to generate serial number: %s", err)
	}

	priv, err := rsa.GenerateKey(rand.Reader, 1024)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("failed to generate private key: %s", err)
	}

	intermediates := x509.NewCertPool()
	for _, cert := range state.PeerCertificates[1:] {
		intermediates.AddCert(cert)
	}
	_, err = serverCert.Verify(x509.VerifyOptions{Intermediates: intermediates})
	signingCert := parsedTLSCert
	if _, ok := err.(x509.UnknownAuthorityError); ok {
		// There was a certificate error, so generate a self-signed certificate.
		signingCert = serverCert
	}

	newCertBytes, err := x509.CreateCertificate(rand.Reader, serverCert, signingCert, &priv.PublicKey, tlsCert.PrivateKey)
	if err != nil {
		return tls.Certificate{}, err
	}

	certBuf := new(bytes.Buffer)
	pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: newCertBytes})
	keyBuf := new(bytes.Buffer)
	pem.Encode(keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})

	return tls.X509KeyPair(certBuf.Bytes(), keyBuf.Bytes())
}

func readBypassFile(filename string) error {
	f, err := os.Open(filename)
	if err != nil {
		return fmt.Errorf("could not open %s: %s", filename, err)
	}
	defer f.Close()
	cr := newConfigReader(f)

	for {
		line, err := cr.ReadLine()
		if err != nil {
			break
		}

		if _, subnet, err := net.ParseCIDR(line); err == nil {
			// It's a network range, like 192.168.0.1/24.
			tlsBypassRanges = append(tlsBypassRanges, subnet)
			continue
		}

		tlsBypass[line] = true
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import tui "github.com/gizak/termui"

type TreeItem struct {
	node    *DepsNode
	parent  *TreeItem
	sibling *TreeItem
	child   *TreeItem // pointer to first child
	folded  bool
	total   int // number of (shown) children (not count itself)
}

type TreeView struct {
	tui.Block // embedded
	Root      *TreeItem
	Curr      *TreeItem

	ItemFgColor  tui.Attribute
	ItemBgColor  tui.Attribute
	FocusFgColor tui.Attribute
	FocusBgColor tui.Attribute

	idx int // current cursor position
	off int // first entry displayed

	rows int
	cols int
}

func NewTreeView() *TreeView {
	tv := &TreeView{Block: *tui.NewBlock()}

	tv.ItemFgColor = tui.ThemeAttr("list.item.fg")
	tv.ItemBgColor = tui.ThemeAttr("list.item.bg")
	tv.FocusFgColor = tui.ColorYellow
	tv.FocusBgColor = tui.ColorBlue

	tv.idx = 0
	tv.off = 0
	return tv
}

func (ti *TreeItem) next() *TreeItem {
	if ti.child == nil || ti.folded {
		for ti != nil {
			if ti.sibling != nil {
				return ti.sibling
			}

			ti = ti.parent
		}
		return nil
	}
	return ti.child
}

func (ti *TreeItem) expand() {
	if !ti.folded || ti.child == nil {
		return
	}

	for c := ti.child; c != nil; c = c.sibling {
		ti.total += c.total + 1
	}

	for p := ti.parent; p != nil; p = p.parent {
		p.total += ti.total
	}

	ti.folded = false
}

func (ti *TreeItem) fold() {
	if ti.folded || ti.child == nil {
		return
	}

	for p := ti.parent; p != nil; p = p.parent {
		p.total -= ti.total
	}
	ti.total = 0

	ti.folded = true
}

func (ti *TreeItem) toggle() {
	if ti.folded {
		ti.expand()
	} else {
		ti.fold()
	}
}

// Buffer implements Bufferer interface.
func (tv *TreeView) Buffer() tui.Buffer {
	buf := tv.Block.Buffer()

	i := 0
	printed := 0

	var ti *TreeItem
	for ti = tv.Root; ti != nil; ti = ti.next() {
		if i < tv.off {
			i++
			continue
		}
		if printed == tv.rows {
			break
		}

		fg := tv.ItemFgColor
		bg := tv.ItemBgColor
		if i == tv.idx {
			fg = tv.FocusFgColor
			bg = tv.FocusBgColor

			tv.Curr = ti
		}

		indent := 3 * ti.node.depth
		cs := tui.DefaultTxBuilder.Build(ti.node.name, fg, bg)
		cs = tui.DTrimTxCls(cs, tv.cols-2-indent)

		j := 0
		if i == tv.idx {
			// draw current line cursor from the beginning
			for j < indent {
				buf.Set(j+1, printed+1, tui.Cell{' ', fg, bg})
				j++
			}
		} else {
			j = indent
		}

		if ti.folded {
			buf.Set(j+1, printed+1, tui.Cell{'+', fg, bg})
		} else {
			buf.Set(j+1, printed+1, tui.Cell{'-', fg, bg})
		}
		buf.Set(j+2, printed+1, tui.Cell{' ', fg, bg})
		j += 2

		for _, vv := range cs {
			w := vv.Width()
			buf.Set(j+1, printed+1, vv)
			j += w
		}

		printed++
		i++

		if i != tv.idx+1 {
			continue
		}

		// draw current line cursor to the end
		for j < tv.cols {
			buf.Set(j+1, printed, tui.Cell{' ', fg, bg})
			j++
		}
	}
	return buf
}

func (tv *TreeView) Down() {
	if tv.idx < tv.Root.total {
		tv.idx++
	}
	if tv.idx-tv.off >= tv.rows {
		tv.off++
	}
}

func (tv *TreeView) Up() {
	if tv.idx > 0 {
		tv.idx--
	}
	if tv.idx < tv.off {
		tv.off = tv.idx
	}
}

func (tv *TreeView) PageDown() {
	bottom := tv.off + tv.rows - 1
	if bottom > tv.Root.total {
		bottom = tv.Root.total
	}

	// At first, move to the bottom of current page
	if tv.idx != bottom {
		tv.idx = bottom
		return
	}

	tv.idx += tv.rows
	if tv.idx > tv.Root.total {
		tv.idx = tv.Root.total
	}
	if tv.idx-tv.off >= tv.rows {
		tv.off = tv.idx - tv.rows + 1
	}
}

func (tv *TreeView) PageUp() {
	// At first, move to the top of current page
	if tv.idx != tv.off {
		tv.idx = tv.off
		return
	}

	tv.idx -= tv.rows
	if tv.idx < 0 {
		tv.idx = 0
	}

	tv.off = tv.idx
}

func (tv *TreeView) Home() {
	tv.idx = 0
	tv.off = 0
}

func (tv *TreeView) End() {
	tv.idx = tv.Root.total
	tv.off = tv.idx - tv.rows + 1

	if tv.off < 0 {
		tv.off = 0
	}
}

func (tv *TreeView) Toggle() {
	tv.Curr.toggle()
}

func makeItems(dep *DepsNode, parent *TreeItem) *TreeItem {
	item := &TreeItem{node: dep, parent: parent, folded: false, total: len(dep.child)}

	var prev *TreeItem
	for _, v := range dep.child {
		c := makeItems(v, item)

		if item.child == nil {
			item.child = c
		}
		if prev != nil {
			prev.sibling = c
		}
		prev = c

		item.total += c.total
	}
	return item
}

func ShowWithTUI(dep *DepsNode) {
	if err := tui.Init(); err != nil {
		panic(err)
	}
	defer tui.Close()

	root := makeItems(dep, nil)

	tv := NewTreeView()

	tv.BorderLabel = "ELF Tree"
	tv.Height = tui.TermHeight()
	tv.Width = tui.TermWidth()
	tv.Root = root
	tv.Curr = root

	tv.rows = tv.Height - 2 // exclude border at top and bottom
	tv.cols = tv.Width - 2  // exclude border at left and right

	tui.Render(tv)

	// handle key pressing
	tui.Handle("/sys/kbd/q", func(tui.Event) {
		// press q to quit
		tui.StopLoop()
	})
	tui.Handle("/sys/kbd/C-c", func(tui.Event) {
		// press Ctrl-C to quit
		tui.StopLoop()
	})

	tui.Handle("/sys/kbd/<down>", func(tui.Event) {
		tv.Down()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<up>", func(tui.Event) {
		tv.Up()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<next>", func(tui.Event) {
		tv.PageDown()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<previous>", func(tui.Event) {
		tv.PageUp()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<home>", func(tui.Event) {
		tv.Home()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<end>", func(tui.Event) {
		tv.End()
		tui.Render(tv)
	})

	tui.Handle("/sys/kbd/<enter>", func(tui.Event) {
		tv.Toggle()
		tui.Render(tv)
	})

	tui.Handle("/sys/wnd/resize", func(tui.Event) {
		tv.Height = tui.TermHeight()
		tv.Width = tui.TermWidth()
		tv.rows = tv.Height - 2
		tv.cols = tv.Width - 2
		tui.Render(tv)
	})

	tui.Loop()
}
<commit_msg>Add a status line at the bottom<commit_after>package main

import tui "github.com/gizak/termui"

type TreeItem struct {
	node    *DepsNode
	parent  *TreeItem
	sibling *TreeItem
	child   *TreeItem // pointer to first child
	folded  bool
	total   int // number of (shown) children (not count itself)
}

type TreeView struct {
	tui.Block // embedded
	Root      *TreeItem
	Curr      *TreeItem

	ItemFgColor  tui.Attribute
	ItemBgColor  tui.Attribute
	FocusFgColor tui.Attribute
	FocusBgColor tui.Attribute

	idx int // current cursor position
	off int // first entry displayed

	rows int
	cols int
}

func NewTreeView() *TreeView {
	tv := &TreeView{Block: *tui.NewBlock()}

	tv.ItemFgColor = tui.ThemeAttr("list.item.fg")
	tv.ItemBgColor = tui.ThemeAttr("list.item.bg")
	tv.FocusFgColor = tui.ColorYellow
	tv.FocusBgColor = tui.ColorBlue

	tv.idx = 0
	tv.off = 0
	return tv
}

func (ti *TreeItem) next() *TreeItem {
	if ti.child == nil || ti.folded {
		for ti != nil {
			if ti.sibling != nil {
				return ti.sibling
			}

			ti = ti.parent
		}
		return nil
	}
	return ti.child
}

func (ti *TreeItem) expand() {
	if !ti.folded || ti.child == nil {
		return
	}

	for c := ti.child; c != nil; c = c.sibling {
		ti.total += c.total + 1
	}

	for p := ti.parent; p != nil; p = p.parent {
		p.total += ti.total
	}

	ti.folded = false
}

func (ti *TreeItem) fold() {
	if ti.folded || ti.child == nil {
		return
	}

	for p := ti.parent; p != nil; p = p.parent {
		p.total -= ti.total
	}
	ti.total = 0

	ti.folded = true
}

func (ti *TreeItem) toggle() {
	if ti.folded {
		ti.expand()
	} else {
		ti.fold()
	}
}

// Buffer implements Bufferer interface.
func (tv *TreeView) Buffer() tui.Buffer {
	buf := tv.Block.Buffer()

	i := 0
	printed := 0

	var ti *TreeItem
	for ti = tv.Root; ti != nil; ti = ti.next() {
		if i < tv.off {
			i++
			continue
		}
		if printed == tv.rows {
			break
		}

		fg := tv.ItemFgColor
		bg := tv.ItemBgColor
		if i == tv.idx {
			fg = tv.FocusFgColor
			bg = tv.FocusBgColor

			tv.Curr = ti
		}

		indent := 3 * ti.node.depth
		cs := tui.DefaultTxBuilder.Build(ti.node.name, fg, bg)
		cs = tui.DTrimTxCls(cs, tv.cols-2-indent)

		j := 0
		if i == tv.idx {
			// draw current line cursor from the beginning
			for j < indent {
				buf.Set(j+1, printed+1, tui.Cell{' ', fg, bg})
				j++
			}
		} else {
			j = indent
		}

		if ti.folded {
			buf.Set(j+1, printed+1, tui.Cell{'+', fg, bg})
		} else {
			buf.Set(j+1, printed+1, tui.Cell{'-', fg, bg})
		}
		buf.Set(j+2, printed+1, tui.Cell{' ', fg, bg})
		j += 2

		for _, vv := range cs {
			w := vv.Width()
			buf.Set(j+1, printed+1, vv)
			j += w
		}

		printed++
		i++

		if i != tv.idx+1 {
			continue
		}

		// draw current line cursor to the end
		for j < tv.cols {
			buf.Set(j+1, printed, tui.Cell{' ', fg, bg})
			j++
		}
	}

	// the last line is a status line
	var line string

	if tv.Curr != nil {
		line = tv.Curr.node.name

		n := tv.Curr.node.parent
		for n != nil {
			line = n.name + " > " + line

			n = n.parent
		}
	} else {
		line = "ELF tree"
	}

	fg := tui.ColorBlack
	bg := tui.ColorWhite

	cs := tui.DefaultTxBuilder.Build(line, fg, bg)
	cs = tui.DTrimTxCls(cs, tv.cols-2)

	buf.Set(1, printed+1, tui.Cell{' ', fg, bg})
	buf.Set(2, printed+1, tui.Cell{' ', fg, bg})

	j := 2
	for _, vv := range cs {
		w := vv.Width()
		buf.Set(j+1, tv.rows+1, vv)
		j += w
	}

	// draw status line to the end
	for j < tv.cols {
		buf.Set(j+1, tv.rows+1, tui.Cell{' ', fg, bg})
		j++
	}

	return buf
}

func (tv *TreeView) Down() {
	if tv.idx < tv.Root.total {
		tv.idx++
	}
	if tv.idx-tv.off >= tv.rows {
		tv.off++
	}
}

func (tv *TreeView) Up() {
	if tv.idx > 0 {
		tv.idx--
	}
	if tv.idx < tv.off {
		tv.off = tv.idx
	}
}

func (tv *TreeView) PageDown() {
	bottom := tv.off + tv.rows - 1
	if bottom > tv.Root.total {
		bottom = tv.Root.total
	}

	// At first, move to the bottom of current page
	if tv.idx != bottom {
		tv.idx = bottom
		return
	}

	tv.idx += tv.rows
	if tv.idx > tv.Root.total {
		tv.idx = tv.Root.total
	}
	if tv.idx-tv.off >= tv.rows {
		tv.off = tv.idx - tv.rows + 1
	}
}

func (tv *TreeView) PageUp() {
	// At first, move to the top of current page
	if tv.idx != tv.off {
		tv.idx = tv.off
		return
	}

	tv.idx -= tv.rows
	if tv.idx < 0 {
		tv.idx = 0
	}

	tv.off = tv.idx
}

func (tv *TreeView) Home() {
	tv.idx = 0
	tv.off = 0
}

func (tv *TreeView) End() {
	tv.idx = tv.Root.total
	tv.off = tv.idx - tv.rows + 1

	if tv.off < 0 {
		tv.off = 0
	}
}

func (tv *TreeView) Toggle() {
	tv.Curr.toggle()
}

func makeItems(dep *DepsNode, parent *TreeItem) *TreeItem {
	item := &TreeItem{node: dep, parent: parent, folded: false, total: len(dep.child)}

	var prev *TreeItem
	for _, v := range dep.child {
		c := makeItems(v, item)

		if item.child == nil {
			item.child = c
		}
		if prev != nil {
			prev.sibling = c
		}
		prev = c

		item.total += c.total
	}
	return item
}

func ShowWithTUI(dep *DepsNode) {
	if err := tui.Init(); err != nil {
		panic(err)
	}
	defer tui.Close()

	root := makeItems(dep, nil)

	tv := NewTreeView()

	tv.BorderLabel = "ELF Tree"
	tv.Height = tui.TermHeight()
	tv.Width = tui.TermWidth()
	tv.Root = root
	tv.Curr = root

	tv.rows = tv.Height - 3 // exclude border at top and bottom
	tv.cols = tv.Width - 2  // exclude border at left and right

	tui.Render(tv)

	// handle key pressing
	tui.Handle("/sys/kbd/q", func(tui.Event) {
		// press q to quit
		tui.StopLoop()
	})
	tui.Handle("/sys/kbd/C-c", func(tui.Event) {
		// press Ctrl-C to quit
		tui.StopLoop()
	})

	tui.Handle("/sys/kbd/<down>", func(tui.Event) {
		tv.Down()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<up>", func(tui.Event) {
		tv.Up()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<next>", func(tui.Event) {
		tv.PageDown()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<previous>", func(tui.Event) {
		tv.PageUp()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<home>", func(tui.Event) {
		tv.Home()
		tui.Render(tv)
	})
	tui.Handle("/sys/kbd/<end>", func(tui.Event) {
		tv.End()
		tui.Render(tv)
	})

	tui.Handle("/sys/kbd/<enter>", func(tui.Event) {
		tv.Toggle()
		tui.Render(tv)
	})

	tui.Handle("/sys/wnd/resize", func(tui.Event) {
		tv.Height = tui.TermHeight()
		tv.Width = tui.TermWidth()
		tv.rows = tv.Height - 3
		tv.cols = tv.Width - 2
		tui.Render(tv)
	})

	tui.Loop()
}
<|endoftext|>