<commit_before>// Package edit implements a command line editor.
package edit

import (
	"fmt"
	"os"
	"syscall"

	"github.com/elves/elvish/errutil"
	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/logutil"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
	"github.com/elves/elvish/sys"
)

var Logger = logutil.Discard

const (
	lackEOLRune = '\u23ce'
	lackEOL     = "\033[7m" + string(lackEOLRune) + "\033[m"
)

// Editor keeps the status of the line editor.
type Editor struct {
	file   *os.File
	writer *writer
	reader *Reader
	sigs   chan os.Signal
	store  *store.Store
	evaler *eval.Evaler
	cmdSeq int
	editorState
}

type editorState struct {
	// States used during ReadLine. Reset at the beginning of ReadLine.
	active                bool
	savedTermios          *sys.Termios
	tokens                []Token
	prompt, rprompt, line string
	dot                   int
	notifications         []string
	tips                  []string
	mode                  bufferMode
	completion            *completion
	completionLines       int
	navigation            *navigation
	history               historyState
	isExternal            map[string]bool
	// Used for builtins.
	lastKey    Key
	nextAction action
}

type bufferMode int

const (
	modeInsert bufferMode = iota
	modeCommand
	modeCompletion
	modeNavigation
	modeHistory
)

type actionType int

const (
	noAction actionType = iota
	reprocessKey
	exitReadLine
)

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	EOF  bool
	Err  error
}

// NewEditor creates an Editor.
func NewEditor(file *os.File, sigs chan os.Signal, ev *eval.Evaler, st *store.Store) *Editor {
	seq := -1
	if st != nil {
		var err error
		seq, err = st.NextCmdSeq()
		if err != nil {
			// TODO(xiaq): Also report the error
			seq = -1
		}
	}

	ed := &Editor{
		file:   file,
		writer: newWriter(file),
		reader: NewReader(file),
		sigs:   sigs,
		store:  st,
		evaler: ev,
		cmdSeq: seq,
	}
	ev.AddModule("le", makeModule(ed))
	return ed
}

func (ed *Editor) flash() {
	// TODO implement fish-like flash effect
}

func (ed *Editor) addTip(format string, args ...interface{}) {
	ed.tips = append(ed.tips, fmt.Sprintf(format, args))
}

func (ed *Editor) notify(format string, args ...interface{}) {
	ed.notifications = append(ed.notifications, fmt.Sprintf(format, args...))
}

func (ed *Editor) refresh(fullRefresh bool) error {
	// Re-lex the line, unless we are in modeCompletion
	name := "[interacitve]"
	src := ed.line
	if ed.mode != modeCompletion {
		n, _ /*err*/ := parse.Parse(src)
		if n == nil {
			ed.tokens = []Token{{ParserError, src, nil, ""}}
		} else {
			ed.tokens = tokenize(src, n)
			_, err := ed.evaler.Compile(name, src, n)
			if err != nil {
				if err, ok := err.(*errutil.ContextualError); ok {
					ed.addTip("compiler error highlighted")
					p := err.Pos()
					for i, token := range ed.tokens {
						if token.Node.Begin() <= p && p < token.Node.End() {
							ed.tokens[i].MoreStyle += styleForCompilerError
							break
						}
					}
				}
			}
		}
		for i, t := range ed.tokens {
			for _, stylist := range stylists {
				ed.tokens[i].MoreStyle += stylist(t.Node, ed)
			}
		}
	}
	return ed.writer.refresh(&ed.editorState, fullRefresh)
}

// acceptCompletion accepts currently selected completion candidate.
func (ed *Editor) acceptCompletion() {
	c := ed.completion
	if 0 <= c.current && c.current < len(c.candidates) {
		accepted := c.candidates[c.current].source.text
		ed.insertAtDot(accepted)
	}
	ed.completion = nil
	ed.mode = modeInsert
}

// insertAtDot inserts text at the dot and moves the dot after it.
func (ed *Editor) insertAtDot(text string) {
	ed.line = ed.line[:ed.dot] + text + ed.line[ed.dot:]
	ed.dot += len(text)
}

func setupTerminal(file *os.File) (*sys.Termios, error) {
	fd := int(file.Fd())
	term, err := sys.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't get terminal attribute: %s", err)
	}

	savedTermios := term.Copy()

	term.SetICanon(false)
	term.SetEcho(false)
	term.SetVMin(1)
	term.SetVTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't set up terminal attribute: %s", err)
	}

	/*
		err = sys.FlushInput(fd)
		if err != nil {
			return nil, fmt.Errorf("can't flush input: %s", err)
		}
	*/

	return savedTermios, nil
}

// startReadLine prepares the terminal for the editor.
func (ed *Editor) startReadLine() error {
	savedTermios, err := setupTerminal(ed.file)
	if err != nil {
		return err
	}
	ed.savedTermios = savedTermios

	_, width := sys.GetWinsize(int(ed.file.Fd()))
	// Turn on autowrap, write lackEOL along with enough padding to fill the
	// whole screen. If the cursor was in the first column, we end up in the
	// same line (just off the line boundary); otherwise we are now in the next
	// line. We now rewind to the first column and erase anything there. The
	// final effect is that a lackEOL gets written if and only if the cursor
	// was not in the first column.
	//
	// After that, we turn off autowrap. The editor has its own wrapping
	// mechanism.
	fmt.Fprintf(ed.file, "\033[?7h%s%*s\r \r"+
		// Turn off autowrap
		"\033[?7l"+
		// Turn on SGR-style mouse tracking
		"\033[?1000;1006h",
		lackEOL, width-WcWidth(lackEOLRune), "")

	return nil
}

// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(addError func(error)) {
	ed.mode = modeInsert
	ed.tips = nil
	ed.completion = nil
	ed.navigation = nil
	ed.dot = len(ed.line)
	// TODO Perhaps make it optional to NOT clear the rprompt
	ed.rprompt = ""
	addError(ed.refresh(false))
	ed.file.WriteString("\n")

	// ed.reader.Stop()
	ed.reader.Quit()

	// turn on autowrap and turn off mouse tracking
	ed.file.WriteString("\033[?7h\033[?1000;1006l")

	// restore termios
	err := ed.savedTermios.ApplyToFd(int(ed.file.Fd()))

	if err != nil {
		addError(fmt.Errorf("can't restore terminal attribute: %s", err))
	}
	ed.savedTermios = nil
	ed.editorState = editorState{}
}

// ReadLine reads a line interactively.
func (ed *Editor) ReadLine(prompt, rprompt func() string) (lr LineRead) {
	ed.editorState = editorState{active: true}
	isExternalCh := make(chan map[string]bool, 1)
	go getIsExternal(ed.evaler, isExternalCh)

	ed.writer.resetOldBuf()
	go ed.reader.Run()

	err := ed.startReadLine()
	if err != nil {
		return LineRead{Err: err}
	}
	defer ed.finishReadLine(func(err error) {
		if err != nil {
			lr.Err = errutil.CatError(lr.Err, err)
		}
	})

MainLoop:
	for {
		ed.prompt = prompt()
		ed.rprompt = rprompt()

		err := ed.refresh(false)
		if err != nil {
			return LineRead{Err: err}
		}

		ed.tips = nil

		select {
		case m := <-isExternalCh:
			ed.isExternal = m
		case sig := <-ed.sigs:
			// TODO(xiaq): Maybe support customizable handling of signals
			switch sig {
			case syscall.SIGINT:
				// Start over
				ed.editorState = editorState{
					savedTermios: ed.savedTermios,
					isExternal:   ed.isExternal,
				}
				goto MainLoop
			case syscall.SIGWINCH:
				continue MainLoop
			case syscall.SIGCHLD:
				// ignore
			default:
				ed.addTip("ignored signal %s", sig)
			}
		case err := <-ed.reader.ErrorChan():
			ed.notify("reader error: %s", err.Error())
		case mouse := <-ed.reader.MouseChan():
			ed.addTip("mouse: %#v", mouse)
		case <-ed.reader.CPRChan():
			// Ignore CPR
		case k := <-ed.reader.KeyChan():
		lookupKey:
			keyBinding, ok := keyBindings[ed.mode]
			if !ok {
				ed.addTip("No binding for current mode")
				continue
			}

			fn, bound := keyBinding[k]
			if !bound {
				fn = keyBinding[Default]
			}

			ed.lastKey = k
			fn.Call(ed)
			act := ed.nextAction
			ed.nextAction = action{}

			switch act.actionType {
			case noAction:
				continue
			case reprocessKey:
				err = ed.refresh(false)
				if err != nil {
					return LineRead{Err: err}
				}
				goto lookupKey
			case exitReadLine:
				lr = act.returnValue
				if lr.EOF == false && lr.Err == nil && lr.Line != "" {
					ed.appendHistory(lr.Line)
				}

				return lr
			}
		}
	}
}
<commit_msg>edit: fix addTip.<commit_after>// Package edit implements a command line editor.
package edit

import (
	"fmt"
	"os"
	"syscall"

	"github.com/elves/elvish/errutil"
	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/logutil"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
	"github.com/elves/elvish/sys"
)

var Logger = logutil.Discard

const (
	lackEOLRune = '\u23ce'
	lackEOL     = "\033[7m" + string(lackEOLRune) + "\033[m"
)

// Editor keeps the status of the line editor.
type Editor struct {
	file   *os.File
	writer *writer
	reader *Reader
	sigs   chan os.Signal
	store  *store.Store
	evaler *eval.Evaler
	cmdSeq int
	editorState
}

type editorState struct {
	// States used during ReadLine. Reset at the beginning of ReadLine.
	active                bool
	savedTermios          *sys.Termios
	tokens                []Token
	prompt, rprompt, line string
	dot                   int
	notifications         []string
	tips                  []string
	mode                  bufferMode
	completion            *completion
	completionLines       int
	navigation            *navigation
	history               historyState
	isExternal            map[string]bool
	// Used for builtins.
	lastKey    Key
	nextAction action
}

type bufferMode int

const (
	modeInsert bufferMode = iota
	modeCommand
	modeCompletion
	modeNavigation
	modeHistory
)

type actionType int

const (
	noAction actionType = iota
	reprocessKey
	exitReadLine
)

// LineRead is the result of ReadLine. Exactly one member is non-zero, making
// it effectively a tagged union.
type LineRead struct {
	Line string
	EOF  bool
	Err  error
}

// NewEditor creates an Editor.
func NewEditor(file *os.File, sigs chan os.Signal, ev *eval.Evaler, st *store.Store) *Editor {
	seq := -1
	if st != nil {
		var err error
		seq, err = st.NextCmdSeq()
		if err != nil {
			// TODO(xiaq): Also report the error
			seq = -1
		}
	}

	ed := &Editor{
		file:   file,
		writer: newWriter(file),
		reader: NewReader(file),
		sigs:   sigs,
		store:  st,
		evaler: ev,
		cmdSeq: seq,
	}
	ev.AddModule("le", makeModule(ed))
	return ed
}

func (ed *Editor) flash() {
	// TODO implement fish-like flash effect
}

func (ed *Editor) addTip(format string, args ...interface{}) {
	ed.tips = append(ed.tips, fmt.Sprintf(format, args...))
}

func (ed *Editor) notify(format string, args ...interface{}) {
	ed.notifications = append(ed.notifications, fmt.Sprintf(format, args...))
}

func (ed *Editor) refresh(fullRefresh bool) error {
	// Re-lex the line, unless we are in modeCompletion
	name := "[interacitve]"
	src := ed.line
	if ed.mode != modeCompletion {
		n, _ /*err*/ := parse.Parse(src)
		if n == nil {
			ed.tokens = []Token{{ParserError, src, nil, ""}}
		} else {
			ed.tokens = tokenize(src, n)
			_, err := ed.evaler.Compile(name, src, n)
			if err != nil {
				if err, ok := err.(*errutil.ContextualError); ok {
					ed.addTip("compiler error highlighted")
					p := err.Pos()
					for i, token := range ed.tokens {
						if token.Node.Begin() <= p && p < token.Node.End() {
							ed.tokens[i].MoreStyle += styleForCompilerError
							break
						}
					}
				}
			}
		}
		for i, t := range ed.tokens {
			for _, stylist := range stylists {
				ed.tokens[i].MoreStyle += stylist(t.Node, ed)
			}
		}
	}
	return ed.writer.refresh(&ed.editorState, fullRefresh)
}

