<commit_before>package telebot

import (
	"strings"
	"sync"

	"github.com/pkg/errors"
)

// HandlerFunc represents a handler function, which is
// used to handle actual endpoints.
type HandlerFunc func(Context) error

// Context represents a context of the current event. It stores data
// depending on its type, whether it's a message, callback or whatever.
type Context interface {
	// Message returns stored message if such presented.
	Message() *Message

	// Callback returns stored callback if such presented.
	Callback() *Callback

	// Query returns stored query if such presented.
	Query() *Query

	// InlineResult returns stored inline result if such presented.
	InlineResult() *InlineResult

	// ShippingQuery returns stored shipping query if such presented.
	ShippingQuery() *ShippingQuery

	// PreCheckoutQuery returns stored pre checkout query if such presented.
	PreCheckoutQuery() *PreCheckoutQuery

	// Poll returns stored poll if such presented.
	Poll() *Poll

	// PollAnswer returns stored poll answer if such presented.
	PollAnswer() *PollAnswer

	// ChatMember returns bot's chat member changes.
	ChatMember() *ChatMemberUpdated

	// Migration returns both migration from and to chat IDs.
	Migration() (int64, int64)

	// Sender returns the current recipient, depending on the context type.
	// Returns nil if user is not presented.
	Sender() *User

	// Chat returns the current chat, depending on the context type.
	// Returns nil if chat is not presented.
	Chat() *Chat

	// Recipient combines both Sender and Chat functions. If there is no user
	// the chat will be returned. The native context cannot be without sender,
	// but it is useful in the case when the context created intentionally
	// by the NewContext constructor and have only Chat field inside.
	Recipient() Recipient

	// Text returns the message text, depending on the context type.
	// In the case when no related data presented, returns an empty string.
	Text() string

	// Data returns the current data, depending on the context type.
	// If the context contains command, returns its arguments string.
	// If the context contains payment, returns its payload.
	// In the case when no related data presented, returns an empty string.
	Data() string

	// Args returns a raw slice of command or callback arguments as strings.
	// The message arguments split by space, while the callback's ones by a "|" symbol.
	Args() []string

	// Send sends a message to the current recipient.
	// See Send from bot.go.
	Send(what interface{}, opts ...interface{}) error

	// SendAlbum sends an album to the current recipient.
	// See SendAlbum from bot.go.
	SendAlbum(a Album, opts ...interface{}) error

	// Reply replies to the current message.
	// See Reply from bot.go.
	Reply(what interface{}, opts ...interface{}) error

	// Forward forwards the given message to the current recipient.
	// See Forward from bot.go.
	Forward(msg Editable, opts ...interface{}) error

	// ForwardTo forwards the current message to the given recipient.
	// See Forward from bot.go
	ForwardTo(to Recipient, opts ...interface{}) error

	// Edit edits the current message.
	// See Edit from bot.go.
	Edit(what interface{}, opts ...interface{}) error

	// EditCaption edits the caption of the current message.
	// See EditCaption from bot.go.
	EditCaption(caption string, opts ...interface{}) error

	// EditOrSend edits the current message if the update is callback,
	// otherwise the content is sent to the chat as a separate message.
	EditOrSend(what interface{}, opts ...interface{}) error

	// EditOrReply edits the current message if the update is callback,
	// otherwise the content is replied as a separate message.
	EditOrReply(what interface{}, opts ...interface{}) error

	// Delete removes the current message.
	// See Delete from bot.go.
	Delete() error

	// Notify updates the chat action for the current recipient.
	// See Notify from bot.go.
	Notify(action ChatAction) error

	// Ship replies to the current shipping query.
	// See Ship from bot.go.
	Ship(what ...interface{}) error

	// Accept finalizes the current deal.
	// See Accept from bot.go.
	Accept(errorMessage ...string) error

	// Answer sends a response to the current inline query.
	// See Answer from bot.go.
	Answer(resp *QueryResponse) error

	// Respond sends a response for the current callback query.
	// See Respond from bot.go.
	Respond(resp ...*CallbackResponse) error

	// Get retrieves data from the context.
	Get(key string) interface{}

	// Set saves data in the context.
	Set(key string, val interface{})
}

// nativeContext is a native implementation of the Context interface.
// "context" is taken by context package, maybe there is a better name.
type nativeContext struct {
	b *Bot

	message          *Message
	callback         *Callback
	query            *Query
	inlineResult     *InlineResult
	shippingQuery    *ShippingQuery
	preCheckoutQuery *PreCheckoutQuery
	poll             *Poll
	pollAnswer       *PollAnswer
	myChatMember     *ChatMemberUpdated
	chatMember       *ChatMemberUpdated

	lock  sync.RWMutex
	store map[string]interface{}
}

func (c *nativeContext) ChatMember() *ChatMemberUpdated {
	switch {
	case c.chatMember != nil:
		return c.chatMember
	case c.myChatMember != nil:
		return c.myChatMember
	default:
		return nil
	}
}

func (c *nativeContext) Message() *Message {
	switch {
	case c.message != nil:
		return c.message
	case c.callback != nil:
		return c.callback.Message
	default:
		return nil
	}
}

func (c *nativeContext) Callback() *Callback {
	return c.callback
}

func (c *nativeContext) Query() *Query {
	return c.query
}

func (c *nativeContext) InlineResult() *InlineResult {
	return c.inlineResult
}

func (c *nativeContext) ShippingQuery() *ShippingQuery {
	return c.shippingQuery
}

func (c *nativeContext) PreCheckoutQuery() *PreCheckoutQuery {
	return c.preCheckoutQuery
}

func (c *nativeContext) Poll() *Poll {
	return c.poll
}

func (c *nativeContext) PollAnswer() *PollAnswer {
	return c.pollAnswer
}

func (c *nativeContext) Migration() (int64, int64) {
	return c.message.MigrateFrom, c.message.MigrateTo
}

func (c *nativeContext) Sender() *User {
	switch {
	case c.message != nil:
		return c.message.Sender
	case c.callback != nil:
		return c.callback.Sender
	case c.query != nil:
		return c.query.Sender
	case c.inlineResult != nil:
		return c.inlineResult.Sender
	case c.shippingQuery != nil:
		return c.shippingQuery.Sender
	case c.preCheckoutQuery != nil:
		return c.preCheckoutQuery.Sender
	case c.pollAnswer != nil:
		return c.pollAnswer.Sender
	default:
		return nil
	}
}

func (c *nativeContext) Chat() *Chat {
	switch {
	case c.message != nil:
		return c.message.Chat
	case c.callback != nil:
		return c.callback.Message.Chat
	default:
		return nil
	}
}

func (c *nativeContext) Recipient() Recipient {
	chat := c.Chat()
	if chat != nil {
		return chat
	}
	return c.Sender()
}

func (c *nativeContext) Text() string {
	switch {
	case c.message != nil:
		return c.message.Text
	case c.callback != nil:
		return c.callback.Message.Text
	default:
		return ""
	}
}

func (c *nativeContext) Data() string {
	switch {
	case c.message != nil:
		return c.message.Payload
	case c.callback != nil:
		return c.callback.Data
	case c.query != nil:
		return c.query.Text
	case c.inlineResult != nil:
		return c.inlineResult.Query
	case c.shippingQuery != nil:
		return c.shippingQuery.Payload
	case c.preCheckoutQuery != nil:
		return c.preCheckoutQuery.Payload
	default:
		return ""
	}
}

func (c *nativeContext) Args() []string {
	if c.message != nil {
		payload := strings.Trim(c.message.Payload, " ")
		if payload == "" {
			return nil
		}
		return strings.Split(payload, " ")
	}
	if c.callback != nil {
		return strings.Split(c.callback.Data, "|")
	}
	return nil
}

func (c *nativeContext) Send(what interface{}, opts ...interface{}) error {
	_, err := c.b.Send(c.Recipient(), what, opts...)
	return err
}

func (c *nativeContext) SendAlbum(a Album, opts ...interface{}) error {
	_, err := c.b.SendAlbum(c.Recipient(), a, opts...)
	return err
}

func (c *nativeContext) Reply(what interface{}, opts ...interface{}) error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	_, err := c.b.Reply(msg, what, opts...)
	return err
}

func (c *nativeContext) Forward(msg Editable, opts ...interface{}) error {
	_, err := c.b.Forward(c.Recipient(), msg, opts...)
	return err
}

func (c *nativeContext) ForwardTo(to Recipient, opts ...interface{}) error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	_, err := c.b.Forward(to, msg, opts...)
	return err
}

func (c *nativeContext) Edit(what interface{}, opts ...interface{}) error {
	if c.inlineResult != nil {
		_, err := c.b.Edit(c.inlineResult, what, opts...)
		return err
	}
	if c.callback != nil {
		_, err := c.b.Edit(c.callback, what, opts...)
		return err
	}
	return ErrBadContext
}

func (c *nativeContext) EditCaption(caption string, opts ...interface{}) error {
	clb := c.callback
	if clb == nil || clb.Message == nil {
		return ErrBadContext
	}
	_, err := c.b.EditCaption(clb.Message, caption, opts...)
	return err
}

func (c *nativeContext) EditOrSend(what interface{}, opts ...interface{}) error {
	if c.callback != nil {
		return c.Edit(what, opts...)
	}
	return c.Send(what, opts...)
}

func (c *nativeContext) EditOrReply(what interface{}, opts ...interface{}) error {
	if c.callback != nil {
		return c.Edit(what, opts...)
	}
	return c.Reply(what, opts...)
}

func (c *nativeContext) Delete() error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	return c.b.Delete(msg)
}

func (c *nativeContext) Notify(action ChatAction) error {
	return c.b.Notify(c.Recipient(), action)
}

func (c *nativeContext) Ship(what ...interface{}) error {
	if c.shippingQuery == nil {
		return errors.New("telebot: context shipping query is nil")
	}
	return c.b.Ship(c.shippingQuery, what...)
}

func (c *nativeContext) Accept(errorMessage ...string) error {
	if c.preCheckoutQuery == nil {
		return errors.New("telebot: context pre checkout query is nil")
	}
	return c.b.Accept(c.preCheckoutQuery, errorMessage...)
}

func (c *nativeContext) Answer(resp *QueryResponse) error {
	if c.query == nil {
		return errors.New("telebot: context inline query is nil")
	}
	return c.b.Answer(c.query, resp)
}

func (c *nativeContext) Respond(resp ...*CallbackResponse) error {
	if c.callback == nil {
		return errors.New("telebot: context callback is nil")
	}
	return c.b.Respond(c.callback, resp...)
}

func (c *nativeContext) Set(key string, value interface{}) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.store == nil {
		c.store = make(map[string]interface{})
	}
	c.store[key] = value
}

func (c *nativeContext) Get(key string) interface{} {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.store[key]
}
<commit_msg>context: fix nil dereference in Chat function<commit_after>package telebot

import (
	"strings"
	"sync"

	"github.com/pkg/errors"
)

// HandlerFunc represents a handler function, which is
// used to handle actual endpoints.
type HandlerFunc func(Context) error