// acceptCompletion accepts currently selected completion candidate.
func (ed *Editor) acceptCompletion() {
	c := ed.completion
	if 0 <= c.current && c.current < len(c.candidates) {
		accepted := c.candidates[c.current].source.text
		ed.insertAtDot(accepted)
	}
	ed.completion = nil
	ed.mode = modeInsert
}

// insertAtDot inserts text at the dot and moves the dot after it.
func (ed *Editor) insertAtDot(text string) {
	ed.line = ed.line[:ed.dot] + text + ed.line[ed.dot:]
	ed.dot += len(text)
}

func setupTerminal(file *os.File) (*sys.Termios, error) {
	fd := int(file.Fd())
	term, err := sys.NewTermiosFromFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't get terminal attribute: %s", err)
	}

	savedTermios := term.Copy()

	term.SetICanon(false)
	term.SetEcho(false)
	term.SetVMin(1)
	term.SetVTime(0)

	err = term.ApplyToFd(fd)
	if err != nil {
		return nil, fmt.Errorf("can't set up terminal attribute: %s", err)
	}

	/*
		err = sys.FlushInput(fd)
		if err != nil {
			return nil, fmt.Errorf("can't flush input: %s", err)
		}
	*/

	return savedTermios, nil
}

// startReadLine prepares the terminal for the editor.
func (ed *Editor) startReadLine() error {
	savedTermios, err := setupTerminal(ed.file)
	if err != nil {
		return err
	}
	ed.savedTermios = savedTermios

	_, width := sys.GetWinsize(int(ed.file.Fd()))
	// Turn on autowrap, write lackEOL along with enough padding to fill the
	// whole screen. If the cursor was in the first column, we end up in the
	// same line (just off the line boundary); otherwise we are now in the next
	// line. We now rewind to the first column and erase anything there. The
	// final effect is that a lackEOL gets written if and only if the cursor
	// was not in the first column.
	//
	// After that, we turn off autowrap. The editor has its own wrapping
	// mechanism.
	fmt.Fprintf(ed.file, "\033[?7h%s%*s\r \r"+
		// Turn off autowrap
		"\033[?7l"+
		// Turn on SGR-style mouse tracking
		"\033[?1000;1006h",
		lackEOL, width-WcWidth(lackEOLRune), "")

	return nil
}

// finishReadLine puts the terminal in a state suitable for other programs to
// use.
func (ed *Editor) finishReadLine(addError func(error)) {
	ed.mode = modeInsert
	ed.tips = nil
	ed.completion = nil
	ed.navigation = nil
	ed.dot = len(ed.line)
	// TODO Perhaps make it optional to NOT clear the rprompt
	ed.rprompt = ""
	addError(ed.refresh(false))
	ed.file.WriteString("\n")

	// ed.reader.Stop()
	ed.reader.Quit()

	// turn on autowrap and turn off mouse tracking
	ed.file.WriteString("\033[?7h\033[?1000;1006l")

	// restore termios
	err := ed.savedTermios.ApplyToFd(int(ed.file.Fd()))

	if err != nil {
		addError(fmt.Errorf("can't restore terminal attribute: %s", err))
	}
	ed.savedTermios = nil
	ed.editorState = editorState{}
}

// ReadLine reads a line interactively.
func (ed *Editor) ReadLine(prompt, rprompt func() string) (lr LineRead) {
	ed.editorState = editorState{active: true}
	isExternalCh := make(chan map[string]bool, 1)
	go getIsExternal(ed.evaler, isExternalCh)

	ed.writer.resetOldBuf()
	go ed.reader.Run()

	err := ed.startReadLine()
	if err != nil {
		return LineRead{Err: err}
	}
	defer ed.finishReadLine(func(err error) {
		if err != nil {
			lr.Err = errutil.CatError(lr.Err, err)
		}
	})

MainLoop:
	for {
		ed.prompt = prompt()
		ed.rprompt = rprompt()

		err := ed.refresh(false)
		if err != nil {
			return LineRead{Err: err}
		}

		ed.tips = nil

		select {
		case m := <-isExternalCh:
			ed.isExternal = m
		case sig := <-ed.sigs:
			// TODO(xiaq): Maybe support customizable handling of signals
			switch sig {
			case syscall.SIGINT:
				// Start over
				ed.editorState = editorState{
					savedTermios: ed.savedTermios,
					isExternal:   ed.isExternal,
				}
				goto MainLoop
			case syscall.SIGWINCH:
				continue MainLoop
			case syscall.SIGCHLD:
				// ignore
			default:
				ed.addTip("ignored signal %s", sig)
			}
		case err := <-ed.reader.ErrorChan():
			ed.notify("reader error: %s", err.Error())
		case mouse := <-ed.reader.MouseChan():
			ed.addTip("mouse: %#v", mouse)
		case <-ed.reader.CPRChan():
			// Ignore CPR
		case k := <-ed.reader.KeyChan():
		lookupKey:
			keyBinding, ok := keyBindings[ed.mode]
			if !ok {
				ed.addTip("No binding for current mode")
				continue
			}

			fn, bound := keyBinding[k]
			if !bound {
				fn = keyBinding[Default]
			}

			ed.lastKey = k
			fn.Call(ed)
			act := ed.nextAction
			ed.nextAction = action{}

			switch act.actionType {
			case noAction:
				continue
			case reprocessKey:
				err = ed.refresh(false)
				if err != nil {
					return LineRead{Err: err}
				}
				goto lookupKey
			case exitReadLine:
				lr = act.returnValue
				if lr.EOF == false && lr.Err == nil && lr.Line != "" {
					ed.appendHistory(lr.Line)
				}

				return lr
			}
		}
	}
}
<|endoftext|>


<commit_before>package bolt

import (
	"github.com/ansible-semaphore/semaphore/db"
	"go.etcd.io/bbolt"
	"time"
)

func (d *BoltDb) CreateTask(task db.Task) (newTask db.Task, err error) {
	task.Created = time.Now()
	res, err := d.createObject(0, db.TaskProps, task)
	if err != nil {
		return
	}
	newTask = res.(db.Task)
	return
}

func (d *BoltDb) UpdateTask(task db.Task) error {
	return d.updateObject(0, db.TaskProps, task)
}

func (d *BoltDb) CreateTaskOutput(output db.TaskOutput) (db.TaskOutput, error) {
	newOutput, err := d.createObject(output.TaskID, db.TaskOutputProps, output)
	if err != nil {
		return db.TaskOutput{}, err
	}
	return newOutput.(db.TaskOutput), nil
}

func (d *BoltDb) getTasks(projectID int, templateID *int, params db.RetrieveQueryParams) (tasksWithTpl []db.TaskWithTpl, err error) {
	var tasks []db.Task

	err = d.getObjects(0, db.TaskProps, params, func(tsk interface{}) bool {
		task := tsk.(db.Task)

		if task.ProjectID != projectID {
			return false
		}

		if templateID != nil && task.TemplateID != *templateID {
			return false
		}

		return true
	}, &tasks)

	if err != nil {
		return
	}

	var templates = make(map[int]db.Template)
	var users = make(map[int]db.User)

	tasksWithTpl = make([]db.TaskWithTpl, len(tasks))
	for i, task := range tasks {
		tpl, ok := templates[task.TemplateID]
		if !ok {
			if templateID == nil {
				tpl, _ = d.getRawTemplate(task.ProjectID, task.TemplateID)
			} else {
				tpl, _ = d.getRawTemplate(task.ProjectID, *templateID)
			}
			templates[task.TemplateID] = tpl
		}
		tasksWithTpl[i] = db.TaskWithTpl{Task: task}
		tasksWithTpl[i].TemplatePlaybook = tpl.Playbook
		tasksWithTpl[i].TemplateAlias = tpl.Name
		tasksWithTpl[i].TemplateType = tpl.Type
		if task.UserID != nil {
			usr, ok := users[*task.UserID]
			if !ok {
				usr, err = d.GetUser(*task.UserID)
				if err != nil {
					return
				}
				users[*task.UserID] = usr
			}
			tasksWithTpl[i].UserName = &usr.Name
		}

		err = tasksWithTpl[i].Fill(d)
		if err != nil {
			return
		}
	}

	return
}

func (d *BoltDb) GetTask(projectID int, taskID int) (task db.Task, err error) {
	err = d.getObject(0, db.TaskProps, intObjectID(taskID), &task)
	if err != nil {
		return
	}

	if task.ProjectID != projectID {
		task = db.Task{}
		err = db.ErrNotFound
		return
	}

	return
}

func (d *BoltDb) GetTemplateTasks(projectID int, templateID int, params db.RetrieveQueryParams) ([]db.TaskWithTpl, error) {
	return d.getTasks(projectID, &templateID, params)
}

func (d *BoltDb) GetProjectTasks(projectID int, params db.RetrieveQueryParams) ([]db.TaskWithTpl, error) {
	return d.getTasks(projectID, nil, params)
}

func (d *BoltDb) deleteTaskWithOutputs(projectID int, taskID int, tx *bbolt.Tx) (err error) {
	// check if task exists in the project
	_, err = d.GetTask(projectID, taskID)
	if err != nil {
		return
	}

	err = d.deleteObject(0, db.TaskProps, intObjectID(taskID), tx)
	if err != nil {
		return
	}

	return tx.DeleteBucket(makeBucketId(db.TaskOutputProps, taskID))
}

func (d *BoltDb) DeleteTaskWithOutputs(projectID int, taskID int) error {
	return d.db.Update(func(tx *bbolt.Tx) error {
		return d.deleteTaskWithOutputs(projectID, taskID, tx)
	})
}

func (d *BoltDb) GetTaskOutputs(projectID int, taskID int) (outputs []db.TaskOutput, err error) {
	// check if task exists in the project
	_, err = d.GetTask(projectID, taskID)

	if err != nil {
		return
	}

	err = d.getObjects(taskID, db.TaskOutputProps, db.RetrieveQueryParams{}, nil, &outputs)

	return
}
<commit_msg>fix(be): BoltDB issue with deleted user https://github.com/ansible-semaphore/semaphore/issues/1055<commit_after>package bolt

import (
	"github.com/ansible-semaphore/semaphore/db"
	"go.etcd.io/bbolt"
	"time"
)

func (d *BoltDb) CreateTask(task db.Task) (newTask db.Task, err error) {
	task.Created = time.Now()
	res, err := d.createObject(0, db.TaskProps, task)
	if err != nil {
		return
	}
	newTask = res.(db.Task)
	return
}

func (d *BoltDb) UpdateTask(task db.Task) error {
	return d.updateObject(0, db.TaskProps, task)
}

func (d *BoltDb) CreateTaskOutput(output db.TaskOutput) (db.TaskOutput, error) {
	newOutput, err := d.createObject(output.TaskID, db.TaskOutputProps, output)
	if err != nil {
		return db.TaskOutput{}, err
	}
	return newOutput.(db.TaskOutput), nil
}