// Context represents a context of the current event. It stores data
// depending on its type, whether it's a message, callback or whatever.
type Context interface {
	// Message returns stored message if such presented.
	Message() *Message

	// Callback returns stored callback if such presented.
	Callback() *Callback

	// Query returns stored query if such presented.
	Query() *Query

	// InlineResult returns stored inline result if such presented.
	InlineResult() *InlineResult

	// ShippingQuery returns stored shipping query if such presented.
	ShippingQuery() *ShippingQuery

	// PreCheckoutQuery returns stored pre checkout query if such presented.
	PreCheckoutQuery() *PreCheckoutQuery

	// Poll returns stored poll if such presented.
	Poll() *Poll

	// PollAnswer returns stored poll answer if such presented.
	PollAnswer() *PollAnswer

	// ChatMember returns chat member changes.
	ChatMember() *ChatMemberUpdated

	// Migration returns both migration from and to chat IDs.
	Migration() (int64, int64)

	// Sender returns the current recipient, depending on the context type.
	// Returns nil if user is not presented.
	Sender() *User

	// Chat returns the current chat, depending on the context type.
	// Returns nil if chat is not presented.
	Chat() *Chat

	// Recipient combines both Sender and Chat functions. If there is no user
	// the chat will be returned. The native context cannot be without sender,
	// but it is useful in the case when the context created intentionally
	// by the NewContext constructor and have only Chat field inside.
	Recipient() Recipient

	// Text returns the message text, depending on the context type.
	// In the case when no related data presented, returns an empty string.
	Text() string

	// Data returns the current data, depending on the context type.
	// If the context contains command, returns its arguments string.
	// If the context contains payment, returns its payload.
	// In the case when no related data presented, returns an empty string.
	Data() string

	// Args returns a raw slice of command or callback arguments as strings.
	// The message arguments split by space, while the callback's ones by a "|" symbol.
	Args() []string

	// Send sends a message to the current recipient.
	// See Send from bot.go.
	Send(what interface{}, opts ...interface{}) error

	// SendAlbum sends an album to the current recipient.
	// See SendAlbum from bot.go.
	SendAlbum(a Album, opts ...interface{}) error

	// Reply replies to the current message.
	// See Reply from bot.go.
	Reply(what interface{}, opts ...interface{}) error

	// Forward forwards the given message to the current recipient.
	// See Forward from bot.go.
	Forward(msg Editable, opts ...interface{}) error

	// ForwardTo forwards the current message to the given recipient.
	// See Forward from bot.go
	ForwardTo(to Recipient, opts ...interface{}) error

	// Edit edits the current message.
	// See Edit from bot.go.
	Edit(what interface{}, opts ...interface{}) error

	// EditCaption edits the caption of the current message.
	// See EditCaption from bot.go.
	EditCaption(caption string, opts ...interface{}) error

	// EditOrSend edits the current message if the update is callback,
	// otherwise the content is sent to the chat as a separate message.
	EditOrSend(what interface{}, opts ...interface{}) error

	// EditOrReply edits the current message if the update is callback,
	// otherwise the content is replied as a separate message.
	EditOrReply(what interface{}, opts ...interface{}) error

	// Delete removes the current message.
	// See Delete from bot.go.
	Delete() error

	// Notify updates the chat action for the current recipient.
	// See Notify from bot.go.
	Notify(action ChatAction) error

	// Ship replies to the current shipping query.
	// See Ship from bot.go.
	Ship(what ...interface{}) error

	// Accept finalizes the current deal.
	// See Accept from bot.go.
	Accept(errorMessage ...string) error

	// Answer sends a response to the current inline query.
	// See Answer from bot.go.
	Answer(resp *QueryResponse) error

	// Respond sends a response for the current callback query.
	// See Respond from bot.go.
	Respond(resp ...*CallbackResponse) error

	// Get retrieves data from the context.
	Get(key string) interface{}

	// Set saves data in the context.
	Set(key string, val interface{})
}

// nativeContext is a native implementation of the Context interface.
// "context" is taken by context package, maybe there is a better name.
type nativeContext struct {
	b *Bot

	message          *Message
	callback         *Callback
	query            *Query
	inlineResult     *InlineResult
	shippingQuery    *ShippingQuery
	preCheckoutQuery *PreCheckoutQuery
	poll             *Poll
	pollAnswer       *PollAnswer
	myChatMember     *ChatMemberUpdated
	chatMember       *ChatMemberUpdated

	lock  sync.RWMutex
	store map[string]interface{}
}

func (c *nativeContext) Message() *Message {
	switch {
	case c.message != nil:
		return c.message
	case c.callback != nil:
		return c.callback.Message
	default:
		return nil
	}
}

func (c *nativeContext) Callback() *Callback {
	return c.callback
}

func (c *nativeContext) Query() *Query {
	return c.query
}

func (c *nativeContext) InlineResult() *InlineResult {
	return c.inlineResult
}

func (c *nativeContext) ShippingQuery() *ShippingQuery {
	return c.shippingQuery
}

func (c *nativeContext) PreCheckoutQuery() *PreCheckoutQuery {
	return c.preCheckoutQuery
}

func (c *nativeContext) ChatMember() *ChatMemberUpdated {
	switch {
	case c.chatMember != nil:
		return c.chatMember
	case c.myChatMember != nil:
		return c.myChatMember
	default:
		return nil
	}
}

func (c *nativeContext) Poll() *Poll {
	return c.poll
}

func (c *nativeContext) PollAnswer() *PollAnswer {
	return c.pollAnswer
}

func (c *nativeContext) Migration() (int64, int64) {
	return c.message.MigrateFrom, c.message.MigrateTo
}

func (c *nativeContext) Sender() *User {
	switch {
	case c.message != nil:
		return c.message.Sender
	case c.callback != nil:
		return c.callback.Sender
	case c.query != nil:
		return c.query.Sender
	case c.inlineResult != nil:
		return c.inlineResult.Sender
	case c.shippingQuery != nil:
		return c.shippingQuery.Sender
	case c.preCheckoutQuery != nil:
		return c.preCheckoutQuery.Sender
	case c.pollAnswer != nil:
		return c.pollAnswer.Sender
	default:
		return nil
	}
}

func (c *nativeContext) Chat() *Chat {
	switch {
	case c.message != nil:
		return c.message.Chat
	case c.callback != nil && c.callback.Message != nil:
		return c.callback.Message.Chat
	default:
		return nil
	}
}

func (c *nativeContext) Recipient() Recipient {
	chat := c.Chat()
	if chat != nil {
		return chat
	}
	return c.Sender()
}

func (c *nativeContext) Text() string {
	switch {
	case c.message != nil:
		return c.message.Text
	case c.callback != nil:
		return c.callback.Message.Text
	default:
		return ""
	}
}

func (c *nativeContext) Data() string {
	switch {
	case c.message != nil:
		return c.message.Payload
	case c.callback != nil:
		return c.callback.Data
	case c.query != nil:
		return c.query.Text
	case c.inlineResult != nil:
		return c.inlineResult.Query
	case c.shippingQuery != nil:
		return c.shippingQuery.Payload
	case c.preCheckoutQuery != nil:
		return c.preCheckoutQuery.Payload
	default:
		return ""
	}
}

func (c *nativeContext) Args() []string {
	if c.message != nil {
		payload := strings.Trim(c.message.Payload, " ")
		if payload == "" {
			return nil
		}
		return strings.Split(payload, " ")
	}
	if c.callback != nil {
		return strings.Split(c.callback.Data, "|")
	}
	return nil
}

func (c *nativeContext) Send(what interface{}, opts ...interface{}) error {
	_, err := c.b.Send(c.Recipient(), what, opts...)
	return err
}

func (c *nativeContext) SendAlbum(a Album, opts ...interface{}) error {
	_, err := c.b.SendAlbum(c.Recipient(), a, opts...)
	return err
}

func (c *nativeContext) Reply(what interface{}, opts ...interface{}) error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	_, err := c.b.Reply(msg, what, opts...)
	return err
}

func (c *nativeContext) Forward(msg Editable, opts ...interface{}) error {
	_, err := c.b.Forward(c.Recipient(), msg, opts...)
	return err
}

func (c *nativeContext) ForwardTo(to Recipient, opts ...interface{}) error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	_, err := c.b.Forward(to, msg, opts...)
	return err
}

func (c *nativeContext) Edit(what interface{}, opts ...interface{}) error {
	if c.inlineResult != nil {
		_, err := c.b.Edit(c.inlineResult, what, opts...)
		return err
	}
	if c.callback != nil {
		_, err := c.b.Edit(c.callback, what, opts...)
		return err
	}
	return ErrBadContext
}

func (c *nativeContext) EditCaption(caption string, opts ...interface{}) error {
	clb := c.callback
	if clb == nil || clb.Message == nil {
		return ErrBadContext
	}
	_, err := c.b.EditCaption(clb.Message, caption, opts...)
	return err
}

func (c *nativeContext) EditOrSend(what interface{}, opts ...interface{}) error {
	if c.callback != nil {
		return c.Edit(what, opts...)
	}
	return c.Send(what, opts...)
}

func (c *nativeContext) EditOrReply(what interface{}, opts ...interface{}) error {
	if c.callback != nil {
		return c.Edit(what, opts...)
	}
	return c.Reply(what, opts...)
}

func (c *nativeContext) Delete() error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	return c.b.Delete(msg)
}

func (c *nativeContext) Notify(action ChatAction) error {
	return c.b.Notify(c.Recipient(), action)
}

func (c *nativeContext) Ship(what ...interface{}) error {
	if c.shippingQuery == nil {
		return errors.New("telebot: context shipping query is nil")
	}
	return c.b.Ship(c.shippingQuery, what...)
}

func (c *nativeContext) Accept(errorMessage ...string) error {
	if c.preCheckoutQuery == nil {
		return errors.New("telebot: context pre checkout query is nil")
	}
	return c.b.Accept(c.preCheckoutQuery, errorMessage...)
}

func (c *nativeContext) Answer(resp *QueryResponse) error {
	if c.query == nil {
		return errors.New("telebot: context inline query is nil")
	}
	return c.b.Answer(c.query, resp)
}

func (c *nativeContext) Respond(resp ...*CallbackResponse) error {
	if c.callback == nil {
		return errors.New("telebot: context callback is nil")
	}
	return c.b.Respond(c.callback, resp...)
}

func (c *nativeContext) Set(key string, value interface{}) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.store == nil {
		c.store = make(map[string]interface{})
	}
	c.store[key] = value
}

func (c *nativeContext) Get(key string) interface{} {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.store[key]
}
<|endoftext|>


<commit_before>package echo

import (
	"encoding/json"
	"encoding/xml"
	"io"
	"mime"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"

	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/logger"

	"bytes"

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

type (
	// Context represents context for the current request. It holds request and
	// response objects, path parameters, data and registered handler.
	Context interface {
		netContext.Context
		Request() engine.Request
		Response() engine.Response
		Path() string
		P(int) string
		Param(string) string
		Query(string) string
		Form(string) string
		Set(string, interface{})
		Get(string) interface{}
		Bind(interface{}) error
		Render(int, string, interface{}) error
		HTML(int, string) error
		String(int, string) error
		JSON(int, interface{}) error
		JSONBlob(int, []byte) error
		JSONP(int, string, interface{}) error
		XML(int, interface{}) error
		XMLBlob(int, []byte) error
		File(string) error
		Attachment(io.Reader, string) error
		NoContent(int) error
		Redirect(int, string) error
		Error(err error)
		Handle(Context) error
		Logger() logger.Logger
		Object() *context

		SetFunc(string, interface{})
		GetFunc(string) interface{}
		Funcs() map[string]interface{}
		Reset(engine.Request, engine.Response)
		Fetch(string, interface{}) ([]byte, error)
		SetRenderer(Renderer)
	}

	context struct {
		request  engine.Request
		response engine.Response
		path     string
		pnames   []string
		pvalues  []string
		store    store
		handler  Handler
		echo     *Echo
		funcs    map[string]interface{}
		renderer Renderer
	}

	store map[string]interface{}
)

const (
	indexPage = "index.html"
)

// NewContext creates a Context object.
func NewContext(req engine.Request, res engine.Response, e *Echo) Context {
	return &context{
		request:  req,
		response: res,
		echo:     e,
		pvalues:  make([]string, *e.maxParam),
		store:    make(store),
		handler:  notFoundHandler,
		funcs:    make(map[string]interface{}),
	}
}

func (c *context) Handle(ctx Context) error {
	return c.handler.Handle(ctx)
}

func (c *context) Deadline() (deadline time.Time, ok bool) {
	return
}

func (c *context) Done() <-chan struct{} {
	return nil
}

func (c *context) Err() error {
	return nil
}

func (c *context) Value(key interface{}) interface{} {
	return nil
}

// Request returns *http.Request.
func (c *context) Request() engine.Request {
	return c.request
}

// Response returns *Response.
func (c *context) Response() engine.Response {
	return c.response
}

// Path returns the registered path for the handler.
func (c *context) Path() string {
	return c.path
}

// P returns path parameter by index.
func (c *context) P(i int) (value string) {
	l := len(c.pnames)
	if i < l {
		value = c.pvalues[i]
	}
	return
}

// Param returns path parameter by name.
func (c *context) Param(name string) (value string) {
	l := len(c.pnames)
	for i, n := range c.pnames {
		if n == name && i < l {
			value = c.pvalues[i]
			break
		}
	}
	return
}

// Query returns query parameter by name.
func (c *context) Query(name string) string {
	return c.request.URL().QueryValue(name)
}

// Form returns form parameter by name.
func (c *context) Form(name string) string {
	return c.request.FormValue(name)
}

// Get retrieves data from the context.
func (c *context) Get(key string) interface{} {
	return c.store[key]
}

// Set saves data in the context.
func (c *context) Set(key string, val interface{}) {
	if c.store == nil {
		c.store = make(store)
	}
	c.store[key] = val
}

// Bind binds the request body into specified type `i`. The default binder does
// it based on Content-Type header.
func (c *context) Bind(i interface{}) error {
	return c.echo.binder.Bind(i, c)
}

// Render renders a template with data and sends a text/html response with status
// code. Templates can be registered using `Echo.SetRenderer()`.
func (c *context) Render(code int, name string, data interface{}) (err error) {
	b, err := c.Fetch(name, data)
	if err != nil {
		return
	}
	c.response.Header().Set(ContentType, TextHTMLCharsetUTF8)
	c.response.WriteHeader(code)
	_, err = c.response.Write(b)
	return
}

// HTML sends an HTTP response with status code.
func (c *context) HTML(code int, html string) (err error) {
	c.response.Header().Set(ContentType, TextHTMLCharsetUTF8)
	c.response.WriteHeader(code)
	_, err = c.response.Write([]byte(html))
	return
}

// String sends a string response with status code.
func (c *context) String(code int, s string) (err error) {
	c.response.Header().Set(ContentType, TextPlainCharsetUTF8)
	c.response.WriteHeader(code)
	_, err = c.response.Write([]byte(s))
	return
}

// JSON sends a JSON response with status code.
func (c *context) JSON(code int, i interface{}) (err error) {
	b, err := json.Marshal(i)
	if c.echo.Debug() {
		b, err = json.MarshalIndent(i, "", "  ")
	}
	if err != nil {
		return err
	}
	return c.JSONBlob(code, b)
}

// JSONBlob sends a JSON blob response with status code.
func (c *context) JSONBlob(code int, b []byte) (err error) {
	c.response.Header().Set(ContentType, ApplicationJSONCharsetUTF8)
	c.response.WriteHeader(code)
	_, err = c.response.Write(b)
	return
}

// JSONP sends a JSONP response with status code. It uses `callback` to construct
// the JSONP payload.
func (c *context) JSONP(code int, callback string, i interface{}) (err error) {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}
	c.response.Header().Set(ContentType, ApplicationJavaScriptCharsetUTF8)
	c.response.WriteHeader(code)

	if _, err = c.response.Write([]byte(callback + "(")); err != nil {
		return
	}
	if _, err = c.response.Write(b); err != nil {
		return
	}
	_, err = c.response.Write([]byte(");"))
	return
}