func (d *BoltDb) getTasks(projectID int, templateID *int, params db.RetrieveQueryParams) (tasksWithTpl []db.TaskWithTpl, err error) {
	var tasks []db.Task

	err = d.getObjects(0, db.TaskProps, params, func(tsk interface{}) bool {
		task := tsk.(db.Task)

		if task.ProjectID != projectID {
			return false
		}

		if templateID != nil && task.TemplateID != *templateID {
			return false
		}

		return true
	}, &tasks)

	if err != nil {
		return
	}

	var templates = make(map[int]db.Template)
	var users = make(map[int]db.User)

	tasksWithTpl = make([]db.TaskWithTpl, len(tasks))
	for i, task := range tasks {
		tpl, ok := templates[task.TemplateID]
		if !ok {
			if templateID == nil {
				tpl, _ = d.getRawTemplate(task.ProjectID, task.TemplateID)
			} else {
				tpl, _ = d.getRawTemplate(task.ProjectID, *templateID)
			}
			templates[task.TemplateID] = tpl
		}
		tasksWithTpl[i] = db.TaskWithTpl{Task: task}
		tasksWithTpl[i].TemplatePlaybook = tpl.Playbook
		tasksWithTpl[i].TemplateAlias = tpl.Name
		tasksWithTpl[i].TemplateType = tpl.Type
		if task.UserID != nil {
			usr, ok := users[*task.UserID]
			if !ok {
				// trying to get user , but ignore error, because
				// user can be deleted, and it is ok
				usr, _ = d.GetUser(*task.UserID)
				users[*task.UserID] = usr
			}
			tasksWithTpl[i].UserName = &usr.Name
		}

		err = tasksWithTpl[i].Fill(d)
		if err != nil {
			return
		}
	}

	return
}

func (d *BoltDb) GetTask(projectID int, taskID int) (task db.Task, err error) {
	err = d.getObject(0, db.TaskProps, intObjectID(taskID), &task)
	if err != nil {
		return
	}

	if task.ProjectID != projectID {
		task = db.Task{}
		err = db.ErrNotFound
		return
	}

	return
}

func (d *BoltDb) GetTemplateTasks(projectID int, templateID int, params db.RetrieveQueryParams) ([]db.TaskWithTpl, error) {
	return d.getTasks(projectID, &templateID, params)
}

func (d *BoltDb) GetProjectTasks(projectID int, params db.RetrieveQueryParams) ([]db.TaskWithTpl, error) {
	return d.getTasks(projectID, nil, params)
}

func (d *BoltDb) deleteTaskWithOutputs(projectID int, taskID int, tx *bbolt.Tx) (err error) {
	// check if task exists in the project
	_, err = d.GetTask(projectID, taskID)
	if err != nil {
		return
	}

	err = d.deleteObject(0, db.TaskProps, intObjectID(taskID), tx)
	if err != nil {
		return
	}

	return tx.DeleteBucket(makeBucketId(db.TaskOutputProps, taskID))
}

func (d *BoltDb) DeleteTaskWithOutputs(projectID int, taskID int) error {
	return d.db.Update(func(tx *bbolt.Tx) error {
		return d.deleteTaskWithOutputs(projectID, taskID, tx)
	})
}

func (d *BoltDb) GetTaskOutputs(projectID int, taskID int) (outputs []db.TaskOutput, err error) {
	// check if task exists in the project
	_, err = d.GetTask(projectID, taskID)

	if err != nil {
		return
	}

	err = d.getObjects(taskID, db.TaskOutputProps, db.RetrieveQueryParams{}, nil, &outputs)

	return
}
<|endoftext|>


<commit_before>/**
 * @file flag.go
 * @author Mikhail Klementyev jollheef<AT>riseup.net
 * @license GNU AGPLv3
 * @date November, 2015
 * @brief test work with flag table
 */

package db

import (
	"errors"
	"fmt"
	"testing"
)

func TestCreateFlagTable(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	err = createFlagTable(db)
	if err != nil {
		panic(err)
	}
}

func TestAddFlag(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	flag := Flag{ID: 255, TeamID: 1, TaskID: 1, Flag: "test", Solved: false}

	err = AddFlag(db, &flag)
	if err != nil {
		panic(err)
	}

	if flag.ID != 1 {
		panic(errors.New("Flag id not correct"))
	}
}

// Test add flag with closed database
func TestFailAddFlag(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	db.Close()

	err = AddFlag(db, &Flag{})
	if err == nil {
		panic(err)
	}
}

func TestGetFlags(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	nflags := 150

	for i := 0; i < nflags; i++ {

		flag := Flag{ID: 255, TeamID: 1, TaskID: 1,
			Flag: fmt.Sprintf("%d", i), Solved: false}

		err = AddFlag(db, &flag)
		if err != nil {
			panic(err)
		}
	}

	flags, err := GetFlags(db)
	if err != nil {
		panic(err)
	}

	if len(flags) != nflags {
		panic(errors.New("Mismatch get flags length"))
	}

	for i := 0; i < nflags; i++ {

		if flags[i].Flag != fmt.Sprintf("%d", i) && flags[i].ID != i {
			panic(errors.New("Get invalid flag"))
		}
	}
}

// Test get flags with closed database
func TestFailGetFlags(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	db.Close()

	_, err = GetFlags(db)
	if err == nil {
		panic(err)
	}
}

func TestGetSolvedCount(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	nflags := 150
	teamID := 1
	taskID := 1

	for i := 0; i < nflags; i++ {

		flag := Flag{ID: 255, TeamID: teamID, TaskID: taskID,
			Flag: fmt.Sprintf("%d", i)}

		if i%2 == 0 {
			flag.Solved = true
		} else {
			flag.Solved = false
		}

		err = AddFlag(db, &flag)
		if err != nil {
			panic(err)
		}
	}

	solvedFlags, err := GetSolvedCount(db, taskID)
	if err != nil {
		panic(err)
	}

	if solvedFlags != nflags/2 {
		panic(errors.New("Mismatch solved flags length"))
	}
}

// Test get solved count with closed database
func TestFailGetSolvedCount(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	db.Close()

	_, err = GetSolvedCount(db, 0)
	if err == nil {
		panic(err)
	}
}

func TestIsSolved(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	teamID := 10
	taskID := 15

	flag := Flag{TeamID: teamID, TaskID: taskID, Solved: true}

	err = AddFlag(db, &flag)
	if err != nil {
		panic(err)
	}

	solved, err := IsSolved(db, teamID, taskID)
	if !solved {
		panic("Solved task unsolved")
	}

	if err != nil {
		panic(err)
	}
}

// Test is task solved with closed database
func TestFailIsSolved(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	db.Close()

	_, err = IsSolved(db, 0, 0)
	if err == nil {
		panic(err)
	}
}

// Test is task solved with unsolved task
func TestFailIsSolved2(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	solved, err := IsSolved(db, 0, 0)
	if err != nil {
		panic(err)
	}

	if solved {
		panic("Unsolved task solved")
	}
}
<commit_msg>Add test for GetSolvedBy<commit_after>/**
 * @file flag.go
 * @author Mikhail Klementyev jollheef<AT>riseup.net
 * @license GNU AGPLv3
 * @date November, 2015
 * @brief test work with flag table
 */

package db

import (
	"errors"
	"fmt"
	"testing"
)

func TestCreateFlagTable(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	err = createFlagTable(db)
	if err != nil {
		panic(err)
	}
}

func TestAddFlag(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	flag := Flag{ID: 255, TeamID: 1, TaskID: 1, Flag: "test", Solved: false}

	err = AddFlag(db, &flag)
	if err != nil {
		panic(err)
	}

	if flag.ID != 1 {
		panic(errors.New("Flag id not correct"))
	}
}

// Test add flag with closed database
func TestFailAddFlag(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	db.Close()

	err = AddFlag(db, &Flag{})
	if err == nil {
		panic(err)
	}
}

func TestGetFlags(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	nflags := 150

	for i := 0; i < nflags; i++ {

		flag := Flag{ID: 255, TeamID: 1, TaskID: 1,
			Flag: fmt.Sprintf("%d", i), Solved: false}

		err = AddFlag(db, &flag)
		if err != nil {
			panic(err)
		}
	}

	flags, err := GetFlags(db)
	if err != nil {
		panic(err)
	}

	if len(flags) != nflags {
		panic(errors.New("Mismatch get flags length"))
	}

	for i := 0; i < nflags; i++ {

		if flags[i].Flag != fmt.Sprintf("%d", i) && flags[i].ID != i {
			panic(errors.New("Get invalid flag"))
		}
	}
}

// Test get flags with closed database
func TestFailGetFlags(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	db.Close()

	_, err = GetFlags(db)
	if err == nil {
		panic(err)
	}
}

func TestGetSolvedCount(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	nflags := 150
	teamID := 1
	taskID := 1

	for i := 0; i < nflags; i++ {

		flag := Flag{ID: 255, TeamID: teamID, TaskID: taskID,
			Flag: fmt.Sprintf("%d", i)}

		if i%2 == 0 {
			flag.Solved = true
		} else {
			flag.Solved = false
		}

		err = AddFlag(db, &flag)
		if err != nil {
			panic(err)
		}
	}

	solvedFlags, err := GetSolvedCount(db, taskID)
	if err != nil {
		panic(err)
	}

	if solvedFlags != nflags/2 {
		panic(errors.New("Mismatch solved flags length"))
	}
}

// Test get solved count with closed database
func TestFailGetSolvedCount(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	db.Close()

	_, err = GetSolvedCount(db, 0)
	if err == nil {
		panic(err)
	}
}

func TestIsSolved(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	teamID := 10
	taskID := 15

	flag := Flag{TeamID: teamID, TaskID: taskID, Solved: true}

	err = AddFlag(db, &flag)
	if err != nil {
		panic(err)
	}

	solved, err := IsSolved(db, teamID, taskID)
	if !solved {
		panic("Solved task unsolved")
	}

	if err != nil {
		panic(err)
	}
}

// Test is task solved with closed database
func TestFailIsSolved(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	db.Close()

	_, err = IsSolved(db, 0, 0)
	if err == nil {
		panic(err)
	}
}

// Test is task solved with unsolved task
func TestFailIsSolved2(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	solved, err := IsSolved(db, 0, 0)
	if err != nil {
		panic(err)
	}

	if solved {
		panic("Unsolved task solved")
	}
}

func TestGetSolvedBy(*testing.T) {

	db, err := InitDatabase(dbPath)
	if err != nil {
		panic(err)
	}

	defer db.Close()

	teamID := 10
	taskID := 15

	flag := Flag{TeamID: teamID, TaskID: taskID, Solved: true}

	err = AddFlag(db, &flag)
	if err != nil {
		panic(err)
	}

	teamIDs, err := GetSolvedBy(db, taskID)
	if err != nil {
		panic(err)
	}

	if len(teamIDs) != 1 {
		panic("legnth of solved by mismatch")
	}

	if teamIDs[0] != teamID {
		panic("team id mismatch")
	}
}
<|endoftext|>


<commit_before>package database

import "database/sql"

const (
	sqlCreateTables string = `
CREATE TABLE config (
	identifier TEXT,
	value TEXT
);

CREATE TABLE clients (
	identifier TEXT,
	ip_address TEXT,
	last_ping  INTEGER
);

CREATE TABLE client_url_list (
    client_id INTEGER,
    url_list_id INTEGER
);
CREATE INDEX client_id_index ON client_url_list (client_id);

CREATE TABLE url_lists (
    id INTEGER PRIMARY KEY,
    name TEXT
);

CREATE TABLE urls (
	id INTEGER PRIMARY KEY,
	url TEXT
);

CREATE TABLE url_list_url (
	id INTEGER PRIMARY KEY,
	url_id INTEGER,
	url_list_id INTEGER
);

`

	sqlFindUrlId string = "SELECT id FROM urls WHERE url = ?;"
	sqlInsertUrl string = "INSERT INTO urls(url) VALUES(?);"
	sqlFetchUrls string = "SELECT url FROM urls;"
	sqlDeleteUrl string = "DELETE FROM urls WHERE url = ?;"

	sqlFindListId string = "SELECT id FROM url_lists WHERE name = ?;"
	sqlInsertList string = "INSERT INTO url_lists(name) VALUES(?);"
	sqlFetchLists string = "SELECT name FROM url_lists;"
	sqlDeleteList string = "DELETE FROM url_lists WHERE name = ?;"

	sqlInsertListUrl string = "INSERT INTO url_list_url(url_list_id, url_id) VALUES(?, ?);"
	sqlDeleteListUrl string = "DELETE FROM url_list_url WHERE id = ?;"
	sqlFetchListUrls string = `
	SELECT url FROM urls
	INNER JOIN url_list_url ON url_list_url.url_id = urls.id
	WHERE url_list_id = ?;
	`

	sqlInsertConfig string = "INSERT INTO config(identifier, value) VALUES(?, ?);"
)

type Database struct {
	Conn *sql.DB
}

func (db *Database) Close() (err error) {
	err = db.Conn.Close()
	return
}

func (db *Database) InsertUrl(url string) (err error) {
	_, err = db.Conn.Exec(sqlInsertUrl, url)
	return
}

func (db *Database) DeleteUrl(url string) (err error) {
	_, err = db.Conn.Exec(sqlDeleteUrl, url)
	return
}

func (db *Database) InsertList(name string) (err error) {
	_, err = db.Conn.Exec(sqlInsertList, name)
	return
}

func (db *Database) DeleteList(name string) (err error) {
	_, err = db.Conn.Exec(sqlDeleteList, name)
	return
}

func (db *Database) InsertConfig(identifier string, value string) (err error) {
	_, err = db.Conn.Exec(sqlInsertConfig, identifier, value)
	return
}

func (db *Database) FindListId(name string) (id int, err error) {
	row := db.Conn.QueryRow(sqlFindListId, name)

	err = row.Scan(&id)

	return
}

func (db *Database) FindUrlId(url string) (id int, err error) {
	row := db.Conn.QueryRow(sqlFindUrlId, url)

	err = row.Scan(&id)

	return
}

func (db *Database) FetchUrls() (urls []string, err error) {
	rows, err := db.Conn.Query(sqlFetchUrls)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		var url string

		err = rows.Scan(&url)
		if err != nil {
			return
		}

		urls = append(urls, url)
	}

	err = rows.Err()

	return
}

func (db *Database) FetchLists() (lists []string, err error) {
	rows, err := db.Conn.Query(sqlFetchLists)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		var name string

		err = rows.Scan(&name)
		if err != nil {
			return
		}

		lists = append(lists, name)
	}

	err = rows.Err()

	return
}

func (db *Database) FetchListUrls(name string) (urls []string, err error) {
	list_id, err := db.FindListId(name)
	if err != nil {
		return
	}

	rows, err := db.Conn.Query(sqlFetchListUrls, list_id)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		var url string

		err = rows.Scan(&url)
		if err != nil {
			return
		}

		urls = append(urls, url)
	}

	err = rows.Err()

	return
}

func (db *Database) AssignUrlToList(name string, url string) (err error) {
	list_id, err := db.FindListId(name)
	if err != nil {
		return
	}

	url_id, err := db.FindUrlId(url)
	if err != nil {
		return
	}

	_, err = db.Conn.Exec(sqlInsertListUrl, list_id, url_id)
	return

}

func (db *Database) CreateTables() (err error) {
	// Run SQL to create necessary schema
	_, err = db.Conn.Exec(sqlCreateTables)
	return
}

func Connect(database string) (db *Database, err error) {
	c, err := sql.Open("sqlite3", database)
	if err == nil {
		db = &Database{c}
	}

	return
}
<commit_msg>Add database functions for dealing with clients<commit_after>package database

import "database/sql"

const (
	// schema
	sqlCreateTables string = `
CREATE TABLE config (
	identifier TEXT NOT NULL,
	value TEXT
);

CREATE TABLE clients (
	identifier  TEXT NOT NULL,
	ip_address  TEXT NOT NULL,
	last_ping   TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
	url_list_id INTEGER,
);

CREATE TABLE url_lists (
    id INTEGER PRIMARY KEY,
    name TEXT
);

CREATE TABLE urls (
	id INTEGER PRIMARY KEY,
	url TEXT
);

CREATE TABLE url_list_url (
	id INTEGER PRIMARY KEY,
	url_id INTEGER,
	url_list_id INTEGER
);

`

	// clients table
	sqlInsertClient string = `
	INSERT INTO clients (identifier, ip_address)
	VALUES(?, ?);
	`
	sqlGetClient string = `
	SELECT identifier, timestamp, ip_address, url_list_id
	FROM clients WHERE identifier = ?;
	`
	sqlSetClientList      string = "UPDATE clients SET url_list_id = ? WHERE identifer = ?;"
	sqlSetClientIpAddress string = "UPDATE clients SET ip_address = ? WHERE identifier = ?;"
	sqlTouchClient        string = "UPDATE clients SET last_ping = CURRENT_TIMESTAMP;"

	// urls table
	sqlFindUrlId string = "SELECT id FROM urls WHERE url = ?;"
	sqlInsertUrl string = "INSERT INTO urls(url) VALUES(?);"
	sqlFetchUrls string = "SELECT url FROM urls;"
	sqlDeleteUrl string = "DELETE FROM urls WHERE url = ?;"

	// url_lists table
	sqlFindListId string = "SELECT id FROM url_lists WHERE name = ?;"
	sqlInsertList string = "INSERT INTO url_lists(name) VALUES(?);"
	sqlFetchLists string = "SELECT name FROM url_lists;"
	sqlDeleteList string = "DELETE FROM url_lists WHERE name = ?;"

	// url_lists_url table
	sqlInsertListUrl string = "INSERT INTO url_list_url(url_list_id, url_id) VALUES(?, ?);"
	sqlDeleteListUrl string = "DELETE FROM url_list_url WHERE id = ?;"
	sqlFetchListUrls string = `
	SELECT url FROM urls
	INNER JOIN url_list_url ON url_list_url.url_id = urls.id
	WHERE url_list_id = ?;
	`

	sqlInsertConfig string = "INSERT INTO config(identifier, value) VALUES(?, ?);"
)

type Database struct {
	Conn *sql.DB
}

type Client struct {
	identifier  string
	timestamp   string
	ip_address  string
	url_list_id int
}

func (db *Database) Close() (err error) {
	err = db.Conn.Close()
	return
}

func (db *Database) InsertClient(identifier string, ip_address string) (err error) {
	_, err = db.Conn.Exec(sqlInsertClient, identifier, ip_address)
	return
}

func (db *Database) SetClientIpAddress(identifier string, ip_address string) (err error) {
	_, err = db.Conn.Exec(sqlSetClientIpAddress, identifier, ip_address)
	return
}

func (db *Database) TouchClient(identifier string) (err error) {
	_, err = db.Conn.Exec(sqlTouchClient, identifier)
	return
}

func (db *Database) AssignClientToList(name string, client_id string) (err error) {
	list_id, err := db.FindListId(name)
	if err != nil {
		return
	}

	_, err = db.Conn.Exec(sqlSetClientList, list_id, client_id)
	return
}

func (db *Database) GetClient(identifier string) (client Client, err error) {
	err = db.Conn.QueryRow(sqlGetClient, identifier).Scan(
		&client.identifier,
		&client.timestamp,
		&client.ip_address,
		&client.url_list_id)

	return
}

func (db *Database) InsertUrl(url string) (err error) {
	_, err = db.Conn.Exec(sqlInsertUrl, url)
	return
}

func (db *Database) DeleteUrl(url string) (err error) {
	_, err = db.Conn.Exec(sqlDeleteUrl, url)
	return
}

func (db *Database) InsertList(name string) (err error) {
	_, err = db.Conn.Exec(sqlInsertList, name)
	return
}

func (db *Database) DeleteList(name string) (err error) {
	_, err = db.Conn.Exec(sqlDeleteList, name)
	return
}

func (db *Database) InsertConfig(identifier string, value string) (err error) {
	_, err = db.Conn.Exec(sqlInsertConfig, identifier, value)
	return
}

func (db *Database) FindListId(name string) (id int, err error) {
	row := db.Conn.QueryRow(sqlFindListId, name)

	err = row.Scan(&id)

	return
}

func (db *Database) FindUrlId(url string) (id int, err error) {
	row := db.Conn.QueryRow(sqlFindUrlId, url)

	err = row.Scan(&id)

	return
}

func (db *Database) FetchUrls() (urls []string, err error) {
	rows, err := db.Conn.Query(sqlFetchUrls)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		var url string

		err = rows.Scan(&url)
		if err != nil {
			return
		}

		urls = append(urls, url)
	}

	err = rows.Err()

	return
}

func (db *Database) FetchLists() (lists []string, err error) {
	rows, err := db.Conn.Query(sqlFetchLists)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		var name string

		err = rows.Scan(&name)
		if err != nil {
			return
		}

		lists = append(lists, name)
	}

	err = rows.Err()

	return
}

func (db *Database) FetchListUrls(name string) (urls []string, err error) {
	list_id, err := db.FindListId(name)
	if err != nil {
		return
	}

	rows, err := db.Conn.Query(sqlFetchListUrls, list_id)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		var url string

		err = rows.Scan(&url)
		if err != nil {
			return
		}

		urls = append(urls, url)
	}

	err = rows.Err()

	return
}

func (db *Database) AssignUrlToList(name string, url string) (err error) {
	list_id, err := db.FindListId(name)
	if err != nil {
		return
	}

	url_id, err := db.FindUrlId(url)
	if err != nil {
		return
	}

	_, err = db.Conn.Exec(sqlInsertListUrl, list_id, url_id)
	return

}

func (db *Database) CreateTables() (err error) {
	// Run SQL to create necessary schema
	_, err = db.Conn.Exec(sqlCreateTables)
	return
}

func Connect(database string) (db *Database, err error) {
	c, err := sql.Open("sqlite3", database)
	if err == nil {
		db = &Database{c}
	}

	return
}
<|endoftext|>


<commit_before>package editor

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/hacdias/caddy-cms/config"
	"github.com/hacdias/caddy-cms/utils"
	"github.com/robfig/cron"
	"github.com/spf13/hugo/parser"
)

// POST handles the POST method on editor page
func POST(w http.ResponseWriter, r *http.Request, c *config.Config, filename string) (int, error) {
	// Get the JSON information sent using a buffer
	rawBuffer := new(bytes.Buffer)
	rawBuffer.ReadFrom(r.Body)

	// Creates the raw file "map" using the JSON
	var rawFile map[string]interface{}
	json.Unmarshal(rawBuffer.Bytes(), &rawFile)

	// Initializes the file content to write
	var file []byte

	switch r.Header.Get("X-Content-Type") {
	case "frontmatter-only":
		f, code, err := parseFrontMatterOnlyFile(rawFile, filename)
		if err != nil {
			w.Write([]byte(err.Error()))
			return code, err
		}

		file = f
	case "content-only":
		// The main content of the file
		mainContent := rawFile["content"].(string)
		mainContent = "\n\n" + strings.TrimSpace(mainContent)

		file = []byte(mainContent)
	case "complete":
		f, code, err := parseCompleteFile(r, c, rawFile, filename)
		if err != nil {
			w.Write([]byte(err.Error()))
			return code, err
		}

		file = f
	default:
		return http.StatusBadRequest, nil
	}

	// Write the file
	err := ioutil.WriteFile(filename, file, 0666)

	if err != nil {
		w.Write([]byte(err.Error()))
		return http.StatusInternalServerError, err
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte("{}"))
	return http.StatusOK, nil
}

func parseFrontMatterOnlyFile(rawFile map[string]interface{}, filename string) ([]byte, int, error) {
	frontmatter := strings.TrimPrefix(filepath.Ext(filename), ".")
	var mark rune

	switch frontmatter {
	case "toml":
		mark = rune('+')
	case "json":
		mark = rune('{')
	case "yaml":
		mark = rune('-')
	default:
		return []byte{}, http.StatusNotFound, nil
	}

	f, err := parser.InterfaceToFrontMatter(rawFile, mark)
	fString := string(f)

	// If it's toml or yaml, strip frontmatter identifier
	if frontmatter == "toml" {
		fString = strings.TrimSuffix(fString, "+++\n")
		fString = strings.TrimPrefix(fString, "+++\n")
	}

	if frontmatter == "yaml" {
		fString = strings.TrimSuffix(fString, "---\n")
		fString = strings.TrimPrefix(fString, "---\n")
	}

	f = []byte(fString)

	if err != nil {
		return []byte{}, http.StatusInternalServerError, err
	}

	return f, http.StatusOK, nil
}