// XML sends an XML response with status code.
func (c *context) XML(code int, i interface{}) (err error) {
	b, err := xml.Marshal(i)
	if c.echo.Debug() {
		b, err = xml.MarshalIndent(i, "", "  ")
	}
	if err != nil {
		return err
	}
	return c.XMLBlob(code, b)
}

// XMLBlob sends a XML blob response with status code.
func (c *context) XMLBlob(code int, b []byte) (err error) {
	c.response.Header().Set(ContentType, ApplicationXMLCharsetUTF8)
	c.response.WriteHeader(code)
	if _, err = c.response.Write([]byte(xml.Header)); err != nil {
		return
	}
	_, err = c.response.Write(b)
	return
}

// File sends a response with the content of the file.
func (c *context) File(file string) error {
	root, file := filepath.Split(file)
	fs := http.Dir(root)
	f, err := fs.Open(file)
	if err != nil {
		return ErrNotFound
	}
	defer f.Close()

	fi, _ := f.Stat()
	if fi.IsDir() {
		file = path.Join(file, "index.html")
		f, err = fs.Open(file)
		if err != nil {
			return ErrNotFound
		}
		fi, _ = f.Stat()
	}

	return ServeContent(c.Request(), c.Response(), f, fi)
}

// Attachment sends a response from `io.Reader` as attachment, prompting client
// to save the file.
func (c *context) Attachment(r io.Reader, name string) (err error) {
	c.response.Header().Set(ContentType, detectContentType(name))
	c.response.Header().Set(ContentDisposition, "attachment; filename="+name)
	c.response.WriteHeader(http.StatusOK)
	_, err = io.Copy(c.response, r)
	return
}

// NoContent sends a response with no body and a status code.
func (c *context) NoContent(code int) error {
	c.response.WriteHeader(code)
	return nil
}

// Redirect redirects the request with status code.
func (c *context) Redirect(code int, url string) error {
	if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
		return ErrInvalidRedirectCode
	}
	c.response.Redirect(url, code)
	return nil
}

// Error invokes the registered HTTP error handler. Generally used by middleware.
func (c *context) Error(err error) {
	c.echo.httpErrorHandler(err, c)
}

// Logger returns the `Logger` instance.
func (c *context) Logger() logger.Logger {
	return c.echo.logger
}

// Object returns the `context` object.
func (c *context) Object() *context {
	return c
}

func ServeContent(req engine.Request, res engine.Response, f http.File, fi os.FileInfo) error {
	res.Header().Set(ContentType, detectContentType(fi.Name()))
	res.Header().Set(LastModified, fi.ModTime().UTC().Format(http.TimeFormat))
	res.WriteHeader(http.StatusOK)
	_, err := io.Copy(res, f)
	return err
}

func detectContentType(name string) (t string) {
	if t = mime.TypeByExtension(filepath.Ext(name)); t == "" {
		t = OctetStream
	}
	return
}

func (c *context) reset(req engine.Request, res engine.Response) {
	c.request = req
	c.response = res
	c.store = nil
	c.funcs = make(map[string]interface{})
	c.renderer = nil
	c.handler = notFoundHandler
}

// Echo returns the `Echo` instance.
func (c *context) Echo() *Echo {
	return c.echo
}

func (c *context) Reset(req engine.Request, res engine.Response) {
	c.reset(req, res)
}

func (c *context) GetFunc(key string) interface{} {
	return c.funcs[key]
}

func (c *context) SetFunc(key string, val interface{}) {
	c.funcs[key] = val
}

func (c *context) Funcs() map[string]interface{} {
	return c.funcs
}

func (c *context) Fetch(name string, data interface{}) (b []byte, err error) {
	if c.renderer == nil {
		if c.echo.renderer == nil {
			return nil, ErrRendererNotRegistered
		}
		c.renderer = c.echo.renderer
	}
	buf := new(bytes.Buffer)
	err = c.renderer.Render(buf, name, data, c)
	if err != nil {
		return
	}
	b = buf.Bytes()
	return
}

// SetRenderer registers an HTML template renderer.
func (c *context) SetRenderer(r Renderer) {
	c.renderer = r
}
<commit_msg>improved<commit_after>package echo

import (
	"encoding/json"
	"encoding/xml"
	"io"
	"mime"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"time"

	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/logger"

	"bytes"

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

type (
	// Context represents context for the current request. It holds request and
	// response objects, path parameters, data and registered handler.
	Context interface {
		netContext.Context
		Request() engine.Request
		Response() engine.Response
		Path() string
		P(int) string
		Param(string) string
		Query(string) string
		Form(string) string
		Set(string, interface{})
		Get(string) interface{}
		Bind(interface{}) error
		Render(int, string, interface{}) error
		HTML(int, string) error
		String(int, string) error
		JSON(int, interface{}) error
		JSONBlob(int, []byte) error
		JSONP(int, string, interface{}) error
		XML(int, interface{}) error
		XMLBlob(int, []byte) error
		File(string) error
		Attachment(io.Reader, string) error
		NoContent(int) error
		Redirect(int, string) error
		Error(err error)
		Handle(Context) error
		Logger() logger.Logger
		Object() *context

		SetFunc(string, interface{})
		GetFunc(string) interface{}
		Funcs() map[string]interface{}
		Reset(engine.Request, engine.Response)
		Fetch(string, interface{}) ([]byte, error)
		SetRenderer(Renderer)
	}

	context struct {
		netContext netContext.Context
		request    engine.Request
		response   engine.Response
		path       string
		pnames     []string
		pvalues    []string
		store      store
		handler    Handler
		echo       *Echo
		funcs      map[string]interface{}
		renderer   Renderer
	}

	store map[string]interface{}
)

const (
	indexPage = "index.html"
)

// NewContext creates a Context object.
func NewContext(req engine.Request, res engine.Response, e *Echo) Context {
	return &context{
		request:  req,
		response: res,
		echo:     e,
		pvalues:  make([]string, *e.maxParam),
		store:    make(store),
		handler:  notFoundHandler,
		funcs:    make(map[string]interface{}),
	}
}

func (c *context) NetContext() netContext.Context {
	return c.netContext
}

func (c *context) SetNetContext(ctx netContext.Context) {
	c.netContext = ctx
}

func (c *context) Deadline() (deadline time.Time, ok bool) {
	return c.netContext.Deadline()
}

func (c *context) Done() <-chan struct{} {
	return c.netContext.Done()
}

func (c *context) Err() error {
	return c.netContext.Err()
}

func (c *context) Value(key interface{}) interface{} {
	return c.netContext.Value(key)
}

func (c *context) Handle(ctx Context) error {
	return c.handler.Handle(ctx)
}

// Request returns *http.Request.
func (c *context) Request() engine.Request {
	return c.request
}

// Response returns *Response.
func (c *context) Response() engine.Response {
	return c.response
}

// Path returns the registered path for the handler.
func (c *context) Path() string {
	return c.path
}

// P returns path parameter by index.
func (c *context) P(i int) (value string) {
	l := len(c.pnames)
	if i < l {
		value = c.pvalues[i]
	}
	return
}

// Param returns path parameter by name.
func (c *context) Param(name string) (value string) {
	l := len(c.pnames)
	for i, n := range c.pnames {
		if n == name && i < l {
			value = c.pvalues[i]
			break
		}
	}
	return
}

// Query returns query parameter by name.
func (c *context) Query(name string) string {
	return c.request.URL().QueryValue(name)
}

// Form returns form parameter by name.
func (c *context) Form(name string) string {
	return c.request.FormValue(name)
}

// Get retrieves data from the context.
func (c *context) Get(key string) interface{} {
	return c.store[key]
}

// Set saves data in the context.
func (c *context) Set(key string, val interface{}) {
	if c.store == nil {
		c.store = make(store)
	}
	c.store[key] = val
}

// Bind binds the request body into specified type `i`. The default binder does
// it based on Content-Type header.
func (c *context) Bind(i interface{}) error {
	return c.echo.binder.Bind(i, c)
}

// Render renders a template with data and sends a text/html response with status
// code. Templates can be registered using `Echo.SetRenderer()`.
func (c *context) Render(code int, name string, data interface{}) (err error) {
	b, err := c.Fetch(name, data)
	if err != nil {
		return
	}
	c.response.Header().Set(ContentType, TextHTMLCharsetUTF8)
	c.response.WriteHeader(code)
	_, err = c.response.Write(b)
	return
}

// HTML sends an HTTP response with status code.
func (c *context) HTML(code int, html string) (err error) {
	c.response.Header().Set(ContentType, TextHTMLCharsetUTF8)
	c.response.WriteHeader(code)
	_, err = c.response.Write([]byte(html))
	return
}

// String sends a string response with status code.
func (c *context) String(code int, s string) (err error) {
	c.response.Header().Set(ContentType, TextPlainCharsetUTF8)
	c.response.WriteHeader(code)
	_, err = c.response.Write([]byte(s))
	return
}

// JSON sends a JSON response with status code.
func (c *context) JSON(code int, i interface{}) (err error) {
	b, err := json.Marshal(i)
	if c.echo.Debug() {
		b, err = json.MarshalIndent(i, "", "  ")
	}
	if err != nil {
		return err
	}
	return c.JSONBlob(code, b)
}

// JSONBlob sends a JSON blob response with status code.
func (c *context) JSONBlob(code int, b []byte) (err error) {
	c.response.Header().Set(ContentType, ApplicationJSONCharsetUTF8)
	c.response.WriteHeader(code)
	_, err = c.response.Write(b)
	return
}

// JSONP sends a JSONP response with status code. It uses `callback` to construct
// the JSONP payload.
func (c *context) JSONP(code int, callback string, i interface{}) (err error) {
	b, err := json.Marshal(i)
	if err != nil {
		return err
	}
	c.response.Header().Set(ContentType, ApplicationJavaScriptCharsetUTF8)
	c.response.WriteHeader(code)

	if _, err = c.response.Write([]byte(callback + "(")); err != nil {
		return
	}
	if _, err = c.response.Write(b); err != nil {
		return
	}
	_, err = c.response.Write([]byte(");"))
	return
}

// XML sends an XML response with status code.
func (c *context) XML(code int, i interface{}) (err error) {
	b, err := xml.Marshal(i)
	if c.echo.Debug() {
		b, err = xml.MarshalIndent(i, "", "  ")
	}
	if err != nil {
		return err
	}
	return c.XMLBlob(code, b)
}

// XMLBlob sends a XML blob response with status code.
func (c *context) XMLBlob(code int, b []byte) (err error) {
	c.response.Header().Set(ContentType, ApplicationXMLCharsetUTF8)
	c.response.WriteHeader(code)
	if _, err = c.response.Write([]byte(xml.Header)); err != nil {
		return
	}
	_, err = c.response.Write(b)
	return
}

// File sends a response with the content of the file.
func (c *context) File(file string) error {
	root, file := filepath.Split(file)
	fs := http.Dir(root)
	f, err := fs.Open(file)
	if err != nil {
		return ErrNotFound
	}
	defer f.Close()

	fi, _ := f.Stat()
	if fi.IsDir() {
		file = path.Join(file, "index.html")
		f, err = fs.Open(file)
		if err != nil {
			return ErrNotFound
		}
		fi, _ = f.Stat()
	}

	return ServeContent(c.Request(), c.Response(), f, fi)
}

// Attachment sends a response from `io.Reader` as attachment, prompting client
// to save the file.
func (c *context) Attachment(r io.Reader, name string) (err error) {
	c.response.Header().Set(ContentType, detectContentType(name))
	c.response.Header().Set(ContentDisposition, "attachment; filename="+name)
	c.response.WriteHeader(http.StatusOK)
	_, err = io.Copy(c.response, r)
	return
}

// NoContent sends a response with no body and a status code.
func (c *context) NoContent(code int) error {
	c.response.WriteHeader(code)
	return nil
}

// Redirect redirects the request with status code.
func (c *context) Redirect(code int, url string) error {
	if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
		return ErrInvalidRedirectCode
	}
	c.response.Redirect(url, code)
	return nil
}

// Error invokes the registered HTTP error handler. Generally used by middleware.
func (c *context) Error(err error) {
	c.echo.httpErrorHandler(err, c)
}

// Logger returns the `Logger` instance.
func (c *context) Logger() logger.Logger {
	return c.echo.logger
}

// Object returns the `context` object.
func (c *context) Object() *context {
	return c
}

func ServeContent(req engine.Request, res engine.Response, f http.File, fi os.FileInfo) error {
	res.Header().Set(ContentType, detectContentType(fi.Name()))
	res.Header().Set(LastModified, fi.ModTime().UTC().Format(http.TimeFormat))
	res.WriteHeader(http.StatusOK)
	_, err := io.Copy(res, f)
	return err
}

func detectContentType(name string) (t string) {
	if t = mime.TypeByExtension(filepath.Ext(name)); t == "" {
		t = OctetStream
	}
	return
}

func (c *context) reset(req engine.Request, res engine.Response) {
	c.netContext = nil
	c.request = req
	c.response = res
	c.store = nil
	c.funcs = make(map[string]interface{})
	c.renderer = nil
	c.handler = notFoundHandler
}

// Echo returns the `Echo` instance.
func (c *context) Echo() *Echo {
	return c.echo
}

func (c *context) Reset(req engine.Request, res engine.Response) {
	c.reset(req, res)
}

func (c *context) GetFunc(key string) interface{} {
	return c.funcs[key]
}

func (c *context) SetFunc(key string, val interface{}) {
	c.funcs[key] = val
}

func (c *context) Funcs() map[string]interface{} {
	return c.funcs
}

func (c *context) Fetch(name string, data interface{}) (b []byte, err error) {
	if c.renderer == nil {
		if c.echo.renderer == nil {
			return nil, ErrRendererNotRegistered
		}
		c.renderer = c.echo.renderer
	}
	buf := new(bytes.Buffer)
	err = c.renderer.Render(buf, name, data, c)
	if err != nil {
		return
	}
	b = buf.Bytes()
	return
}

// SetRenderer registers an HTML template renderer.
func (c *context) SetRenderer(r Renderer) {
	c.renderer = r
}
<|endoftext|>


<commit_before>package app

import (
	"bufio"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"

	"github.com/gowww/fatal"
	"github.com/gowww/i18n"
	"github.com/gowww/router"
	"github.com/gowww/view"
)

// A Context contains the data for a handler.
type Context struct {
	Res http.ResponseWriter
	Req *http.Request
}

// contextHandle wraps the router for setting headers and deferring their write.
func contextHandle(h http.Handler) http.Handler {
	return Handler(func(c *Context) {
		cw := &contextWriter{ResponseWriter: c.Res}
		defer func() {
			if cw.status != 0 {
				c.Res.WriteHeader(cw.status)
			}
		}()
		cw.Header().Set("Connection", "keep-alive")
		h.ServeHTTP(cw, c.Req)
	})
}

// logWriter keeps the status code from WriteHeader to allow setting headers after a Context.Status call.
// Required when using Context.Status with Context.JSON, for example.
type contextWriter struct {
	http.ResponseWriter
	status int
}

func (cw *contextWriter) WriteHeader(status int) {
	cw.status = status
}

func (cw *contextWriter) Write(b []byte) (int, error) {
	if cw.status != 0 {
		cw.ResponseWriter.WriteHeader(cw.status)
		cw.status = 0
	}
	return cw.ResponseWriter.Write(b)
}

// CloseNotify implements the http.CloseNotifier interface.
// No channel is returned if CloseNotify is not implemented by an upstream response writer.
func (cw *contextWriter) CloseNotify() <-chan bool {
	n, ok := cw.ResponseWriter.(http.CloseNotifier)
	if !ok {
		return nil
	}
	return n.CloseNotify()
}

// Flush implements the http.Flusher interface.
// Nothing is done if Flush is not implemented by an upstream response writer.
func (cw *contextWriter) Flush() {
	f, ok := cw.ResponseWriter.(http.Flusher)
	if ok {
		f.Flush()
	}
}

// Hijack implements the http.Hijacker interface.
// Error http.ErrNotSupported is returned if Hijack is not implemented by an upstream response writer.
func (cw *contextWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	h, ok := cw.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, http.ErrNotSupported
	}
	return h.Hijack()
}

// Push implements the http.Pusher interface.
// http.ErrNotSupported is returned if Push is not implemented by an upstream response writer or not supported by the client.
func (cw *contextWriter) Push(target string, opts *http.PushOptions) error {
	p, ok := cw.ResponseWriter.(http.Pusher)
	if !ok {
		return http.ErrNotSupported
	}
	return p.Push(target, opts)
}

// Get returns a context value.
func (c *Context) Get(key interface{}) interface{} {
	return c.Req.Context().Value(key)
}

// Set sets a context value.
func (c *Context) Set(key, val interface{}) {
	c.Req = c.Req.WithContext(context.WithValue(c.Req.Context(), key, val))
}

// PathValue returns the value of path parameter.
func (c *Context) PathValue(key string) string {
	return router.Parameter(c.Req, key)
}

// FormValue gets the form value from the request.
func (c *Context) FormValue(key string) string {
	return c.Req.FormValue(key)
}

// HasFormValue checks if the form value exists in the request.
func (c *Context) HasFormValue(key string) bool {
	return c.Req.FormValue(key) != ""
}

// Write writes the response.
func (c *Context) Write(b []byte) (int, error) {
	return c.Res.Write(b)
}

// Text writes the response with a string.
func (c *Context) Text(s string) {
	c.Write([]byte(s))
}

// Textf writes the response with a formatted string.
func (c *Context) Textf(s string, a ...interface{}) {
	fmt.Fprintf(c.Res, s, a...)
}

// Bytes writes the response with a bytes slice.
func (c *Context) Bytes(b []byte) {
	c.Write(b)
}

// Status sets the HTTP status of the response.
func (c *Context) Status(code int) {
	c.Res.WriteHeader(code)
}

// View writes the response with a rendered view.
// This data is always part of the rendering:
//	.	the GlobalViewData
//	.c	the Context
func (c *Context) View(name string, data ...ViewData) {
	d := view.Data{"c": c}
	for _, dt := range data {
		for k, v := range dt {
			d[k] = v
		}
	}
	err := views.ExecuteTemplate(c, name, d)
	if err != nil {
		log.Println(err)
	}
}

// JSON writes the response with a marshalled JSON.
func (c *Context) JSON(v interface{}) {
	c.Res.Header().Set("Content-Type", "application/json")
	enc := json.NewEncoder(c.Res)
	enc.Encode(v)
}

// Redirect redirects the client to the url with status code.
func (c *Context) Redirect(url string, status int) {
	http.Redirect(c.Res, c.Req, url, status)
}

// Cookie decrypts and returns the named cookie value from the request.
// If cookie is not found, an empty string is returned.
// If multiple cookies match the given name, only one cookie value will be returned.
func (c *Context) Cookie(name string) string {
	ck, _ := c.Req.Cookie(name)
	if ck == nil {
		return ""
	}
	val, err := base64.StdEncoding.DecodeString(ck.Value)
	if err != nil {
		return ck.Value
	}
	if encrypter == nil {
		return string(val)
	}
	ck.Value, err = encrypter.DecryptString(string(val))
	if err != nil {
		return string(val)
	}
	return ck.Value
}

// SetCookie sets an encrypted cookie to the response.
func (c *Context) SetCookie(cookie *http.Cookie) {
	if encrypter != nil {
		var err error
		if cookie.Value, err = encrypter.EncryptString(cookie.Value); err != nil {
			c.Panic(err)
		}
		cookie.Value = base64.StdEncoding.EncodeToString([]byte(cookie.Value))
	}
	http.SetCookie(c.Res, cookie)
}

// DeleteCookie removes a cookie from the client.
func (c *Context) DeleteCookie(name string) {
	http.SetCookie(c.Res, &http.Cookie{Name: name, MaxAge: -1})
}

// T returns the translation associated to key, for the client locale.
func (c *Context) T(key string, a ...interface{}) string {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return fmt.Sprintf("[%v]", key)
	}
	return rt.T(key, a...)
}

// Tn returns the translation associated to key, for the client locale.
// If the translation defines plural forms (zero, one, other), it uses the most appropriate.
// All i18n.TnPlaceholder in the translation are replaced with number n.
// If translation is not found, an empty string is returned.
func (c *Context) Tn(key string, n interface{}, a ...interface{}) string {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return fmt.Sprintf("[%v]", key)
	}
	return rt.Tn(key, n, a...)
}

// THTML works like T but returns an HTML unescaped translation. An "nl2br" function is applied to the result.
func (c *Context) THTML(key string, a ...interface{}) template.HTML {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return template.HTML(fmt.Sprintf("[%v]", key))
	}
	return rt.THTML(key, a...)
}

// TnHTML works like Tn but returns an HTML unescaped translation. An "nl2br" function is applied to the result.
func (c *Context) TnHTML(key string, n interface{}, a ...interface{}) template.HTML {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return template.HTML(fmt.Sprintf("[%v]", key))
	}
	return rt.TnHTML(key, n, a...)
}

// Fmtn returns a formatted number with decimal and thousands marks.
func (c *Context) Fmtn(n interface{}) string {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return fmt.Sprintf("[%v]", n)
	}
	return i18n.Fmtn(rt.Locale, n)
}

// Push initiates an HTTP/2 server push.
// See net/http.Pusher for documentation.
func (c *Context) Push(target string, opts *http.PushOptions) {
	if pusher, ok := c.Res.(http.Pusher); ok {
		pusher.Push(target, opts)
	}
}

// NotFound responds with the "not found" handler.
func (c *Context) NotFound() {
	if rt.NotFoundHandler != nil {
		rt.NotFoundHandler.ServeHTTP(c.Res, c.Req)
	} else {
		http.NotFound(c.Res, c.Req)
	}
}

// Panic logs error with stack trace and responds with the error handler if set.
func (c *Context) Panic(err error) {
	panic(fmt.Errorf("Failed serving %s: %v", c.Req.RemoteAddr, err))

}

// Error returns the error value stored in request's context after a recovering or a Context.Error call.
func (c *Context) Error() error {
	return fmt.Errorf("%v", fatal.Error(c.Req))
}
<commit_msg>Use bas64 methods from encrypter<commit_after>package app

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"html/template"
	"log"
	"net"
	"net/http"

	"github.com/gowww/fatal"
	"github.com/gowww/i18n"
	"github.com/gowww/router"
	"github.com/gowww/view"
)

// A Context contains the data for a handler.
type Context struct {
	Res http.ResponseWriter
	Req *http.Request
}

// contextHandle wraps the router for setting headers and deferring their write.
func contextHandle(h http.Handler) http.Handler {
	return Handler(func(c *Context) {
		cw := &contextWriter{ResponseWriter: c.Res}
		defer func() {
			if cw.status != 0 {
				c.Res.WriteHeader(cw.status)
			}
		}()
		cw.Header().Set("Connection", "keep-alive")
		h.ServeHTTP(cw, c.Req)
	})
}

// logWriter keeps the status code from WriteHeader to allow setting headers after a Context.Status call.
// Required when using Context.Status with Context.JSON, for example.
type contextWriter struct {
	http.ResponseWriter
	status int
}

func (cw *contextWriter) WriteHeader(status int) {
	cw.status = status
}

func (cw *contextWriter) Write(b []byte) (int, error) {
	if cw.status != 0 {
		cw.ResponseWriter.WriteHeader(cw.status)
		cw.status = 0
	}
	return cw.ResponseWriter.Write(b)
}

// CloseNotify implements the http.CloseNotifier interface.
// No channel is returned if CloseNotify is not implemented by an upstream response writer.
func (cw *contextWriter) CloseNotify() <-chan bool {
	n, ok := cw.ResponseWriter.(http.CloseNotifier)
	if !ok {
		return nil
	}
	return n.CloseNotify()
}

// Flush implements the http.Flusher interface.
// Nothing is done if Flush is not implemented by an upstream response writer.
func (cw *contextWriter) Flush() {
	f, ok := cw.ResponseWriter.(http.Flusher)
	if ok {
		f.Flush()
	}
}

// Hijack implements the http.Hijacker interface.
// Error http.ErrNotSupported is returned if Hijack is not implemented by an upstream response writer.
func (cw *contextWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	h, ok := cw.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, http.ErrNotSupported
	}
	return h.Hijack()
}