func parseCompleteFile(r *http.Request, c *config.Config, rawFile map[string]interface{}, filename string) ([]byte, int, error) {
	// The main content of the file
	mainContent := rawFile["content"].(string)
	mainContent = "\n\n" + strings.TrimSpace(mainContent) + "\n"

	// Removes the main content from the rest of the frontmatter
	delete(rawFile, "content")

	// Schedule the post
	if r.Header.Get("X-Schedule") == "true" {
		t, err := time.Parse("http.StatusOK6-01-02 15:04:05-07:00", rawFile["date"].(string))

		if err != nil {
			return []byte{}, http.StatusInternalServerError, err
		}

		scheduler := cron.New()
		scheduler.AddFunc(t.In(time.Now().Location()).Format("05 04 15 02 01 *"), func() {
			// Set draft to false
			rawFile["draft"] = false

			// Converts the frontmatter in JSON
			jsonFrontmatter, err := json.Marshal(rawFile)

			if err != nil {
				return
			}

			// Indents the json
			frontMatterBuffer := new(bytes.Buffer)
			json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

			// Generates the final file
			f := new(bytes.Buffer)
			f.Write(frontMatterBuffer.Bytes())
			f.Write([]byte(mainContent))
			file := f.Bytes()

			// Write the file
			err = ioutil.WriteFile(filename, file, 0666)

			if err != nil {
				return
			}

			utils.Run(c)
		})
		scheduler.Start()
	}

	// Converts the frontmatter in JSON
	jsonFrontmatter, err := json.Marshal(rawFile)

	if err != nil {
		return []byte{}, http.StatusInternalServerError, err
	}

	// Indents the json
	frontMatterBuffer := new(bytes.Buffer)
	json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

	// Generates the final file
	f := new(bytes.Buffer)
	f.Write(frontMatterBuffer.Bytes())
	f.Write([]byte(mainContent))
	return f.Bytes(), http.StatusOK, nil
}
<commit_msg>make errors more explicit #25<commit_after>package editor

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/hacdias/caddy-cms/config"
	"github.com/hacdias/caddy-cms/utils"
	"github.com/robfig/cron"
	"github.com/spf13/hugo/parser"
)

// POST handles the POST method on editor page
func POST(w http.ResponseWriter, r *http.Request, c *config.Config, filename string) (int, error) {
	// Get the JSON information sent using a buffer
	rawBuffer := new(bytes.Buffer)
	rawBuffer.ReadFrom(r.Body)

	// Creates the raw file "map" using the JSON
	var rawFile map[string]interface{}
	json.Unmarshal(rawBuffer.Bytes(), &rawFile)

	// Initializes the file content to write
	var file []byte

	switch r.Header.Get("X-Content-Type") {
	case "frontmatter-only":
		f, code, err := parseFrontMatterOnlyFile(rawFile, filename)
		if err != nil {
			w.Write([]byte(err.Error()))
			return code, err
		}

		file = f
	case "content-only":
		// The main content of the file
		mainContent := rawFile["content"].(string)
		mainContent = "\n\n" + strings.TrimSpace(mainContent)

		file = []byte(mainContent)
	case "complete":
		f, code, err := parseCompleteFile(r, c, rawFile, filename)
		if err != nil {
			w.Write([]byte(err.Error()))
			return code, err
		}

		file = f
	default:
		return http.StatusBadRequest, errors.New("X-Content-Type header not defined")
	}

	// Write the file
	err := ioutil.WriteFile(filename, file, 0666)

	if err != nil {
		w.Write([]byte(err.Error()))
		return http.StatusInternalServerError, err
	}

	w.Header().Set("Content-Type", "application/json")
	w.Write([]byte("{}"))
	return http.StatusOK, nil
}

func parseFrontMatterOnlyFile(rawFile map[string]interface{}, filename string) ([]byte, int, error) {
	frontmatter := strings.TrimPrefix(filepath.Ext(filename), ".")
	var mark rune

	switch frontmatter {
	case "toml":
		mark = rune('+')
	case "json":
		mark = rune('{')
	case "yaml":
		mark = rune('-')
	default:
		return []byte{}, http.StatusBadRequest, errors.New("can't define the frontmatter")
	}

	f, err := parser.InterfaceToFrontMatter(rawFile, mark)
	fString := string(f)

	// If it's toml or yaml, strip frontmatter identifier
	if frontmatter == "toml" {
		fString = strings.TrimSuffix(fString, "+++\n")
		fString = strings.TrimPrefix(fString, "+++\n")
	}

	if frontmatter == "yaml" {
		fString = strings.TrimSuffix(fString, "---\n")
		fString = strings.TrimPrefix(fString, "---\n")
	}

	f = []byte(fString)

	if err != nil {
		return []byte{}, http.StatusInternalServerError, err
	}

	return f, http.StatusOK, nil
}

func parseCompleteFile(r *http.Request, c *config.Config, rawFile map[string]interface{}, filename string) ([]byte, int, error) {
	// The main content of the file
	mainContent := rawFile["content"].(string)
	mainContent = "\n\n" + strings.TrimSpace(mainContent) + "\n"

	// Removes the main content from the rest of the frontmatter
	delete(rawFile, "content")

	// Schedule the post
	if r.Header.Get("X-Schedule") == "true" {
		t, err := time.Parse("http.StatusOK6-01-02 15:04:05-07:00", rawFile["date"].(string))

		if err != nil {
			return []byte{}, http.StatusInternalServerError, err
		}

		scheduler := cron.New()
		scheduler.AddFunc(t.In(time.Now().Location()).Format("05 04 15 02 01 *"), func() {
			// Set draft to false
			rawFile["draft"] = false

			// Converts the frontmatter in JSON
			jsonFrontmatter, err := json.Marshal(rawFile)

			if err != nil {
				return
			}

			// Indents the json
			frontMatterBuffer := new(bytes.Buffer)
			json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

			// Generates the final file
			f := new(bytes.Buffer)
			f.Write(frontMatterBuffer.Bytes())
			f.Write([]byte(mainContent))
			file := f.Bytes()

			// Write the file
			err = ioutil.WriteFile(filename, file, 0666)

			if err != nil {
				return
			}

			utils.Run(c)
		})
		scheduler.Start()
	}

	// Converts the frontmatter in JSON
	jsonFrontmatter, err := json.Marshal(rawFile)

	if err != nil {
		return []byte{}, http.StatusInternalServerError, err
	}

	// Indents the json
	frontMatterBuffer := new(bytes.Buffer)
	json.Indent(frontMatterBuffer, jsonFrontmatter, "", "  ")

	// Generates the final file
	f := new(bytes.Buffer)
	f.Write(frontMatterBuffer.Bytes())
	f.Write([]byte(mainContent))
	return f.Bytes(), http.StatusOK, nil
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// For ease of implementation, our database's interface matches Ethereum's
// database implementation. This was to allow use to use Geth code as is for the
// EVM chain.

package database

// Iterator iterates over a database's key/value pairs in ascending key order.
//
// When it encounters an error any seek will return false and will yield no key/
// value pairs. The error can be queried by calling the Error method. Calling
// Release is still necessary.
//
// An iterator must be released after use, but it is not necessary to read an
// iterator until exhaustion. An iterator is not safe for concurrent use, but it
// is safe to use multiple iterators concurrently.
type Iterator interface {
	// Next moves the iterator to the next key/value pair. It returns whether the
	// iterator is exhausted.
	Next() bool

	// Error returns any accumulated error. Exhausting all the key/value pairs
	// is not considered to be an error.
	Error() error

	// Key returns the key of the current key/value pair, or nil if done. The caller
	// should not modify the contents of the returned slice, and its contents may
	// change on the next call to Next.
	Key() []byte

	// Value returns the value of the current key/value pair, or nil if done. The
	// caller should not modify the contents of the returned slice, and its contents
	// may change on the next call to Next.
	Value() []byte

	// Release releases associated resources. Release should always succeed and can
	// be called multiple times without causing error.
	Release()
}

// Iteratee wraps the NewIterator methods of a backing data store.
type Iteratee interface {
	// NewIterator creates a binary-alphabetical iterator over the entire keyspace
	// contained within the key-value database.
	NewIterator() Iterator

	// NewIteratorWithStart creates a binary-alphabetical iterator over a subset of
	// database content starting at a particular initial key (or after, if it does
	// not exist).
	NewIteratorWithStart(start []byte) Iterator

	// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset
	// of database content with a particular key prefix.
	NewIteratorWithPrefix(prefix []byte) Iterator

	// NewIteratorWithStartAndPrefix creates a binary-alphabetical iterator over a
	// subset of database content with a particular key prefix starting at a
	// specified key.
	NewIteratorWithStartAndPrefix(start, prefix []byte) Iterator
}
<commit_msg>updated DB spec<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// For ease of implementation, our database's interface matches Ethereum's
// database implementation. This was to allow use to use Geth code as is for the
// EVM chain.

package database

// Iterator iterates over a database's key/value pairs in ascending key order.
//
// When it encounters an error any seek will return false and will yield no key/
// value pairs. The error can be queried by calling the Error method. Calling
// Release is still necessary.
//
// An iterator must be released after use, but it is not necessary to read an
// iterator until exhaustion. An iterator is not safe for concurrent use, but it
// is safe to use multiple iterators concurrently.
type Iterator interface {
	// Next moves the iterator to the next key/value pair. It returns whether
	// the iterator is exhausted.
	Next() bool

	// Error returns any accumulated error. Exhausting all the key/value pairs
	// is not considered to be an error.
	Error() error

	// Key returns the key of the current key/value pair, or nil if done.
	Key() []byte

	// Value returns the value of the current key/value pair, or nil if done.
	Value() []byte

	// Release releases associated resources. Release should always succeed and
	// can be called multiple times without causing error.
	Release()
}

// Iteratee wraps the NewIterator methods of a backing data store.
type Iteratee interface {
	// NewIterator creates a binary-alphabetical iterator over the entire
	// keyspace contained within the key-value database.
	NewIterator() Iterator

	// NewIteratorWithStart creates a binary-alphabetical iterator over a subset
	// of database content starting at a particular initial key (or after, if it
	// does not exist).
	NewIteratorWithStart(start []byte) Iterator

	// NewIteratorWithPrefix creates a binary-alphabetical iterator over a
	// subset of database content with a particular key prefix.
	NewIteratorWithPrefix(prefix []byte) Iterator

	// NewIteratorWithStartAndPrefix creates a binary-alphabetical iterator over
	// a subset of database content with a particular key prefix starting at a
	// specified key.
	NewIteratorWithStartAndPrefix(start, prefix []byte) Iterator
}
<|endoftext|>


<commit_before>package authprovisioningv2

import (
	"crypto/sha256"
	"encoding/base32"
	"strings"
	"time"

	"github.com/pkg/errors"
	v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v1 "github.com/rancher/rancher/pkg/apis/provisioning.cattle.io/v1"
	"github.com/rancher/rancher/pkg/rbac"
	"github.com/rancher/wrangler/pkg/name"
	"github.com/sirupsen/logrus"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const PRTBRoleBindingID = "auth-prov-v2-prtb-rolebinding"

func (h *handler) OnPRTB(key string, prtb *v3.ProjectRoleTemplateBinding) (*v3.ProjectRoleTemplateBinding, error) {
	if prtb == nil || prtb.DeletionTimestamp != nil || prtb.RoleTemplateName == "" || prtb.ProjectName == "" {
		return prtb, nil
	}

	parts := strings.SplitN(prtb.ProjectName, ":", 2)
	if len(parts) < 2 {
		return prtb, errors.Errorf("cannot determine project and cluster from %v", prtb.ProjectName)
	}

	clusterName := parts[0]

	clusters, err := h.clusters.GetByIndex(byClusterName, clusterName)
	if err != nil {
		return prtb, err
	}

	if len(clusters) == 0 {
		// When no provisioning cluster is found, enqueue the PRTB to wait for
		// the provisioning cluster to be created. If we don't try again
		// permissions for the provisioning objects won't be created until an
		// update to the PRTB happens again.
		logrus.Debugf("[auth-prov-v2-prtb] No provisioning cluster found for cluster %v, enqueuing PRTB %v ", prtb.ClusterName, prtb.Name)
		h.projectRoleTemplateBindingController.EnqueueAfter(prtb.Namespace, prtb.Name, 10*time.Second)
		return prtb, nil
	}

	cluster := clusters[0]

	err = h.ensureClusterViewBinding(cluster, prtb)

	return prtb, err
}

func (h *handler) ensureClusterViewBinding(cluster *v1.Cluster, prtb *v3.ProjectRoleTemplateBinding) error {
	subject, err := rbac.BuildSubjectFromRTB(prtb)
	if err != nil {
		return err
	}

	// The roleBinding name format: r-cluster-<cluster name>-view-<prtb name>-<hashed subject>
	// Example: r-cluster1-view-prtb-foo-wn5d5n7udr
	roleBinding := &rbacv1.RoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name.SafeConcatName(clusterViewName(cluster), prtb.Name, hashSubject(subject)),
			Namespace: cluster.Namespace,
			OwnerReferences: []metav1.OwnerReference{
				{
					APIVersion: cluster.APIVersion,
					Kind:       cluster.Kind,
					Name:       cluster.Name,
					UID:        cluster.UID,
				},
			},
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: rbacv1.GroupName,
			Kind:     "Role",
			Name:     clusterViewName(cluster),
		},
	}

	roleBinding.Subjects = []rbacv1.Subject{subject}

	return h.roleBindingApply.
		WithListerNamespace(cluster.Namespace).
		WithSetID(PRTBRoleBindingID).
		WithOwner(prtb).
		ApplyObjects(roleBinding)
}