// Push implements the http.Pusher interface.
// http.ErrNotSupported is returned if Push is not implemented by an upstream response writer or not supported by the client.
func (cw *contextWriter) Push(target string, opts *http.PushOptions) error {
	p, ok := cw.ResponseWriter.(http.Pusher)
	if !ok {
		return http.ErrNotSupported
	}
	return p.Push(target, opts)
}

// Get returns a context value.
func (c *Context) Get(key interface{}) interface{} {
	return c.Req.Context().Value(key)
}

// Set sets a context value.
func (c *Context) Set(key, val interface{}) {
	c.Req = c.Req.WithContext(context.WithValue(c.Req.Context(), key, val))
}

// PathValue returns the value of path parameter.
func (c *Context) PathValue(key string) string {
	return router.Parameter(c.Req, key)
}

// FormValue gets the form value from the request.
func (c *Context) FormValue(key string) string {
	return c.Req.FormValue(key)
}

// HasFormValue checks if the form value exists in the request.
func (c *Context) HasFormValue(key string) bool {
	return c.Req.FormValue(key) != ""
}

// Write writes the response.
func (c *Context) Write(b []byte) (int, error) {
	return c.Res.Write(b)
}

// Text writes the response with a string.
func (c *Context) Text(s string) {
	c.Write([]byte(s))
}

// Textf writes the response with a formatted string.
func (c *Context) Textf(s string, a ...interface{}) {
	fmt.Fprintf(c.Res, s, a...)
}

// Bytes writes the response with a bytes slice.
func (c *Context) Bytes(b []byte) {
	c.Write(b)
}

// Status sets the HTTP status of the response.
func (c *Context) Status(code int) {
	c.Res.WriteHeader(code)
}

// View writes the response with a rendered view.
// This data is always part of the rendering:
//	.	the GlobalViewData
//	.c	the Context
func (c *Context) View(name string, data ...ViewData) {
	d := view.Data{"c": c}
	for _, dt := range data {
		for k, v := range dt {
			d[k] = v
		}
	}
	err := views.ExecuteTemplate(c, name, d)
	if err != nil {
		log.Println(err)
	}
}

// JSON writes the response with a marshalled JSON.
func (c *Context) JSON(v interface{}) {
	c.Res.Header().Set("Content-Type", "application/json")
	enc := json.NewEncoder(c.Res)
	enc.Encode(v)
}

// Redirect redirects the client to the url with status code.
func (c *Context) Redirect(url string, status int) {
	http.Redirect(c.Res, c.Req, url, status)
}

// Cookie decrypts and returns the named cookie value from the request.
// If cookie is not found or the decryption failed, an empty string is returned.
// If multiple cookies match the given name, only one cookie value will be returned.
func (c *Context) Cookie(name string) string {
	ck, _ := c.Req.Cookie(name)
	if ck == nil {
		return ""
	}
	if encrypter == nil {
		return ck.Value
	}
	v, err := encrypter.DecryptBase64(ck.Value)
	if err != nil {
		return ""
	}
	return v
}

// SetCookie sets an encrypted cookie to the response.
func (c *Context) SetCookie(cookie *http.Cookie) {
	if encrypter != nil {
		var err error
		if cookie.Value, err = encrypter.EncryptBase64(cookie.Value); err != nil {
			c.Panic(err)
		}
	}
	http.SetCookie(c.Res, cookie)
}

// DeleteCookie removes a cookie from the client.
func (c *Context) DeleteCookie(name string) {
	http.SetCookie(c.Res, &http.Cookie{Name: name, MaxAge: -1})
}

// T returns the translation associated to key, for the client locale.
func (c *Context) T(key string, a ...interface{}) string {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return fmt.Sprintf("[%v]", key)
	}
	return rt.T(key, a...)
}

// Tn returns the translation associated to key, for the client locale.
// If the translation defines plural forms (zero, one, other), it uses the most appropriate.
// All i18n.TnPlaceholder in the translation are replaced with number n.
// If translation is not found, an empty string is returned.
func (c *Context) Tn(key string, n interface{}, a ...interface{}) string {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return fmt.Sprintf("[%v]", key)
	}
	return rt.Tn(key, n, a...)
}

// THTML works like T but returns an HTML unescaped translation. An "nl2br" function is applied to the result.
func (c *Context) THTML(key string, a ...interface{}) template.HTML {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return template.HTML(fmt.Sprintf("[%v]", key))
	}
	return rt.THTML(key, a...)
}

// TnHTML works like Tn but returns an HTML unescaped translation. An "nl2br" function is applied to the result.
func (c *Context) TnHTML(key string, n interface{}, a ...interface{}) template.HTML {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return template.HTML(fmt.Sprintf("[%v]", key))
	}
	return rt.TnHTML(key, n, a...)
}

// Fmtn returns a formatted number with decimal and thousands marks.
func (c *Context) Fmtn(n interface{}) string {
	rt := i18n.RequestTranslator(c.Req)
	if rt == nil {
		return fmt.Sprintf("[%v]", n)
	}
	return i18n.Fmtn(rt.Locale, n)
}

// Push initiates an HTTP/2 server push.
// See net/http.Pusher for documentation.
func (c *Context) Push(target string, opts *http.PushOptions) {
	if pusher, ok := c.Res.(http.Pusher); ok {
		pusher.Push(target, opts)
	}
}

// NotFound responds with the "not found" handler.
func (c *Context) NotFound() {
	if rt.NotFoundHandler != nil {
		rt.NotFoundHandler.ServeHTTP(c.Res, c.Req)
	} else {
		http.NotFound(c.Res, c.Req)
	}
}

// Panic logs error with stack trace and responds with the error handler if set.
func (c *Context) Panic(err error) {
	panic(fmt.Errorf("Failed serving %s: %v", c.Req.RemoteAddr, err))

}

// Error returns the error value stored in request's context after a recovering or a Context.Error call.
func (c *Context) Error() error {
	return fmt.Errorf("%v", fatal.Error(c.Req))
}
<|endoftext|>


<commit_before>package gg

import (
	"image"
	"image/color"
	"image/draw"
	"math"

	"github.com/golang/freetype/raster"
	"golang.org/x/image/font"
	"golang.org/x/image/font/basicfont"
	"golang.org/x/image/math/fixed"
)

type LineCap int

const (
	LineCapRound LineCap = iota
	LineCapButt
	LineCapSquare
)

type LineJoin int

const (
	LineJoinRound LineJoin = iota
	LineJoinBevel
)

type FillRule int

const (
	FillRuleWinding FillRule = iota
	FillRuleEvenOdd
)

type Context struct {
	width      int
	height     int
	im         *image.RGBA
	color      color.Color
	path       raster.Path
	start      fixed.Point26_6
	lineWidth  float64
	lineCap    LineCap
	lineJoin   LineJoin
	fillRule   FillRule
	fontFace   font.Face
	fontHeight float64
	matrix     Matrix
	stack      []*Context
}

func NewContext(width, height int) *Context {
	return NewContextForRGBA(image.NewRGBA(image.Rect(0, 0, width, height)))
}

func NewContextForImage(im image.Image) *Context {
	return NewContextForRGBA(imageToRGBA(im))
}

func NewContextForRGBA(im *image.RGBA) *Context {
	return &Context{
		width:      im.Bounds().Size().X,
		height:     im.Bounds().Size().Y,
		im:         im,
		color:      color.Transparent,
		lineWidth:  1,
		fillRule:   FillRuleWinding,
		fontFace:   basicfont.Face7x13,
		fontHeight: 13,
		matrix:     Identity(),
	}
}

func (dc *Context) Image() image.Image {
	return dc.im
}

func (dc *Context) Width() int {
	return dc.width
}

func (dc *Context) Height() int {
	return dc.height
}

func (dc *Context) SavePNG(path string) error {
	return SavePNG(path, dc.im)
}

func (dc *Context) SetLineWidth(lineWidth float64) {
	dc.lineWidth = lineWidth
}

func (dc *Context) SetLineCap(lineCap LineCap) {
	dc.lineCap = lineCap
}

func (dc *Context) SetLineCapRound() {
	dc.lineCap = LineCapRound
}

func (dc *Context) SetLineCapButt() {
	dc.lineCap = LineCapButt
}

func (dc *Context) SetLineCapSquare() {
	dc.lineCap = LineCapSquare
}

func (dc *Context) SetLineJoin(lineJoin LineJoin) {
	dc.lineJoin = lineJoin
}

func (dc *Context) SetLineJoinRound() {
	dc.lineJoin = LineJoinRound
}

func (dc *Context) SetLineJoinBevel() {
	dc.lineJoin = LineJoinBevel
}

func (dc *Context) SetFillRule(fillRule FillRule) {
	dc.fillRule = fillRule
}

func (dc *Context) SetFillRuleWinding() {
	dc.fillRule = FillRuleWinding
}

func (dc *Context) SetFillRuleEvenOdd() {
	dc.fillRule = FillRuleEvenOdd
}

// Color Setters

func (dc *Context) SetColor(c color.Color) {
	dc.color = c
}

func (dc *Context) SetHexColor(x string) {
	r, g, b := parseHexColor(x)
	dc.SetRGB255(r, g, b)
}

func (dc *Context) SetRGBA255(r, g, b, a int) {
	dc.color = color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
}

func (dc *Context) SetRGB255(r, g, b int) {
	dc.SetRGBA255(r, g, b, 255)
}

func (dc *Context) SetRGBA(r, g, b, a float64) {
	dc.color = color.NRGBA{
		uint8(r * 255),
		uint8(g * 255),
		uint8(b * 255),
		uint8(a * 255),
	}
}

func (dc *Context) SetRGB(r, g, b float64) {
	dc.SetRGBA(r, g, b, 1)
}

// Path Manipulation

func (dc *Context) MoveTo(x, y float64) {
	x, y = dc.TransformPoint(x, y)
	dc.start = fp(x, y)
	dc.path.Start(dc.start)
}

func (dc *Context) LineTo(x, y float64) {
	x, y = dc.TransformPoint(x, y)
	if len(dc.path) == 0 {
		dc.MoveTo(x, y)
	} else {
		dc.path.Add1(fp(x, y))
	}
}

func (dc *Context) QuadraticTo(x1, y1, x2, y2 float64) {
	x1, y1 = dc.TransformPoint(x1, y1)
	x2, y2 = dc.TransformPoint(x2, y2)
	if len(dc.path) == 0 {
		dc.MoveTo(x1, y1)
	} else {
		dc.path.Add2(fp(x1, y1), fp(x2, y2))
	}
}

func (dc *Context) ClosePath() {
	if len(dc.path) > 0 {
		dc.path.Add1(dc.start)
	}
}

func (dc *Context) ClearPath() {
	dc.path.Clear()
}

// Path Drawing

func (dc *Context) StrokePreserve() {
	var capper raster.Capper
	switch dc.lineCap {
	case LineCapButt:
		capper = raster.ButtCapper
	case LineCapRound:
		capper = raster.RoundCapper
	case LineCapSquare:
		capper = raster.SquareCapper
	}
	var joiner raster.Joiner
	switch dc.lineJoin {
	case LineJoinBevel:
		joiner = raster.BevelJoiner
	case LineJoinRound:
		joiner = raster.RoundJoiner
	}
	painter := raster.NewRGBAPainter(dc.im)
	painter.SetColor(dc.color)
	r := raster.NewRasterizer(dc.width, dc.height)
	r.UseNonZeroWinding = true
	r.AddStroke(dc.path, fi(dc.lineWidth), capper, joiner)
	r.Rasterize(painter)
}

func (dc *Context) Stroke() {
	dc.StrokePreserve()
	dc.ClearPath()
}

func (dc *Context) FillPreserve() {
	// make sure the path is closed
	path := make(raster.Path, len(dc.path))
	copy(path, dc.path)
	path.Add1(dc.start)
	painter := raster.NewRGBAPainter(dc.im)
	painter.SetColor(dc.color)
	r := raster.NewRasterizer(dc.width, dc.height)
	r.UseNonZeroWinding = dc.fillRule == FillRuleWinding
	r.AddPath(path)
	r.Rasterize(painter)
}

func (dc *Context) Fill() {
	dc.FillPreserve()
	dc.ClearPath()
}

// Convenient Drawing Functions

func (dc *Context) Clear() {
	draw.Draw(dc.im, dc.im.Bounds(), image.NewUniform(dc.color), image.ZP, draw.Src)
}

func (dc *Context) DrawLine(x1, y1, x2, y2 float64) {
	dc.MoveTo(x1, y1)
	dc.LineTo(x2, y2)
}

func (dc *Context) DrawRectangle(x, y, w, h float64) {
	dc.MoveTo(x, y)
	dc.LineTo(x+w, y)
	dc.LineTo(x+w, y+h)
	dc.LineTo(x, y+h)
	dc.LineTo(x, y)
}