func clusterViewName(cluster *v1.Cluster) string {
	return name.SafeConcatName("r-cluster", cluster.Name, "view")
}

func hashSubject(subject rbacv1.Subject) string {
	h := sha256.New()
	h.Write([]byte(subject.String()))
	return strings.ToLower(base32.StdEncoding.WithPadding(-1).EncodeToString(h.Sum(nil))[:10])
}
<commit_msg>Fix name collision for prtbs<commit_after>package authprovisioningv2

import (
	"crypto/sha256"
	"encoding/base32"
	"strings"
	"time"

	"github.com/pkg/errors"
	v3 "github.com/rancher/rancher/pkg/apis/management.cattle.io/v3"
	v1 "github.com/rancher/rancher/pkg/apis/provisioning.cattle.io/v1"
	"github.com/rancher/rancher/pkg/rbac"
	"github.com/rancher/wrangler/pkg/name"
	"github.com/sirupsen/logrus"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const PRTBRoleBindingID = "auth-prov-v2-prtb-rolebinding"

func (h *handler) OnPRTB(key string, prtb *v3.ProjectRoleTemplateBinding) (*v3.ProjectRoleTemplateBinding, error) {
	if prtb == nil || prtb.DeletionTimestamp != nil || prtb.RoleTemplateName == "" || prtb.ProjectName == "" {
		return prtb, nil
	}

	parts := strings.SplitN(prtb.ProjectName, ":", 2)
	if len(parts) < 2 {
		return prtb, errors.Errorf("cannot determine project and cluster from %v", prtb.ProjectName)
	}

	clusterName := parts[0]

	clusters, err := h.clusters.GetByIndex(byClusterName, clusterName)
	if err != nil {
		return prtb, err
	}

	if len(clusters) == 0 {
		// When no provisioning cluster is found, enqueue the PRTB to wait for
		// the provisioning cluster to be created. If we don't try again
		// permissions for the provisioning objects won't be created until an
		// update to the PRTB happens again.
		logrus.Debugf("[auth-prov-v2-prtb] No provisioning cluster found for cluster %v, enqueuing PRTB %v ", prtb.ClusterName, prtb.Name)
		h.projectRoleTemplateBindingController.EnqueueAfter(prtb.Namespace, prtb.Name, 10*time.Second)
		return prtb, nil
	}

	cluster := clusters[0]

	err = h.ensureClusterViewBinding(cluster, prtb)

	return prtb, err
}

func (h *handler) ensureClusterViewBinding(cluster *v1.Cluster, prtb *v3.ProjectRoleTemplateBinding) error {
	subject, err := rbac.BuildSubjectFromRTB(prtb)
	if err != nil {
		return err
	}

	// The roleBinding name format: r-cluster-<cluster name>-view-<prtb namespace>-<prtb name>-<hashed subject>
	// Example: r-cluster1-view-prtb-bar-foo-wn5d5n7udr
	roleBinding := &rbacv1.RoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name.SafeConcatName(clusterViewName(cluster), prtb.Namespace, prtb.Name, hashSubject(subject)),
			Namespace: cluster.Namespace,
			OwnerReferences: []metav1.OwnerReference{
				{
					APIVersion: cluster.APIVersion,
					Kind:       cluster.Kind,
					Name:       cluster.Name,
					UID:        cluster.UID,
				},
			},
		},
		RoleRef: rbacv1.RoleRef{
			APIGroup: rbacv1.GroupName,
			Kind:     "Role",
			Name:     clusterViewName(cluster),
		},
	}

	roleBinding.Subjects = []rbacv1.Subject{subject}

	return h.roleBindingApply.
		WithListerNamespace(cluster.Namespace).
		WithSetID(PRTBRoleBindingID).
		WithOwner(prtb).
		ApplyObjects(roleBinding)
}

func clusterViewName(cluster *v1.Cluster) string {
	return name.SafeConcatName("r-cluster", cluster.Name, "view")
}

func hashSubject(subject rbacv1.Subject) string {
	h := sha256.New()
	h.Write([]byte(subject.String()))
	return strings.ToLower(base32.StdEncoding.WithPadding(-1).EncodeToString(h.Sum(nil))[:10])
}
<|endoftext|>


<commit_before>// Copyright 2014 Joseph Hager. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !darwin,!netgo,!android

package engi

import (
	"azul3d.org/chippy.v1"
	"azul3d.org/keyboard.v1"
	"azul3d.org/mouse.v1"
	"github.com/go-gl/glow/gl/2.1/gl"
	"log"
	"os"
	"runtime"
)

var window *chippy.Window

func program() {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	screen := chippy.DefaultScreen()
	window = chippy.NewWindow()
	window.SetTitle(config.Title)
	window.SetSize(config.Width, config.Height)
	window.SetPositionCenter(screen)

	if config.Fullscreen {
		window.SetFullscreen(true)
		mode := screen.Mode()
		config.Width, config.Height = mode.Resolution()
	}

	err := window.Open(screen)
	if err != nil {
		log.Fatal(err)
	}

	configs := window.GLConfigs()
	bestConfig := chippy.GLChooseConfig(configs, chippy.GLWorstConfig, chippy.GLBestConfig)
	window.GLSetConfig(bestConfig)

	context, err := window.GLCreateContext(2, 1, chippy.GLCoreProfile, nil)
	if err != nil {
		log.Fatal(err)
	}
	window.GLMakeCurrent(context)

	if err := gl.Init(); err != nil {
		log.Fatal(err)
	}
	GL = newgl2()

	GL.Viewport(0, 0, config.Width, config.Height)

	responder.Preload()
	timing = NewStats(config.LogFPS)
	timing.Update()

	Files.Load(func() {})

	responder.Setup()

	events := window.Events()
	defer window.CloseEvents(events)

	for {
		responder.Update(float32(timing.Dt))
		GL.Clear(gl.COLOR_BUFFER_BIT)
		responder.Render()
		window.GLSwapBuffers()
		timing.Update()

		for i := 0; i < len(events); i++ {
			e := <-events
			switch ev := e.(type) {
			case chippy.ResizedEvent:
				config.Width = ev.Width
				config.Height = ev.Height
				GL.Viewport(0, 0, config.Width, config.Height)
				responder.Resize(config.Width, config.Height)
			case keyboard.StateEvent:
				switch ev.State {
				case keyboard.Up:
					responder.Key(Key(ev.Key), Modifier(0), RELEASE)
				case keyboard.Down:
					responder.Key(Key(ev.Key), Modifier(0), PRESS)
				}
			case keyboard.TypedEvent:
				responder.Type(ev.Rune)
			case mouse.Event:
				switch ev.State {
				case mouse.Up:
					responder.Mouse(float32(0), float32(0), RELEASE)
				case mouse.Down:
					responder.Mouse(float32(0), float32(0), PRESS)
				case mouse.ScrollForward:
					responder.Scroll(float32(1))
				case mouse.ScrollBack:
					responder.Scroll(float32(-1))
				}
			case chippy.CloseEvent, chippy.DestroyedEvent:
				// Not triggering
				println(1)
				return
			default:
			}
		}
	}
}

func run() {
	chippy.SetDebugOutput(os.Stdout)

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

	go program()
	chippy.MainLoop()
}

func exit() {
	window.Destroy()
	chippy.Exit()
}

const Escape = Key(keyboard.Escape)
<commit_msg>Fix exiting on windows and linux<commit_after>// Copyright 2014 Joseph Hager. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !darwin,!netgo,!android

package engi

import (
	"azul3d.org/chippy.v1"
	"azul3d.org/keyboard.v1"
	"azul3d.org/mouse.v1"
	"github.com/go-gl/glow/gl/2.1/gl"
	"log"
	"os"
	"runtime"
)

var window *chippy.Window

func program() {
	defer chippy.Exit()

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	screen := chippy.DefaultScreen()
	window = chippy.NewWindow()
	window.SetTitle(config.Title)
	window.SetSize(config.Width, config.Height)
	window.SetPositionCenter(screen)

	if config.Fullscreen {
		window.SetFullscreen(true)
		mode := screen.Mode()
		config.Width, config.Height = mode.Resolution()
	}

	err := window.Open(screen)
	if err != nil {
		log.Fatal(err)
	}

	configs := window.GLConfigs()
	bestConfig := chippy.GLChooseConfig(configs, chippy.GLWorstConfig, chippy.GLBestConfig)
	window.GLSetConfig(bestConfig)

	context, err := window.GLCreateContext(2, 1, chippy.GLCoreProfile, nil)
	if err != nil {
		log.Fatal(err)
	}
	window.GLMakeCurrent(context)

	if err := gl.Init(); err != nil {
		log.Fatal(err)
	}
	GL = newgl2()

	GL.Viewport(0, 0, config.Width, config.Height)

	responder.Preload()
	timing = NewStats(config.LogFPS)
	timing.Update()

	Files.Load(func() {})

	responder.Setup()

	events := window.Events()
	defer window.CloseEvents(events)

	for {
		responder.Update(float32(timing.Dt))
		GL.Clear(gl.COLOR_BUFFER_BIT)
		responder.Render()
		window.GLSwapBuffers()
		timing.Update()

		for i := 0; i < len(events); i++ {
			e := <-events
			switch ev := e.(type) {
			case chippy.ResizedEvent:
				config.Width = ev.Width
				config.Height = ev.Height
				GL.Viewport(0, 0, config.Width, config.Height)
				responder.Resize(config.Width, config.Height)
			case keyboard.StateEvent:
				switch ev.State {
				case keyboard.Up:
					responder.Key(Key(ev.Key), Modifier(0), RELEASE)
				case keyboard.Down:
					responder.Key(Key(ev.Key), Modifier(0), PRESS)
				}
			case keyboard.TypedEvent:
				responder.Type(ev.Rune)
			case mouse.Event:
				switch ev.State {
				case mouse.Up:
					responder.Mouse(float32(0), float32(0), RELEASE)
				case mouse.Down:
					responder.Mouse(float32(0), float32(0), PRESS)
				case mouse.ScrollForward:
					responder.Scroll(float32(1))
				case mouse.ScrollBack:
					responder.Scroll(float32(-1))
				}
			case chippy.CloseEvent, chippy.DestroyedEvent:
				return
			default:
			}
		}
	}
}

func run() {
	chippy.SetDebugOutput(os.Stdout)

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

	go program()
	chippy.MainLoop()
}

func exit() {
	window.Destroy()
}