func (dc *Context) DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64) {
	const n = 16
	for i := 0; i <= n; i++ {
		p1 := float64(i+0) / n
		p2 := float64(i+1) / n
		a1 := angle1 + (angle2-angle1)*p1
		a2 := angle1 + (angle2-angle1)*p2
		x0 := x + rx*math.Cos(a1)
		y0 := y + ry*math.Sin(a1)
		x1 := x + rx*math.Cos(a1+(a2-a1)/2)
		y1 := y + ry*math.Sin(a1+(a2-a1)/2)
		x2 := x + rx*math.Cos(a2)
		y2 := y + ry*math.Sin(a2)
		cx := 2*x1 - x0/2 - x2/2
		cy := 2*y1 - y0/2 - y2/2
		if i == 0 {
			dc.MoveTo(x0, y0)
		}
		dc.QuadraticTo(cx, cy, x2, y2)
	}
}

func (dc *Context) DrawEllipse(x, y, rx, ry float64) {
	dc.DrawEllipticalArc(x, y, rx, ry, 0, 2*math.Pi)
}

func (dc *Context) DrawArc(x, y, r, angle1, angle2 float64) {
	dc.DrawEllipticalArc(x, y, r, r, angle1, angle2)
}

func (dc *Context) DrawCircle(x, y, r float64) {
	dc.DrawEllipticalArc(x, y, r, r, 0, 2*math.Pi)
}

func (dc *Context) DrawImage(im image.Image, x, y int) {
	p := image.Pt(x, y)
	r := image.Rectangle{p, p.Add(im.Bounds().Size())}
	draw.Draw(dc.im, r, im, image.ZP, draw.Over)
}

// Text Functions

func (dc *Context) SetFontFace(fontFace font.Face) {
	dc.fontFace = fontFace
}

func (dc *Context) LoadFontFace(path string, points float64) {
	dc.fontFace = loadFontFace(path, points)
	dc.fontHeight = points * 72 / 96
}

func (dc *Context) DrawString(x, y float64, s string) {
	x, y = dc.TransformPoint(x, y)
	d := &font.Drawer{
		Dst:  dc.im,
		Src:  image.NewUniform(dc.color),
		Face: dc.fontFace,
		Dot:  fp(x, y),
	}
	d.DrawString(s)
}

func (dc *Context) MeasureString(s string) (w, h float64) {
	d := &font.Drawer{
		Dst:  nil,
		Src:  nil,
		Face: dc.fontFace,
	}
	a := d.MeasureString(s)
	return float64(a >> 6), dc.fontHeight
}

// Transformation Matrix Operations

func (dc *Context) Identity() {
	dc.matrix = Identity()
}

func (dc *Context) Translate(x, y float64) {
	dc.matrix = dc.matrix.Translate(x, y)
}

func (dc *Context) Scale(x, y float64) {
	dc.matrix = dc.matrix.Scale(x, y)
}

func (dc *Context) Rotate(angle float64) {
	dc.matrix = dc.matrix.Rotate(angle)
}

func (dc *Context) RotateAbout(angle, x, y float64) {
	dc.matrix = dc.matrix.RotateAbout(angle, x, y)
}

func (dc *Context) Shear(x, y float64) {
	dc.matrix = dc.matrix.Shear(x, y)
}

func (dc *Context) TransformPoint(x, y float64) (tx, ty float64) {
	return dc.matrix.TransformPoint(x, y)
}

// Stack

func (dc *Context) Push() {
	x := *dc
	x.path = make(raster.Path, len(dc.path))
	copy(x.path, dc.path)
	dc.stack = append(dc.stack, &x)
}

func (dc *Context) Pop() {
	s := dc.stack
	x, s := s[len(s)-1], s[:len(s)-1]
	*dc = *x
}
<commit_msg>Push/Pop doesn't affect Path<commit_after>package gg

import (
	"image"
	"image/color"
	"image/draw"
	"math"

	"github.com/golang/freetype/raster"
	"golang.org/x/image/font"
	"golang.org/x/image/font/basicfont"
	"golang.org/x/image/math/fixed"
)

type LineCap int

const (
	LineCapRound LineCap = iota
	LineCapButt
	LineCapSquare
)

type LineJoin int

const (
	LineJoinRound LineJoin = iota
	LineJoinBevel
)

type FillRule int

const (
	FillRuleWinding FillRule = iota
	FillRuleEvenOdd
)

type Context struct {
	width      int
	height     int
	im         *image.RGBA
	color      color.Color
	path       raster.Path
	start      fixed.Point26_6
	lineWidth  float64
	lineCap    LineCap
	lineJoin   LineJoin
	fillRule   FillRule
	fontFace   font.Face
	fontHeight float64
	matrix     Matrix
	stack      []*Context
}

func NewContext(width, height int) *Context {
	return NewContextForRGBA(image.NewRGBA(image.Rect(0, 0, width, height)))
}

func NewContextForImage(im image.Image) *Context {
	return NewContextForRGBA(imageToRGBA(im))
}

func NewContextForRGBA(im *image.RGBA) *Context {
	return &Context{
		width:      im.Bounds().Size().X,
		height:     im.Bounds().Size().Y,
		im:         im,
		color:      color.Transparent,
		lineWidth:  1,
		fillRule:   FillRuleWinding,
		fontFace:   basicfont.Face7x13,
		fontHeight: 13,
		matrix:     Identity(),
	}
}

func (dc *Context) Image() image.Image {
	return dc.im
}

func (dc *Context) Width() int {
	return dc.width
}

func (dc *Context) Height() int {
	return dc.height
}

func (dc *Context) SavePNG(path string) error {
	return SavePNG(path, dc.im)
}

func (dc *Context) SetLineWidth(lineWidth float64) {
	dc.lineWidth = lineWidth
}

func (dc *Context) SetLineCap(lineCap LineCap) {
	dc.lineCap = lineCap
}

func (dc *Context) SetLineCapRound() {
	dc.lineCap = LineCapRound
}

func (dc *Context) SetLineCapButt() {
	dc.lineCap = LineCapButt
}

func (dc *Context) SetLineCapSquare() {
	dc.lineCap = LineCapSquare
}

func (dc *Context) SetLineJoin(lineJoin LineJoin) {
	dc.lineJoin = lineJoin
}

func (dc *Context) SetLineJoinRound() {
	dc.lineJoin = LineJoinRound
}

func (dc *Context) SetLineJoinBevel() {
	dc.lineJoin = LineJoinBevel
}

func (dc *Context) SetFillRule(fillRule FillRule) {
	dc.fillRule = fillRule
}

func (dc *Context) SetFillRuleWinding() {
	dc.fillRule = FillRuleWinding
}

func (dc *Context) SetFillRuleEvenOdd() {
	dc.fillRule = FillRuleEvenOdd
}

// Color Setters

func (dc *Context) SetColor(c color.Color) {
	dc.color = c
}

func (dc *Context) SetHexColor(x string) {
	r, g, b := parseHexColor(x)
	dc.SetRGB255(r, g, b)
}

func (dc *Context) SetRGBA255(r, g, b, a int) {
	dc.color = color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}
}

func (dc *Context) SetRGB255(r, g, b int) {
	dc.SetRGBA255(r, g, b, 255)
}

func (dc *Context) SetRGBA(r, g, b, a float64) {
	dc.color = color.NRGBA{
		uint8(r * 255),
		uint8(g * 255),
		uint8(b * 255),
		uint8(a * 255),
	}
}

func (dc *Context) SetRGB(r, g, b float64) {
	dc.SetRGBA(r, g, b, 1)
}

// Path Manipulation

func (dc *Context) MoveTo(x, y float64) {
	x, y = dc.TransformPoint(x, y)
	dc.start = fp(x, y)
	dc.path.Start(dc.start)
}

func (dc *Context) LineTo(x, y float64) {
	x, y = dc.TransformPoint(x, y)
	if len(dc.path) == 0 {
		dc.MoveTo(x, y)
	} else {
		dc.path.Add1(fp(x, y))
	}
}

func (dc *Context) QuadraticTo(x1, y1, x2, y2 float64) {
	x1, y1 = dc.TransformPoint(x1, y1)
	x2, y2 = dc.TransformPoint(x2, y2)
	if len(dc.path) == 0 {
		dc.MoveTo(x1, y1)
	} else {
		dc.path.Add2(fp(x1, y1), fp(x2, y2))
	}
}

func (dc *Context) ClosePath() {
	if len(dc.path) > 0 {
		dc.path.Add1(dc.start)
	}
}

func (dc *Context) ClearPath() {
	dc.path.Clear()
}

// Path Drawing

func (dc *Context) StrokePreserve() {
	var capper raster.Capper
	switch dc.lineCap {
	case LineCapButt:
		capper = raster.ButtCapper
	case LineCapRound:
		capper = raster.RoundCapper
	case LineCapSquare:
		capper = raster.SquareCapper
	}
	var joiner raster.Joiner
	switch dc.lineJoin {
	case LineJoinBevel:
		joiner = raster.BevelJoiner
	case LineJoinRound:
		joiner = raster.RoundJoiner
	}
	painter := raster.NewRGBAPainter(dc.im)
	painter.SetColor(dc.color)
	r := raster.NewRasterizer(dc.width, dc.height)
	r.UseNonZeroWinding = true
	r.AddStroke(dc.path, fi(dc.lineWidth), capper, joiner)
	r.Rasterize(painter)
}

func (dc *Context) Stroke() {
	dc.StrokePreserve()
	dc.ClearPath()
}

func (dc *Context) FillPreserve() {
	// make sure the path is closed
	path := make(raster.Path, len(dc.path))
	copy(path, dc.path)
	path.Add1(dc.start)
	painter := raster.NewRGBAPainter(dc.im)
	painter.SetColor(dc.color)
	r := raster.NewRasterizer(dc.width, dc.height)
	r.UseNonZeroWinding = dc.fillRule == FillRuleWinding
	r.AddPath(path)
	r.Rasterize(painter)
}

func (dc *Context) Fill() {
	dc.FillPreserve()
	dc.ClearPath()
}

// Convenient Drawing Functions

func (dc *Context) Clear() {
	draw.Draw(dc.im, dc.im.Bounds(), image.NewUniform(dc.color), image.ZP, draw.Src)
}

func (dc *Context) DrawLine(x1, y1, x2, y2 float64) {
	dc.MoveTo(x1, y1)
	dc.LineTo(x2, y2)
}

func (dc *Context) DrawRectangle(x, y, w, h float64) {
	dc.MoveTo(x, y)
	dc.LineTo(x+w, y)
	dc.LineTo(x+w, y+h)
	dc.LineTo(x, y+h)
	dc.LineTo(x, y)
}

func (dc *Context) DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64) {
	const n = 16
	for i := 0; i <= n; i++ {
		p1 := float64(i+0) / n
		p2 := float64(i+1) / n
		a1 := angle1 + (angle2-angle1)*p1
		a2 := angle1 + (angle2-angle1)*p2
		x0 := x + rx*math.Cos(a1)
		y0 := y + ry*math.Sin(a1)
		x1 := x + rx*math.Cos(a1+(a2-a1)/2)
		y1 := y + ry*math.Sin(a1+(a2-a1)/2)
		x2 := x + rx*math.Cos(a2)
		y2 := y + ry*math.Sin(a2)
		cx := 2*x1 - x0/2 - x2/2
		cy := 2*y1 - y0/2 - y2/2
		if i == 0 {
			dc.MoveTo(x0, y0)
		}
		dc.QuadraticTo(cx, cy, x2, y2)
	}
}

func (dc *Context) DrawEllipse(x, y, rx, ry float64) {
	dc.DrawEllipticalArc(x, y, rx, ry, 0, 2*math.Pi)
}

func (dc *Context) DrawArc(x, y, r, angle1, angle2 float64) {
	dc.DrawEllipticalArc(x, y, r, r, angle1, angle2)
}

func (dc *Context) DrawCircle(x, y, r float64) {
	dc.DrawEllipticalArc(x, y, r, r, 0, 2*math.Pi)
}

func (dc *Context) DrawImage(im image.Image, x, y int) {
	p := image.Pt(x, y)
	r := image.Rectangle{p, p.Add(im.Bounds().Size())}
	draw.Draw(dc.im, r, im, image.ZP, draw.Over)
}

// Text Functions

func (dc *Context) SetFontFace(fontFace font.Face) {
	dc.fontFace = fontFace
}

func (dc *Context) LoadFontFace(path string, points float64) {
	dc.fontFace = loadFontFace(path, points)
	dc.fontHeight = points * 72 / 96
}

func (dc *Context) DrawString(x, y float64, s string) {
	x, y = dc.TransformPoint(x, y)
	d := &font.Drawer{
		Dst:  dc.im,
		Src:  image.NewUniform(dc.color),
		Face: dc.fontFace,
		Dot:  fp(x, y),
	}
	d.DrawString(s)
}