const Escape = Key(keyboard.Escape)
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"koding/kontrol/kontrolhelper"
	"koding/tools/amqputil"
	"koding/tools/config"
	"koding/tools/lifecycle"
	"koding/tools/logger"
	"koding/tools/sockjs"
	"koding/tools/utils"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/fatih/set"
	"github.com/streadway/amqp"
)

const BROKER_NAME = "broker"

var (
	conf *config.Config
	log  = logger.New(BROKER_NAME)

	// routeMap holds the subscription list/set for any given routing key
	routeMap = make(map[string]*set.Set)

	// sessionsMap holds sessions with their socketIds
	sessionsMap    = make(map[string]*sockjs.Session)
	globalMapMutex sync.Mutex

	changeClientsGauge          = lifecycle.CreateClientsGauge()
	changeNewClientsGauge       = logger.CreateCounterGauge("newClients", logger.NoUnit, true)
	changeWebsocketClientsGauge = logger.CreateCounterGauge("websocketClients", logger.NoUnit, false)

	flagProfile      = flag.String("c", "", "Configuration profile from file")
	flagBrokerDomain = flag.String("a", "", "Send kontrol a custom domain istead of os.Hostname")
	flagKontrolUUID  = flag.String("u", "", "Enable Kontrol mode")
	flagBrokerType   = flag.String("b", "broker", "Define broker type. Available: broker and brokerKite. B")
	flagDebug        = flag.Bool("d", false, "Debug mode")
)

// Broker is a router/multiplexer that routes messages coming from a SockJS
// server to an AMQP exchange and vice versa. Broker basically listens to
// client messages (Koding users) from the SockJS server. The message is
// either passed to the appropriate exchange or a response is sent back to the
// client. Each message has an "action" field that defines how to act for a
// received message.
type Broker struct {
	Config            *config.Broker
	Hostname          string
	ServiceUniqueName string
	AuthAllExchange   string
	PublishConn       *amqp.Connection
	ConsumeConn       *amqp.Connection

	// Accepts SockJS connections
	listener net.Listener

	// Closed when SockJS server is ready to acccept connections
	ready chan struct{}
}

// NewBroker returns a new Broker instance with ServiceUniqueName and Hostname
// prepopulated. After creating a Broker instance, one has to call
// broker.Run() or broker.Start() to start the broker instance and call
// broker.Close() for a graceful stop.
func NewBroker() *Broker {
	// returns os.Hostname() if config.BrokerDomain is empty, otherwise it just
	// returns config.BrokerDomain back
	brokerHostname := kontrolhelper.CustomHostname(*flagBrokerDomain)
	sanitizedHostname := strings.Replace(brokerHostname, ".", "_", -1)
	serviceUniqueName := BROKER_NAME + "|" + sanitizedHostname

	return &Broker{
		Hostname:          brokerHostname,
		ServiceUniqueName: serviceUniqueName,
		ready:             make(chan struct{}),
	}
}

func main() {
	flag.Parse()
	if *flagProfile == "" {
		log.Fatal("Please specify profile via -c. Aborting.")
	}

	conf = config.MustConfig(*flagProfile)
	broker := NewBroker()

	switch *flagBrokerType {
	case "brokerKite":
		broker.Config = &conf.BrokerKite
	case "premiumBrokerKite":
		broker.Config = &conf.PremiumBrokerKite
	default:
		broker.Config = &conf.Broker
	}

	// update broker name
	log = logger.New(broker.Config.Name)
	var logLevel logger.Level
	if *flagDebug {
		logLevel = logger.DEBUG
	} else {
		logLevel = logger.GetLoggingLevelFromConfig(BROKER_NAME, *flagProfile)
	}

	log.SetLevel(logLevel)
	broker.Run()
}

// Run starts the broker.
func (b *Broker) Run() {
	// sets the maximum number of CPUs that can be executing simultaneously
	runtime.GOMAXPROCS(runtime.NumCPU())

	lifecycle.Startup(BROKER_NAME, false)
	logger.RunGaugesLoop(log)

	if err := b.registerToKontrol(); err != nil {
		log.Critical("Couldnt register to kontrol, stopping... %v", err)
		return
	}

	if err := b.startAMQP(); err != nil {
		log.Critical("Couldnt create amqp bindings, stopping... %v", err)
		return
	}

	b.startSockJS() // blocking
}

// Start is like Run() but waits until the SockJS listener is ready to be
// used.
func (b *Broker) Start() {
	go b.Run()
	<-b.ready
}

// Close close all amqp connections and closes the SockJS server listener
func (b *Broker) Close() {
	b.PublishConn.Close()
	b.ConsumeConn.Close()
	b.listener.Close()
}

// registerToKontrol registers the broker to KontrolDaemon. This is needed to
// populate a list of brokers and show them to the client. The list is
// available at: https://koding.com/-/services/broker?all
func (b *Broker) registerToKontrol() error {
	if err := kontrolhelper.RegisterToKontrol(
		conf,
		b.Config.Name,
		b.Config.Name, // servicGenericName
		b.ServiceUniqueName,
		*flagKontrolUUID,
		b.Hostname,
		b.Config.Port,
	); err != nil {
		return err
	}
	return nil
}

// startAMQP setups the the neccesary publisher and consumer connections for
// the broker broker.
func (b *Broker) startAMQP() error {
	b.PublishConn = amqputil.CreateConnection(conf, b.Config.Name)
	b.ConsumeConn = amqputil.CreateConnection(conf, b.Config.Name)
	consumeChannel := amqputil.CreateChannel(b.ConsumeConn)
	presenceQueue := amqputil.JoinPresenceExchange(
		consumeChannel,      // channel
		"services-presence", // exchange
		b.Config.Name,       // serviceType
		b.Config.Name,       // serviceGenericName
		b.ServiceUniqueName, // serviceUniqueName
		false,               // loadBalancing
	)

	go func() {
		sigusr1Channel := make(chan os.Signal)
		signal.Notify(sigusr1Channel, syscall.SIGUSR1)
		<-sigusr1Channel
		consumeChannel.QueueDelete(presenceQueue, false, false, false)
	}()

	stream := amqputil.DeclareBindConsumeQueue(consumeChannel, "topic", b.Config.Name, "#", false)

	if err := consumeChannel.ExchangeDeclare(
		"updateInstances", // name
		"fanout",          // kind
		false,             // durable
		false,             // autoDelete
		false,             // internal
		false,             // noWait
		nil,               // args
	); err != nil {
		return fmt.Errorf("Couldnt create updateInstances exchange  %v", err)
	}

	if err := consumeChannel.ExchangeBind(BROKER_NAME, "", "updateInstances", false, nil); err != nil {
		return fmt.Errorf("Couldnt bind to updateInstances exchange  %v", err)
	}

	go func(stream <-chan amqp.Delivery) {
		// start to listen from "broker" topic exchange
		for amqpMessage := range stream {
			sendMessageToClient(amqpMessage)
		}

		b.Close()

	}(stream)

	return nil
}

func sendMessageToClient(amqpMessage amqp.Delivery) {
	routingKey := amqpMessage.RoutingKey
	payloadsByte := utils.FilterInvalidUTF8(amqpMessage.Body)

	if amqpMessage.Exchange == "updateInstances" {
		var payloads []interface{}
		if err := json.Unmarshal(payloadsByte, &payloads); err != nil {
			log.Error("Error while unmarshalling %v", err)
			log.Error("data %v", string(payloadsByte))
			log.Error("routingKey %v", routingKey)
			return
		}

		for _, payload := range payloads {
			payloadByte, err := json.Marshal(payload)
			if err != nil {
				log.Error("Error while marshalling %v", err)
				log.Error("data %v", string(payloadByte))
				log.Error("routingKey %v", routingKey)
				continue
			}
			payloadByteRaw := json.RawMessage(payloadByte)
			processMessage(routingKey, &payloadByteRaw)
		}
	} else {
		payloadRaw := json.RawMessage(payloadsByte)
		processMessage(routingKey, &payloadRaw)
	}

}

func processMessage(routingKey string, payload interface{}) {
	pos := strings.IndexRune(routingKey, '.') // skip first dot, since we want at least two components to always include the secret
	for pos != -1 && pos < len(routingKey) {
		index := strings.IndexRune(routingKey[pos+1:], '.')
		pos += index + 1
		if index == -1 {
			pos = len(routingKey)
		}
		routingKeyPrefix := routingKey[:pos]
		globalMapMutex.Lock()

		if routes, ok := routeMap[routingKeyPrefix]; ok {
			routes.Each(func(sessionId interface{}) bool {
				if routeSession, ok := sessionsMap[sessionId.(string)]; ok {
					sendToClient(routeSession, routingKey, &payload)
				}
				return true
			})
		}
		globalMapMutex.Unlock()
	}
}

// startSockJS starts a new HTTPS listener that implies the SockJS protocol.
func (b *Broker) startSockJS() {
	service := sockjs.NewService(
		conf.Client.StaticFilesBaseUrl+"/js/sock.js",
		10*time.Minute,
		b.sockjsSession,
	)
	defer service.Close()

	service.MaxReceivedPerSecond = 50
	service.ErrorHandler = log.LogError

	// TODO use http.Mux instead of sockjs.Mux.
	mux := &sockjs.Mux{
		Handlers: map[string]http.Handler{
			"/subscribe": service,
			"/buildnumber": http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				w.Header().Set("Content-Type", "text/plain")
				w.Write([]byte(strconv.Itoa(conf.BuildNumber)))
			}),
		},
	}

	server := &http.Server{Handler: mux}

	var err error
	b.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP(b.Config.IP), Port: b.Config.Port})
	if err != nil {
		log.Fatal(err)
	}

	if b.Config.CertFile != "" {
		cert, err := tls.LoadX509KeyPair(b.Config.CertFile, b.Config.KeyFile)
		if err != nil {
			log.Fatal(err)
		}
		b.listener = tls.NewListener(b.listener, &tls.Config{
			NextProtos:   []string{"http/1.1"},
			Certificates: []tls.Certificate{cert},
		})
	}

	// signal that we are ready now
	close(b.ready)

	lastErrorTime := time.Now()
	for {
		err := server.Serve(b.listener)
		if err != nil {
			// comes when the broker is closed with Close() method. This error
			// is defined in net/net.go as "var errClosing", unfortunaly it's
			// not exported.
			if strings.Contains(err.Error(), "use of closed network connection") {
				return
			}

			log.Warning("Server error: %v", err)
			if time.Now().Sub(lastErrorTime) < time.Second {
				log.Fatal(nil)
			}
			lastErrorTime = time.Now()
		}
	}

}

// sockjsSession is called for every client connection and handles all the
// message trafic for a single client connection.
func (b *Broker) sockjsSession(session *sockjs.Session) {
	client, err := NewClient(session, b)
	if err != nil {
		log.Critical("Couldnt create client %v", err)
		return
	}

	sessionGaugeEnd := client.gaugeStart()

	defer sessionGaugeEnd()
	defer client.Close()

	err = client.ControlChannel.Publish(b.Config.AuthAllExchange, "broker.clientConnected", false, false, amqp.Publishing{Body: []byte(client.SocketId)})
	if err != nil {
		log.Critical("Couldnt publish to control channel %v", err)
	}

	sendToClient(session, "broker.connected", client.SocketId)

	for data := range session.ReceiveChan {
		if data == nil || session.Closed {
			break
		}

		client.handleSessionMessage(data)
	}
}

// sendToClient sends the given payload back to the client. It attachs the
// routintKey along with the payload. It closes the session if sending fails.
func sendToClient(session *sockjs.Session, routingKey string, payload interface{}) {
	var message struct {
		RoutingKey string      `json:"routingKey"`
		Payload    interface{} `json:"payload"`
	}
	message.RoutingKey = routingKey
	message.Payload = payload
	if !session.Send(message) {
		session.Close()
		log.Warning("Dropped session because of broker to client buffer overflow. %v", session.Tag)
	}
}
<commit_msg>Broker: register message handler function and add doc<commit_after>package main

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"koding/kontrol/kontrolhelper"
	"koding/tools/amqputil"
	"koding/tools/config"
	"koding/tools/lifecycle"
	"koding/tools/logger"
	"koding/tools/sockjs"
	"koding/tools/utils"
	"net"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/fatih/set"
	"github.com/streadway/amqp"
)

const BROKER_NAME = "broker"

var (
	conf *config.Config
	log  = logger.New(BROKER_NAME)

	// routeMap holds the subscription list/set for any given routing key
	routeMap = make(map[string]*set.Set)

	// sessionsMap holds sessions with their socketIds
	sessionsMap    = make(map[string]*sockjs.Session)
	globalMapMutex sync.Mutex

	changeClientsGauge          = lifecycle.CreateClientsGauge()
	changeNewClientsGauge       = logger.CreateCounterGauge("newClients", logger.NoUnit, true)
	changeWebsocketClientsGauge = logger.CreateCounterGauge("websocketClients", logger.NoUnit, false)

	flagProfile      = flag.String("c", "", "Configuration profile from file")
	flagBrokerDomain = flag.String("a", "", "Send kontrol a custom domain istead of os.Hostname")
	flagKontrolUUID  = flag.String("u", "", "Enable Kontrol mode")
	flagBrokerType   = flag.String("b", "broker", "Define broker type. Available: broker and brokerKite. B")
	flagDebug        = flag.Bool("d", false, "Debug mode")
)

// Broker is a router/multiplexer that routes messages coming from a SockJS
// server to an AMQP exchange and vice versa. Broker basically listens to
// client messages (Koding users) from the SockJS server. The message is
// either passed to the appropriate exchange or a response is sent back to the
// client. Each message has an "action" field that defines how to act for a
// received message.
type Broker struct {
	Config            *config.Broker
	Hostname          string
	ServiceUniqueName string
	AuthAllExchange   string
	PublishConn       *amqp.Connection
	ConsumeConn       *amqp.Connection

	// Accepts SockJS connections
	listener net.Listener

	// Closed when SockJS server is ready to acccept connections
	ready chan struct{}
}

// NewBroker returns a new Broker instance with ServiceUniqueName and Hostname
// prepopulated. After creating a Broker instance, one has to call
// broker.Run() or broker.Start() to start the broker instance and call
// broker.Close() for a graceful stop.
func NewBroker() *Broker {
	// returns os.Hostname() if config.BrokerDomain is empty, otherwise it just
	// returns config.BrokerDomain back
	brokerHostname := kontrolhelper.CustomHostname(*flagBrokerDomain)
	sanitizedHostname := strings.Replace(brokerHostname, ".", "_", -1)
	serviceUniqueName := BROKER_NAME + "|" + sanitizedHostname

	return &Broker{
		Hostname:          brokerHostname,
		ServiceUniqueName: serviceUniqueName,
		ready:             make(chan struct{}),
	}
}

func main() {
	flag.Parse()
	if *flagProfile == "" {
		log.Fatal("Please specify profile via -c. Aborting.")
	}

	conf = config.MustConfig(*flagProfile)
	broker := NewBroker()

	switch *flagBrokerType {
	case "brokerKite":
		broker.Config = &conf.BrokerKite
	case "premiumBrokerKite":
		broker.Config = &conf.PremiumBrokerKite
	default:
		broker.Config = &conf.Broker
	}

	// update broker name
	log = logger.New(broker.Config.Name)
	var logLevel logger.Level
	if *flagDebug {
		logLevel = logger.DEBUG
	} else {
		logLevel = logger.GetLoggingLevelFromConfig(BROKER_NAME, *flagProfile)
	}

	log.SetLevel(logLevel)
	broker.Run()
}

// Run starts the broker.
func (b *Broker) Run() {
	// sets the maximum number of CPUs that can be executing simultaneously
	runtime.GOMAXPROCS(runtime.NumCPU())

	lifecycle.Startup(BROKER_NAME, false)
	logger.RunGaugesLoop(log)

	if err := b.registerToKontrol(); err != nil {
		log.Critical("Couldnt register to kontrol, stopping... %v", err)
		return
	}

	if err := b.startAMQP(); err != nil {
		log.Critical("Couldnt create amqp bindings, stopping... %v", err)
		return
	}

	b.startSockJS() // blocking
}

// Start is like Run() but waits until the SockJS listener is ready to be
// used.
func (b *Broker) Start() {
	go b.Run()
	<-b.ready
}

// Close close all amqp connections and closes the SockJS server listener
func (b *Broker) Close() {
	b.PublishConn.Close()
	b.ConsumeConn.Close()
	b.listener.Close()
}

// registerToKontrol registers the broker to KontrolDaemon. This is needed to
// populate a list of brokers and show them to the client. The list is
// available at: https://koding.com/-/services/broker?all
func (b *Broker) registerToKontrol() error {
	if err := kontrolhelper.RegisterToKontrol(
		conf,
		b.Config.Name,
		b.Config.Name, // servicGenericName
		b.ServiceUniqueName,
		*flagKontrolUUID,
		b.Hostname,
		b.Config.Port,
	); err != nil {
		return err
	}
	return nil
}

// startAMQP setups the the neccesary publisher and consumer connections for
// the broker broker.
func (b *Broker) startAMQP() error {
	b.PublishConn = amqputil.CreateConnection(conf, b.Config.Name)
	b.ConsumeConn = amqputil.CreateConnection(conf, b.Config.Name)
	consumeChannel := amqputil.CreateChannel(b.ConsumeConn)
	presenceQueue := amqputil.JoinPresenceExchange(
		consumeChannel,      // channel
		"services-presence", // exchange
		b.Config.Name,       // serviceType
		b.Config.Name,       // serviceGenericName
		b.ServiceUniqueName, // serviceUniqueName
		false,               // loadBalancing
	)

	go func() {
		sigusr1Channel := make(chan os.Signal)
		signal.Notify(sigusr1Channel, syscall.SIGUSR1)
		<-sigusr1Channel
		consumeChannel.QueueDelete(presenceQueue, false, false, false)
	}()

	stream := amqputil.DeclareBindConsumeQueue(consumeChannel, "topic", b.Config.Name, "#", false)

	if err := consumeChannel.ExchangeDeclare(
		"updateInstances", // name
		"fanout",          // kind
		false,             // durable
		false,             // autoDelete
		false,             // internal
		false,             // noWait
		nil,               // args
	); err != nil {
		return fmt.Errorf("Couldnt create updateInstances exchange  %v", err)
	}

	if err := consumeChannel.ExchangeBind(BROKER_NAME, "", "updateInstances", false, nil); err != nil {
		return fmt.Errorf("Couldnt bind to updateInstances exchange  %v", err)
	}

	go func(stream <-chan amqp.Delivery) {
		// start to listen from "broker" topic exchange
		for amqpMessage := range stream {
			sendMessageToClient(amqpMessage)
		}

		b.Close()

	}(stream)

	return nil
}

func sendMessageToClient(amqpMessage amqp.Delivery) {
	routingKey := amqpMessage.RoutingKey
	payloadsByte := utils.FilterInvalidUTF8(amqpMessage.Body)

	// We are sending multiple bodies for updateInstances exchange
	// so that there will be another operations, if exchange is not "updateInstances"
	// no need to add more overhead
	if amqpMessage.Exchange != "updateInstances" {
		payloadRaw := json.RawMessage(payloadsByte)
		processMessage(routingKey, &payloadRaw)
		return
	}

	// this part is only for updateInstances exchange
	var payloads []interface{}
	// unmarshal data to slice of interface
	if err := json.Unmarshal(payloadsByte, &payloads); err != nil {
		log.Error("Error while unmarshalling %v", err)
		log.Error("data %v", string(payloadsByte))
		log.Error("routingKey %v", routingKey)
		return
	}

	// range over the slice and send all of them to the same routingkey
	for _, payload := range payloads {
		payloadByte, err := json.Marshal(payload)
		if err != nil {
			log.Error("Error while marshalling %v", err)
			log.Error("data %v", string(payloadByte))
			log.Error("routingKey %v", routingKey)
			continue
		}
		payloadByteRaw := json.RawMessage(payloadByte)
		processMessage(routingKey, &payloadByteRaw)
	}
}

func processMessage(routingKey string, payload interface{}) {
	pos := strings.IndexRune(routingKey, '.') // skip first dot, since we want at least two components to always include the secret
	for pos != -1 && pos < len(routingKey) {
		index := strings.IndexRune(routingKey[pos+1:], '.')
		pos += index + 1
		if index == -1 {
			pos = len(routingKey)
		}
		routingKeyPrefix := routingKey[:pos]
		globalMapMutex.Lock()

		if routes, ok := routeMap[routingKeyPrefix]; ok {
			routes.Each(func(sessionId interface{}) bool {
				if routeSession, ok := sessionsMap[sessionId.(string)]; ok {
					sendToClient(routeSession, routingKey, &payload)
				}
				return true
			})
		}
		globalMapMutex.Unlock()
	}
}

// startSockJS starts a new HTTPS listener that implies the SockJS protocol.
func (b *Broker) startSockJS() {
	service := sockjs.NewService(
		conf.Client.StaticFilesBaseUrl+"/js/sock.js",
		10*time.Minute,
		b.sockjsSession,
	)
	defer service.Close()

	service.MaxReceivedPerSecond = 50
	service.ErrorHandler = log.LogError

	// TODO use http.Mux instead of sockjs.Mux.
	mux := &sockjs.Mux{
		Handlers: map[string]http.Handler{
			"/subscribe": service,
			"/buildnumber": http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				w.Header().Set("Content-Type", "text/plain")
				w.Write([]byte(strconv.Itoa(conf.BuildNumber)))
			}),
		},
	}

	server := &http.Server{Handler: mux}

	var err error
	b.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP(b.Config.IP), Port: b.Config.Port})
	if err != nil {
		log.Fatal(err)
	}

	if b.Config.CertFile != "" {
		cert, err := tls.LoadX509KeyPair(b.Config.CertFile, b.Config.KeyFile)
		if err != nil {
			log.Fatal(err)
		}
		b.listener = tls.NewListener(b.listener, &tls.Config{
			NextProtos:   []string{"http/1.1"},
			Certificates: []tls.Certificate{cert},
		})
	}

	// signal that we are ready now
	close(b.ready)

	lastErrorTime := time.Now()
	for {
		err := server.Serve(b.listener)
		if err != nil {
			// comes when the broker is closed with Close() method. This error
			// is defined in net/net.go as "var errClosing", unfortunaly it's
			// not exported.
			if strings.Contains(err.Error(), "use of closed network connection") {
				return
			}

			log.Warning("Server error: %v", err)
			if time.Now().Sub(lastErrorTime) < time.Second {
				log.Fatal(nil)
			}
			lastErrorTime = time.Now()
		}
	}

}

// sockjsSession is called for every client connection and handles all the
// message trafic for a single client connection.
func (b *Broker) sockjsSession(session *sockjs.Session) {
	client, err := NewClient(session, b)
	if err != nil {
		log.Critical("Couldnt create client %v", err)
		return
	}

	sessionGaugeEnd := client.gaugeStart()

	defer sessionGaugeEnd()
	defer client.Close()

	err = client.ControlChannel.Publish(b.Config.AuthAllExchange, "broker.clientConnected", false, false, amqp.Publishing{Body: []byte(client.SocketId)})
	if err != nil {
		log.Critical("Couldnt publish to control channel %v", err)
	}

	sendToClient(session, "broker.connected", client.SocketId)

	for data := range session.ReceiveChan {
		if data == nil || session.Closed {
			break
		}

		client.handleSessionMessage(data)
	}
}

// sendToClient sends the given payload back to the client. It attachs the
// routintKey along with the payload. It closes the session if sending fails.
func sendToClient(session *sockjs.Session, routingKey string, payload interface{}) {
	var message struct {
		RoutingKey string      `json:"routingKey"`
		Payload    interface{} `json:"payload"`
	}
	message.RoutingKey = routingKey
	message.Payload = payload
	if !session.Send(message) {
		session.Close()
		log.Warning("Dropped session because of broker to client buffer overflow. %v", session.Tag)
	}
}
<|endoftext|>