func (dc *Context) MeasureString(s string) (w, h float64) {
	d := &font.Drawer{
		Dst:  nil,
		Src:  nil,
		Face: dc.fontFace,
	}
	a := d.MeasureString(s)
	return float64(a >> 6), dc.fontHeight
}

// Transformation Matrix Operations

func (dc *Context) Identity() {
	dc.matrix = Identity()
}

func (dc *Context) Translate(x, y float64) {
	dc.matrix = dc.matrix.Translate(x, y)
}

func (dc *Context) Scale(x, y float64) {
	dc.matrix = dc.matrix.Scale(x, y)
}

func (dc *Context) Rotate(angle float64) {
	dc.matrix = dc.matrix.Rotate(angle)
}

func (dc *Context) RotateAbout(angle, x, y float64) {
	dc.matrix = dc.matrix.RotateAbout(angle, x, y)
}

func (dc *Context) Shear(x, y float64) {
	dc.matrix = dc.matrix.Shear(x, y)
}

func (dc *Context) TransformPoint(x, y float64) (tx, ty float64) {
	return dc.matrix.TransformPoint(x, y)
}

// Stack

func (dc *Context) Push() {
	x := *dc
	dc.stack = append(dc.stack, &x)
}

func (dc *Context) Pop() {
	before := *dc
	s := dc.stack
	x, s := s[len(s)-1], s[:len(s)-1]
	*dc = *x
	dc.path = before.path
}
<|endoftext|>


<commit_before>package endly

import (
	"fmt"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/data"
	"github.com/viant/toolbox/storage"
	"github.com/viant/toolbox/url"
	"math/rand"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var converter = toolbox.NewColumnConverter("yyyy-MM-dd HH:ss")

var serviceManagerKey = (*manager)(nil)
var deferFunctionsKey = (*[]func())(nil)
var workflowKey = (*Workflow)(nil)

//Context represents a workflow session context/state
type Context struct {
	SessionID string
	state     data.Map
	toolbox.Context
	Events      *Events
	EventLogger *EventLogger
	Workflows   *Workflows
	cloned      []*Context
	closed      int32
}

//IsClosed returns true if it is closed.
func (c *Context) IsClosed() bool {
	return atomic.LoadInt32(&c.closed) == 1
}

func reportError(err error) error {
	fileName, funcName, line := toolbox.CallerInfo(4)
	return fmt.Errorf("%v at %v:%v -> %v", err, fileName, line, funcName)
}

//Clone clones the context.
func (c *Context) Clone() *Context {
	if len(c.cloned) == 0 {
		c.cloned = make([]*Context, 0)
	}
	result := &Context{}
	result.Context = c.Context.Clone()
	result.Events = c.Events
	result.state = NewDefaultState()
	result.state.Apply(c.state)
	result.SessionID = c.SessionID
	result.Workflows = c.Workflows
	result.EventLogger = c.EventLogger
	c.cloned = append(c.cloned, result)
	return result
}

func (c *Context) parentURLCandidates() []string {
	var result = make([]string, 0)
	if workflow := c.Workflow(); workflow != nil && workflow.Source != nil {
		baseURL, _ := toolbox.URLSplit(workflow.Source.URL)
		result = append(result, baseURL)
	}
	currentDirectory, err := os.Getwd()
	if err == nil {
		result = append(result, toolbox.FileSchema+currentDirectory)
	}

	return result
}

//ExpandResource substitutes any $ expression with the key value from the state map if it is present.
func (c *Context) ExpandResource(resource *url.Resource) (*url.Resource, error) {
	if resource == nil {
		return nil, reportError(fmt.Errorf("Resource was empty"))
	}
	if resource.URL == "" {
		return nil, reportError(fmt.Errorf("URL was empty"))
	}

	if !strings.Contains(resource.URL, "://") {
		for _, parentCandidate := range c.parentURLCandidates() {
			service, err := storage.NewServiceForURL(parentCandidate, "")
			if err != nil {
				continue
			}
			var candidateURL = toolbox.URLPathJoin(parentCandidate, resource.URL)
			if exists, err := service.Exists(candidateURL); exists && err == nil {
				resource.URL = candidateURL
			}
		}
	}
	var result = url.NewResource(c.Expand(resource.URL), c.Expand(resource.Credential))
	if result.ParsedURL == nil {
		return nil, fmt.Errorf("Failed to parse URL %v", result.URL)
	}
	result.Name = c.Expand(resource.Name)
	result.Type = c.Expand(resource.Type)
	result.Cache = c.Expand(resource.Cache)
	result.CacheExpiryMs = resource.CacheExpiryMs
	return result, nil
}

//Manager returns workflow manager or error
func (c *Context) Manager() (Manager, error) {
	var manager = &manager{}
	if !c.GetInto(serviceManagerKey, &manager) {
		return nil, reportError(fmt.Errorf("Failed to lookup Manager"))
	}
	return manager, nil
}

//TerminalSessions returns client sessions
func (c *Context) TerminalSessions() SystemTerminalSessions {
	var result *SystemTerminalSessions
	if !c.Contains(systemTerminalSessionsKey) {
		var sessions SystemTerminalSessions = make(map[string]*SystemTerminalSession)
		result = &sessions
		c.Put(systemTerminalSessionsKey, result)
	} else {
		c.GetInto(systemTerminalSessionsKey, &result)
	}
	return *result
}

//TerminalSessions returns client sessions
func (c *Context) SeleniumSessions() SeleniumSessions {
	var result *SeleniumSessions
	if !c.Contains(systemTerminalSessionsKey) {
		var sessions SeleniumSessions = make(map[string]*SeleniumSession)
		result = &sessions
		c.Put(seleniumSessionsKey, result)
	} else {
		c.GetInto(seleniumSessionsKey, &result)
	}
	return *result
}

//Service returns a service fo provided id or error.
func (c *Context) Service(name string) (Service, error) {
	manager, err := c.Manager()
	if err != nil {
		return nil, err
	}
	return manager.Service(name)
}

//Deffer add function to be executed if context closes. If returns currently registered functions.
func (c *Context) Deffer(functions ...func()) []func() {
	var result *[]func()
	if !c.Contains(deferFunctionsKey) {
		var functions = make([]func(), 0)
		result = &functions
		c.Put(deferFunctionsKey, result)
	} else {
		c.GetInto(deferFunctionsKey, &result)
	}

	*result = append(*result, functions...)
	c.Put(deferFunctionsKey, &result)
	return *result
}

//State returns a context state map.
func (c *Context) State() data.Map {
	if c.state == nil {
		c.state = NewDefaultState()
	}
	return c.state
}

//SetState sets a new state map
func (c *Context) SetState(state data.Map) {
	c.state = state
}

//Workflow returns the master workflow
func (c *Context) Workflow() *Workflow {
	var result *Workflow
	if !c.Contains(workflowKey) {
		return nil
	}
	c.GetInto(workflowKey, &result)
	return result
}

//OperatingSystem returns operating system for provide session
func (c *Context) OperatingSystem(sessionName string) *OperatingSystem {
	var sessions = c.TerminalSessions()

	if session, has := sessions[sessionName]; has {
		return session.OperatingSystem
	}
	return nil
}

//ExecuteAsSuperUser executes provided command as super user.
func (c *Context) ExecuteAsSuperUser(target *url.Resource, command *ManagedCommand) (*CommandResponse, error) {
	superUserRequest := superUserCommandRequest{
		Target:        target,
		MangedCommand: command,
	}
	request, err := superUserRequest.AsCommandRequest(c)
	if err != nil {
		return nil, err
	}
	return c.Execute(target, request.ManagedCommand)
}

func (c *Context) TerminalSession(target *url.Resource) (*SystemTerminalSession, error) {
	sessions := c.TerminalSessions()
	execService, err := c.Service(ExecServiceID)
	if err != nil {
		return nil, err
	}
	if !sessions.Has(target.Host()) {
		response := execService.Run(c, &OpenSessionRequest{
			Target: target,
		})
		if response.Error == "" {
			return nil, errors.New(response.Error)
		}
	}
	return sessions[target.Host()], nil
}

//Execute execute shell command
func (c *Context) Execute(target *url.Resource, command interface{}) (*CommandResponse, error) {
	if command == nil {
		return nil, nil
	}
	var err error
	var commandRequest *ManagedCommandRequest
	switch actualCommand := command.(type) {
	case *superUserCommandRequest:
		actualCommand.Target = target
		commandRequest, err = actualCommand.AsCommandRequest(c)
		if err != nil {
			return nil, err
		}
	case *CommandRequest:
		actualCommand.Target = target
		commandRequest = actualCommand.AsManagedCommandRequest()
	case *ManagedCommand:
		commandRequest = NewManagedCommandRequest(target, actualCommand)
	case string:
		request := CommandRequest{
			Target:   target,
			Commands: []string{actualCommand},
		}
		commandRequest = request.AsManagedCommandRequest()
	case []string:
		request := CommandRequest{
			Target:   target,
			Commands: actualCommand,
		}
		commandRequest = request.AsManagedCommandRequest()

	default:
		return nil, fmt.Errorf("Unsupported command: %T", command)
	}
	execService, err := c.Service(ExecServiceID)
	if err != nil {
		return nil, err
	}
	response := execService.Run(c, commandRequest)
	if response.Error != "" {
		return nil, errors.New(response.Error)
	}
	if commandResult, ok := response.Response.(*CommandResponse); ok {
		return commandResult, nil
	}
	return nil, nil
}

//Copy transfer source into target url, it takes also exand flag to indicate variable substitution.
func (c *Context) Copy(expand bool, source, target *url.Resource) (interface{}, error) {
	return c.Transfer([]*Transfer{{
		Source: source,
		Target: target,
		Expand: expand}}...)
}

//Transfer transfer data for provided transfer definition.
func (c *Context) Transfer(transfers ...*Transfer) (interface{}, error) {
	if transfers == nil {
		return nil, nil
	}
	transferService, err := c.Service(TransferServiceID)
	if err != nil {
		return nil, err
	}
	response := transferService.Run(c, &TransferCopyRequest{Transfers: transfers})
	if response.Error != "" {
		return nil, errors.New(response.Error)
	}
	return nil, nil
}

//Expand substitute $ expression if present in the text and state map.
func (c *Context) Expand(text string) string {
	state := c.State()
	return state.ExpandAsText(text)
}

//AsRequest converts a source map into request for provided service and action.
func (c *Context) AsRequest(serviceName, action string, source map[string]interface{}) (interface{}, error) {
	service, err := c.Service(serviceName)
	if err != nil {
		return nil, err
	}
	request, err := service.NewRequest(action)
	if err != nil {
		return nil, err
	}
	err = converter.AssignConverted(request, source)
	return request, err
}

//Close closes this context, it executes all deferred function and set closed flag.
func (c *Context) Close() {
	atomic.StoreInt32(&c.closed, 1)
	for _, context := range c.cloned {
		context.Close()
	}
	for _, function := range c.Deffer() {
		function()
	}
}

//MakeAsyncSafe makes this contex async safe
func (c *Context) MakeAsyncSafe() {
	c.Context.Remove(seleniumSessionsKey)
	c.Context.Remove(systemTerminalSessionsKey)
	c.Events = &Events{
		mutex:  &sync.Mutex{},
		Events: make([]*Event, 0),
	}
	c.EventLogger = nil
}

/*
NewDefaultState returns a new default state.
It comes with the following registered keys:
	* rand - random int64
	*  date -  current date formatted as yyyy-MM-dd
	* time - current time formatted as yyyy-MM-dd hh:mm:ss
	* ts - current timestamp formatted  as yyyyMMddhhmmSSS
	* timestamp.yesterday - timestamp in ms
	* timestamp.now - timestamp in ms
	* timestamp.tomorrow - timestamp in ms
	* tmpDir - temp directory
	* uuid.next - generate unique id
	* uuid.get - returns previously generated unique id, or generate new
	*.end.XXX where XXX is the Id of the env variable to return
	* all UFD registry functions
*/
func NewDefaultState() data.Map {
	var result = data.NewMap()
	var now = time.Now()
	source := rand.NewSource(now.UnixNano())
	result.Put("rand", source.Int63())
	result.Put("date", now.Format(toolbox.DateFormatToLayout("yyyy-MM-dd")))
	result.Put("time", now.Format(toolbox.DateFormatToLayout("yyyy-MM-dd hh:mm:ss")))
	result.Put("ts", now.Format(toolbox.DateFormatToLayout("yyyyMMddhhmmSSS")))

	result.Put("tmpDir", func(key string) interface{} {
		tempPath := path.Join(os.TempDir(), key)
		exec.Command("mkdir -p " + tempPath)
		return tempPath
	})

	var cachedUUID uuid.UUID
	result.Put("uuid", func(key string) interface{} {
		if key == "next" {
			cachedUUID = uuid.NewV4()
		}
		if len(cachedUUID) > 0 {
			return cachedUUID.String()
		}
		return ""
	})

	result.Put("timestamp", func(key string) interface{} {
		var timeDiffProvider = toolbox.NewTimeDiffProvider()
		switch key {
		case "now":
			result, _ := timeDiffProvider.Get(nil, "now", 0, "day", "timestamp")
			return result
		case "tomorrow":
			result, _ := timeDiffProvider.Get(nil, "now", 1, "day", "timestamp")
			return result
		case "yesterday":
			result, _ := timeDiffProvider.Get(nil, "now", -1, "day", "timestamp")
			return result
		}
		return nil
	})

	result.Put("env", func(key string) interface{} {
		return os.Getenv(key)
	})

	for k, v := range UdfRegistry {
		result.Put(k, v)
	}

	return result
}
<commit_msg>patched TerminalSession<commit_after>package endly

import (
	"fmt"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/data"
	"github.com/viant/toolbox/storage"
	"github.com/viant/toolbox/url"
	"math/rand"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var converter = toolbox.NewColumnConverter("yyyy-MM-dd HH:ss")

var serviceManagerKey = (*manager)(nil)
var deferFunctionsKey = (*[]func())(nil)
var workflowKey = (*Workflow)(nil)

//Context represents a workflow session context/state
type Context struct {
	SessionID string
	state     data.Map
	toolbox.Context
	Events      *Events
	EventLogger *EventLogger
	Workflows   *Workflows
	cloned      []*Context
	closed      int32
}

//IsClosed returns true if it is closed.
func (c *Context) IsClosed() bool {
	return atomic.LoadInt32(&c.closed) == 1
}

func reportError(err error) error {
	fileName, funcName, line := toolbox.CallerInfo(4)
	return fmt.Errorf("%v at %v:%v -> %v", err, fileName, line, funcName)
}

//Clone clones the context.
func (c *Context) Clone() *Context {
	if len(c.cloned) == 0 {
		c.cloned = make([]*Context, 0)
	}
	result := &Context{}
	result.Context = c.Context.Clone()
	result.Events = c.Events
	result.state = NewDefaultState()
	result.state.Apply(c.state)
	result.SessionID = c.SessionID
	result.Workflows = c.Workflows
	result.EventLogger = c.EventLogger
	c.cloned = append(c.cloned, result)
	return result
}

func (c *Context) parentURLCandidates() []string {
	var result = make([]string, 0)
	if workflow := c.Workflow(); workflow != nil && workflow.Source != nil {
		baseURL, _ := toolbox.URLSplit(workflow.Source.URL)
		result = append(result, baseURL)
	}
	currentDirectory, err := os.Getwd()
	if err == nil {
		result = append(result, toolbox.FileSchema+currentDirectory)
	}

	return result
}

//ExpandResource substitutes any $ expression with the key value from the state map if it is present.
func (c *Context) ExpandResource(resource *url.Resource) (*url.Resource, error) {
	if resource == nil {
		return nil, reportError(fmt.Errorf("Resource was empty"))
	}
	if resource.URL == "" {
		return nil, reportError(fmt.Errorf("URL was empty"))
	}

	if !strings.Contains(resource.URL, "://") {
		for _, parentCandidate := range c.parentURLCandidates() {
			service, err := storage.NewServiceForURL(parentCandidate, "")
			if err != nil {
				continue
			}
			var candidateURL = toolbox.URLPathJoin(parentCandidate, resource.URL)
			if exists, err := service.Exists(candidateURL); exists && err == nil {
				resource.URL = candidateURL
			}
		}
	}
	var result = url.NewResource(c.Expand(resource.URL), c.Expand(resource.Credential))
	if result.ParsedURL == nil {
		return nil, fmt.Errorf("Failed to parse URL %v", result.URL)
	}
	result.Name = c.Expand(resource.Name)
	result.Type = c.Expand(resource.Type)
	result.Cache = c.Expand(resource.Cache)
	result.CacheExpiryMs = resource.CacheExpiryMs
	return result, nil
}

//Manager returns workflow manager or error
func (c *Context) Manager() (Manager, error) {
	var manager = &manager{}
	if !c.GetInto(serviceManagerKey, &manager) {
		return nil, reportError(fmt.Errorf("Failed to lookup Manager"))
	}
	return manager, nil
}

//TerminalSessions returns client sessions
func (c *Context) TerminalSessions() SystemTerminalSessions {
	var result *SystemTerminalSessions
	if !c.Contains(systemTerminalSessionsKey) {
		var sessions SystemTerminalSessions = make(map[string]*SystemTerminalSession)
		result = &sessions
		c.Put(systemTerminalSessionsKey, result)
	} else {
		c.GetInto(systemTerminalSessionsKey, &result)
	}
	return *result
}

//TerminalSessions returns client sessions
func (c *Context) SeleniumSessions() SeleniumSessions {
	var result *SeleniumSessions
	if !c.Contains(systemTerminalSessionsKey) {
		var sessions SeleniumSessions = make(map[string]*SeleniumSession)
		result = &sessions
		c.Put(seleniumSessionsKey, result)
	} else {
		c.GetInto(seleniumSessionsKey, &result)
	}
	return *result
}

//Service returns a service fo provided id or error.
func (c *Context) Service(name string) (Service, error) {
	manager, err := c.Manager()
	if err != nil {
		return nil, err
	}
	return manager.Service(name)
}

//Deffer add function to be executed if context closes. If returns currently registered functions.
func (c *Context) Deffer(functions ...func()) []func() {
	var result *[]func()
	if !c.Contains(deferFunctionsKey) {
		var functions = make([]func(), 0)
		result = &functions
		c.Put(deferFunctionsKey, result)
	} else {
		c.GetInto(deferFunctionsKey, &result)
	}

	*result = append(*result, functions...)
	c.Put(deferFunctionsKey, &result)
	return *result
}

//State returns a context state map.
func (c *Context) State() data.Map {
	if c.state == nil {
		c.state = NewDefaultState()
	}
	return c.state
}

//SetState sets a new state map
func (c *Context) SetState(state data.Map) {
	c.state = state
}

//Workflow returns the master workflow
func (c *Context) Workflow() *Workflow {
	var result *Workflow
	if !c.Contains(workflowKey) {
		return nil
	}
	c.GetInto(workflowKey, &result)
	return result
}

//OperatingSystem returns operating system for provide session
func (c *Context) OperatingSystem(sessionName string) *OperatingSystem {
	var sessions = c.TerminalSessions()

	if session, has := sessions[sessionName]; has {
		return session.OperatingSystem
	}
	return nil
}

//ExecuteAsSuperUser executes provided command as super user.
func (c *Context) ExecuteAsSuperUser(target *url.Resource, command *ManagedCommand) (*CommandResponse, error) {
	superUserRequest := superUserCommandRequest{
		Target:        target,
		MangedCommand: command,
	}
	request, err := superUserRequest.AsCommandRequest(c)
	if err != nil {
		return nil, err
	}
	return c.Execute(target, request.ManagedCommand)
}

func (c *Context) TerminalSession(target *url.Resource) (*SystemTerminalSession, error) {
	sessions := c.TerminalSessions()
	execService, err := c.Service(ExecServiceID)
	if err != nil {
		return nil, err
	}
	if !sessions.Has(target.Host()) {
		response := execService.Run(c, &OpenSessionRequest{
			Target: target,
		})
		if response.Error != "" {
			return nil, errors.New(response.Error)
		}
	}
	return sessions[target.Host()], nil
}

//Execute execute shell command
func (c *Context) Execute(target *url.Resource, command interface{}) (*CommandResponse, error) {
	if command == nil {
		return nil, nil
	}
	var err error
	var commandRequest *ManagedCommandRequest
	switch actualCommand := command.(type) {
	case *superUserCommandRequest:
		actualCommand.Target = target
		commandRequest, err = actualCommand.AsCommandRequest(c)
		if err != nil {
			return nil, err
		}
	case *CommandRequest:
		actualCommand.Target = target
		commandRequest = actualCommand.AsManagedCommandRequest()
	case *ManagedCommand:
		commandRequest = NewManagedCommandRequest(target, actualCommand)
	case string:
		request := CommandRequest{
			Target:   target,
			Commands: []string{actualCommand},
		}
		commandRequest = request.AsManagedCommandRequest()
	case []string:
		request := CommandRequest{
			Target:   target,
			Commands: actualCommand,
		}
		commandRequest = request.AsManagedCommandRequest()

	default:
		return nil, fmt.Errorf("Unsupported command: %T", command)
	}
	execService, err := c.Service(ExecServiceID)
	if err != nil {
		return nil, err
	}
	response := execService.Run(c, commandRequest)
	if response.Error != "" {
		return nil, errors.New(response.Error)
	}
	if commandResult, ok := response.Response.(*CommandResponse); ok {
		return commandResult, nil
	}
	return nil, nil
}

//Copy transfer source into target url, it takes also exand flag to indicate variable substitution.
func (c *Context) Copy(expand bool, source, target *url.Resource) (interface{}, error) {
	return c.Transfer([]*Transfer{{
		Source: source,
		Target: target,
		Expand: expand}}...)
}

//Transfer transfer data for provided transfer definition.
func (c *Context) Transfer(transfers ...*Transfer) (interface{}, error) {
	if transfers == nil {
		return nil, nil
	}
	transferService, err := c.Service(TransferServiceID)
	if err != nil {
		return nil, err
	}
	response := transferService.Run(c, &TransferCopyRequest{Transfers: transfers})
	if response.Error != "" {
		return nil, errors.New(response.Error)
	}
	return nil, nil
}

//Expand substitute $ expression if present in the text and state map.
func (c *Context) Expand(text string) string {
	state := c.State()
	return state.ExpandAsText(text)
}

//AsRequest converts a source map into request for provided service and action.
func (c *Context) AsRequest(serviceName, action string, source map[string]interface{}) (interface{}, error) {
	service, err := c.Service(serviceName)
	if err != nil {
		return nil, err
	}
	request, err := service.NewRequest(action)
	if err != nil {
		return nil, err
	}
	err = converter.AssignConverted(request, source)
	return request, err
}

//Close closes this context, it executes all deferred function and set closed flag.
func (c *Context) Close() {
	atomic.StoreInt32(&c.closed, 1)
	for _, context := range c.cloned {
		context.Close()
	}
	for _, function := range c.Deffer() {
		function()
	}
}

//MakeAsyncSafe makes this contex async safe
func (c *Context) MakeAsyncSafe() {
	c.Context.Remove(seleniumSessionsKey)
	c.Context.Remove(systemTerminalSessionsKey)
	c.Events = &Events{
		mutex:  &sync.Mutex{},
		Events: make([]*Event, 0),
	}
	c.EventLogger = nil
}

/*
NewDefaultState returns a new default state.
It comes with the following registered keys:
	* rand - random int64
	*  date -  current date formatted as yyyy-MM-dd
	* time - current time formatted as yyyy-MM-dd hh:mm:ss
	* ts - current timestamp formatted  as yyyyMMddhhmmSSS
	* timestamp.yesterday - timestamp in ms
	* timestamp.now - timestamp in ms
	* timestamp.tomorrow - timestamp in ms
	* tmpDir - temp directory
	* uuid.next - generate unique id
	* uuid.get - returns previously generated unique id, or generate new
	*.end.XXX where XXX is the Id of the env variable to return
	* all UFD registry functions
*/
func NewDefaultState() data.Map {
	var result = data.NewMap()
	var now = time.Now()
	source := rand.NewSource(now.UnixNano())
	result.Put("rand", source.Int63())
	result.Put("date", now.Format(toolbox.DateFormatToLayout("yyyy-MM-dd")))
	result.Put("time", now.Format(toolbox.DateFormatToLayout("yyyy-MM-dd hh:mm:ss")))
	result.Put("ts", now.Format(toolbox.DateFormatToLayout("yyyyMMddhhmmSSS")))

	result.Put("tmpDir", func(key string) interface{} {
		tempPath := path.Join(os.TempDir(), key)
		exec.Command("mkdir -p " + tempPath)
		return tempPath
	})

	var cachedUUID uuid.UUID
	result.Put("uuid", func(key string) interface{} {
		if key == "next" {
			cachedUUID = uuid.NewV4()
		}
		if len(cachedUUID) > 0 {
			return cachedUUID.String()
		}
		return ""
	})

	result.Put("timestamp", func(key string) interface{} {
		var timeDiffProvider = toolbox.NewTimeDiffProvider()
		switch key {
		case "now":
			result, _ := timeDiffProvider.Get(nil, "now", 0, "day", "timestamp")
			return result
		case "tomorrow":
			result, _ := timeDiffProvider.Get(nil, "now", 1, "day", "timestamp")
			return result
		case "yesterday":
			result, _ := timeDiffProvider.Get(nil, "now", -1, "day", "timestamp")
			return result
		}
		return nil
	})

	result.Put("env", func(key string) interface{} {
		return os.Getenv(key)
	})

	for k, v := range UdfRegistry {
		result.Put(k, v)
	}

	return result
}
<|endoftext|>