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

// +build ignore

package main

import (
	"flag"
	"log"
	"time"

	"github.com/nats-io/go-nats"
)

// NOTE: Use tls scheme for TLS, e.g. nats-req -s tls://demo.nats.io:4443 foo hello
func usage() {
	log.Fatalf("Usage: nats-req [-s server (%s)] <subject> <msg> \n", nats.DefaultURL)
}

func main() {
	var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")

	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	args := flag.Args()
	if len(args) < 2 {
		usage()
	}

	nc, err := nats.Connect(*urls)
	if err != nil {
		log.Fatalf("Can't connect: %v\n", err)
	}
	defer nc.Close()
	subj, payload := args[0], []byte(args[1])

	msg, err := nc.Request(subj, []byte(payload), 100*time.Millisecond)
	if err != nil {
		if nc.LastError() != nil {
			log.Fatalf("Error in Request: %v\n", nc.LastError())
		}
		log.Fatalf("Error in Request: %v\n", err)
	}

	log.Printf("Published [%s] : '%s'\n", subj, payload)
	log.Printf("Received [%v] : '%s'\n", msg.Subject, string(msg.Data))
}
<commit_msg>Update timeout<commit_after>// Copyright 2012-2018 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build ignore

package main

import (
	"flag"
	"log"
	"time"

	"github.com/nats-io/go-nats"
)

// NOTE: Use tls scheme for TLS, e.g. nats-req -s tls://demo.nats.io:4443 foo hello
func usage() {
	log.Fatalf("Usage: nats-req [-s server (%s)] <subject> <msg> \n", nats.DefaultURL)
}

func main() {
	var urls = flag.String("s", nats.DefaultURL, "The nats server URLs (separated by comma)")

	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	args := flag.Args()
	if len(args) < 2 {
		usage()
	}

	nc, err := nats.Connect(*urls)
	if err != nil {
		log.Fatalf("Can't connect: %v\n", err)
	}
	defer nc.Close()
	subj, payload := args[0], []byte(args[1])

	msg, err := nc.Request(subj, []byte(payload), 500*time.Millisecond)
	if err != nil {
		if nc.LastError() != nil {
			log.Fatalf("Error in Request: %v\n", nc.LastError())
		}
		log.Fatalf("Error in Request: %v\n", err)
	}

	log.Printf("Published [%s] : '%s'\n", subj, payload)
	log.Printf("Received [%v] : '%s'\n", msg.Subject, string(msg.Data))
}
<|endoftext|>


<commit_before>package version

// Version tells us the app version string
const Version = "2.9.0"
<commit_msg>Bump to v2.9.1<commit_after>package version

// Version tells us the app version string
const Version = "2.9.1"
<|endoftext|>


<commit_before>package warserver

import (
    "fmt"
    "github.com/gorilla/websocket"
    "io"
    "net"
    "net/http"
    "strconv"
    "warserver/logger"
)

const (
    RECV_BUF_LEN = 1024
    SERVER_IP = "localhost"
    SERVER_PORT = "5269"
)

type websocketHandler interface {
    handleWebsocket(message []byte, cconn *clientConnection)
}

type clientInfo struct {
    Id int
}

type clientConnection struct {
    ws *websocket.Conn
    currentHandler websocketHandler
    handlers chan websocketHandler
    toClient chan []byte
    info clientInfo
}

type serverConnection struct {
    conn net.Conn
}

type pipe struct {
    wsRecv chan []byte
    sockRecv chan []byte
}

type proxy struct {
    proxyConns []*clientConnection
    server *serverConnection
}

func (p *proxy) slotClientConnection(slot int, cconn *clientConnection) {
    p.proxyConns[slot] = cconn;
}

func (p *proxy) removeClientConnection(pos int) {
    j := pos + 1
    copy(p.proxyConns[pos:], p.proxyConns[j:])
    for k, n := len(p.proxyConns) - j + pos, len(p.proxyConns); k < n; k ++ {
        p.proxyConns[k] = nil // or the zero value of T
    } // for k
    p.proxyConns = p.proxyConns[:len(p.proxyConns) - j + pos]
}

func (p *proxy) handleWebsocket(message []byte, cconn *clientConnection) {
    logger.Infof("Proxying message from client: %s", message)
    _, err := p.server.conn.Write(message)
    if err != nil {
        logger.Errorf("Error while writing to socket: %s", err)
    }
}

func (p *proxy) serverReadPump() {
    defer func() {
        p.server.conn.Close()
    }()
    for {
        buf := make([]byte, RECV_BUF_LEN)
        _, err := p.server.conn.Read(buf)
        if err != nil {
            logger.Errorf("Error while reading from socket: %s", err)
            break
        }
        logger.Debugf("Received %s from socket", buf)
        p.broadcast(buf)
    }
}

func (p *proxy) broadcast(message []byte) {
    for i := 0; i < len(p.proxyConns); i++ {
        p.proxyConns[i].toClient <- message
    }
}

func (p *proxy) sendInitialGameInfo() {
    // I'll send basically this once the server can accept it
    message := "new:{\"uids\": ["
    for i := 0; i < len(p.proxyConns); i++ {
        message = message + strconv.Itoa(p.proxyConns[i].info.Id)
        if (i < (len(p.proxyConns) - 1)) {
            message = message + ", "
        }
    }
    message = message + "], \"debug\": 0}"
    logger.Infof("%s", message)
    p.server.conn.Write([]byte(message))
}

func (pc *clientConnection) wsReadPump() {
    defer func() {
        pc.ws.Close()
    }()
    pc.ws.SetReadLimit(RECV_BUF_LEN)
    for {
        _, msg, err := pc.ws.ReadMessage()
        if err != nil {
            if err == io.EOF || err == io.ErrUnexpectedEOF {
                // the client ID here is redundant...
                killcconn := fmt.Sprintf("killClient:{\"Id\": %d}", pc.info.Id)
                pc.currentHandler.handleWebsocket([]byte(killcconn), pc)
            } else {
                logger.Errorf("Error while reading from websocket: %s", err)
            }
            break
        }
        logger.Debugf("Received %s from websocket", msg)
        select {
        case newHandler := <-pc.handlers:
            pc.currentHandler = newHandler
        default:
        }
        pc.currentHandler.handleWebsocket(msg, pc)
    }
}

func (pc *clientConnection) wsWritePump() {
    for msg := range pc.toClient {
        logger.Debugf("Writing %s to websocket", msg)
        err := pc.ws.WriteMessage(websocket.TextMessage, msg)
        if err != nil {
            logger.Errorf("Error while writing to websocket: %s", err)
            break
        }
    }
}

func connectToServer() (net.Conn, error) {
    return net.Dial("tcp", SERVER_IP + ":" + SERVER_PORT)
}

func serveWs(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, "Method not allowed", 405)
        return
    }
    if r.Header.Get("Origin") != "http://"+r.Host {
        http.Error(w, "Origin not allowed", 403)
        return
    }
    ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
    if _, ok := err.(websocket.HandshakeError); ok {
        http.Error(w, "Not a websocket handshake", 400)
        return
    } else if err != nil {
        logger.Errorf("Websocket upgrade error: %s", err)
        return
    }
    gamehub.wsRegister<- ws
}
<commit_msg>Configure websocket handler to handle cross-port connections<commit_after>package warserver

import (
    "fmt"
    "github.com/gorilla/websocket"
    "io"
    "net"
    "net/http"
    "strconv"
    "strings"
    "warserver/logger"
)

const (
    RECV_BUF_LEN = 1024
    SERVER_IP = "localhost"
    SERVER_PORT = "5269"
)

type websocketHandler interface {
    handleWebsocket(message []byte, cconn *clientConnection)
}

type clientInfo struct {
    Id int
}

type clientConnection struct {
    ws *websocket.Conn
    currentHandler websocketHandler
    handlers chan websocketHandler
    toClient chan []byte
    info clientInfo
}

type serverConnection struct {
    conn net.Conn
}

type pipe struct {
    wsRecv chan []byte
    sockRecv chan []byte
}

type proxy struct {
    proxyConns []*clientConnection
    server *serverConnection
}

func (p *proxy) slotClientConnection(slot int, cconn *clientConnection) {
    p.proxyConns[slot] = cconn;
}

func (p *proxy) removeClientConnection(pos int) {
    j := pos + 1
    copy(p.proxyConns[pos:], p.proxyConns[j:])
    for k, n := len(p.proxyConns) - j + pos, len(p.proxyConns); k < n; k ++ {
        p.proxyConns[k] = nil // or the zero value of T
    } // for k
    p.proxyConns = p.proxyConns[:len(p.proxyConns) - j + pos]
}

func (p *proxy) handleWebsocket(message []byte, cconn *clientConnection) {
    logger.Infof("Proxying message from client: %s", message)
    _, err := p.server.conn.Write(message)
    if err != nil {
        logger.Errorf("Error while writing to socket: %s", err)
    }
}

func (p *proxy) serverReadPump() {
    defer func() {
        p.server.conn.Close()
    }()
    for {
        buf := make([]byte, RECV_BUF_LEN)
        _, err := p.server.conn.Read(buf)
        if err != nil {
            logger.Errorf("Error while reading from socket: %s", err)
            break
        }
        logger.Debugf("Received %s from socket", buf)
        p.broadcast(buf)
    }
}

func (p *proxy) broadcast(message []byte) {
    for i := 0; i < len(p.proxyConns); i++ {
        p.proxyConns[i].toClient <- message
    }
}

func (p *proxy) sendInitialGameInfo() {
    // I'll send basically this once the server can accept it
    message := "new:{\"uids\": ["
    for i := 0; i < len(p.proxyConns); i++ {
        message = message + strconv.Itoa(p.proxyConns[i].info.Id)
        if (i < (len(p.proxyConns) - 1)) {
            message = message + ", "
        }
    }
    message = message + "], \"debug\": 0}"
    logger.Infof("%s", message)
    p.server.conn.Write([]byte(message))
}

func (pc *clientConnection) wsReadPump() {
    defer func() {
        pc.ws.Close()
    }()
    pc.ws.SetReadLimit(RECV_BUF_LEN)
    for {
        _, msg, err := pc.ws.ReadMessage()
        if err != nil {
            if err == io.EOF || err == io.ErrUnexpectedEOF {
                // the client ID here is redundant...
                killcconn := fmt.Sprintf("killClient:{\"Id\": %d}", pc.info.Id)
                pc.currentHandler.handleWebsocket([]byte(killcconn), pc)
            } else {
                logger.Errorf("Error while reading from websocket: %s", err)
            }
            break
        }
        logger.Debugf("Received %s from websocket", msg)
        select {
        case newHandler := <-pc.handlers:
            pc.currentHandler = newHandler
        default:
        }
        pc.currentHandler.handleWebsocket(msg, pc)
    }
}

func (pc *clientConnection) wsWritePump() {
    for msg := range pc.toClient {
        logger.Debugf("Writing %s to websocket", msg)
        err := pc.ws.WriteMessage(websocket.TextMessage, msg)
        if err != nil {
            logger.Errorf("Error while writing to websocket: %s", err)
            break
        }
    }
}

func connectToServer() (net.Conn, error) {
    return net.Dial("tcp", SERVER_IP + ":" + SERVER_PORT)
}

func serveWs(w http.ResponseWriter, r *http.Request) {
    if r.Method != "GET" {
        http.Error(w, "Method not allowed", 405)
        return
    }
    if strings.Split(r.Header.Get("Origin"),":")[1] != strings.Split("http://"+r.Host,":")[1] {
        logger.Warnf("Cross origin problem: %s", r.Host)
        http.Error(w, "Origin not allowed", 403)
        return
    }
    ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
    if _, ok := err.(websocket.HandshakeError); ok {
        http.Error(w, "Not a websocket handshake", 400)
        return
    } else if err != nil {
        logger.Errorf("Websocket upgrade error: %s", err)
        return
    }
    gamehub.wsRegister<- ws
}
<|endoftext|>


<commit_before>package mobileauth

import (
	"crypto/rand"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net/http/cookiejar"
	"net/url"
	"strconv"
)

// ErrMustProvidePhoneNumber is returned by AddAuthenticator
// when no phone set on both authentication linker and account
var ErrMustProvidePhoneNumber = errors.New("no phone number on the account")

// ErrMustRemovePhoneNumber is returned by AddAuthenticator
// when phone is set on both authentication linker and account
var ErrMustRemovePhoneNumber = errors.New("a phone number is already on the account")

// ErrAuthenticatorPresent is returned by AddAuthenticator
var ErrAuthenticatorPresent = errors.New("authenticator already present")

// ErrBadSMSCode is returned by FinalizeAddAuthenticator
// when steam rejects supplied SMS code
var ErrBadSMSCode = errors.New("bad sms code")

// ErrUnableToGenerateCorrectCodes is returned by FinalizeAddAuthenticator
var ErrUnableToGenerateCorrectCodes = errors.New("unable to generate correct codes")

// Handles the linking process for a new mobile authenticator
type AuthenticatorLinker struct {
	// Set to register a new phone number when linking.
	// If a phone number is not set on the account, this must be set.
	// If a phone number is set on the account, this must be null.
	PhoneNumber string
	// Randomly-generated device ID. Should only be generated once per linker.
	DeviceID string
	// After the initial link step, if successful, this will be
	// the SteamGuard data for the account. PLEASE save this somewhere
	// after generating it; it's vital data.
	LinkedAccount *SteamGuardAccount
	// True if the authenticator has been fully finalized.
	finalized bool

	_session *SessionData
	_cookies *cookiejar.Jar
}

func NewAuthenticatorLinker(session *SessionData) *AuthenticatorLinker {
	cookies, _ := cookiejar.New(nil)
	session.AddCookies(cookies)

	return &AuthenticatorLinker{
		DeviceID: generateDeviceID(),
		_session: session,
		_cookies: cookies,
	}
}

func (al *AuthenticatorLinker) AddAuthenticator() error {
	hasPhone, _ := al._hasPhoneAttached()
	if hasPhone && al.PhoneNumber != "" {
		return ErrMustRemovePhoneNumber
	}
	if !hasPhone && al.PhoneNumber == "" {
		return ErrMustProvidePhoneNumber
	}

	if !hasPhone {
		if err := al._addPhoneNumber(); err != nil {
			return fmt.Errorf("failed to add phone number: %v", err)
		}
	}

	postData := url.Values{}
	postData.Set("access_token", al._session.OAuthToken)
	postData.Set("steamid", strconv.FormatUint(al._session.SteamID, 10))
	postData.Set("authenticator_type", "1")
	postData.Set("device_identifier", al.DeviceID)
	postData.Set("sms_phone_id", "1")

	respBody, err := MobileLoginRequest(UrlSteamApiBase+"/ITwoFactorService/AddAuthenticator/v0001", "POST", &postData, nil, nil)
	if err != nil {
		return err
	}

	// Unmarshal response
	r := addAuthenticatorResponse{}
	if err := json.Unmarshal(respBody, &r); err != nil {
		return err
	}
	if r.Response == nil {
		return errors.New("malformed json response")
	}
	if r.Response.Status == 29 {
		return ErrAuthenticatorPresent
	}
	if r.Response.Status != 1 {
		return fmt.Errorf("authenticator response status: %v\n", r.Response.Status)
	}

	al.LinkedAccount = r.Response
	al.LinkedAccount.Session = al._session
	al.LinkedAccount.DeviceID = al.DeviceID

	return nil
}

func (al *AuthenticatorLinker) FinalizeAddAuthenticator(smsCode string) error {
	var isSmsCodeGood bool
	postData := url.Values{}
	postData.Set("steamid", strconv.FormatUint(al._session.SteamID, 10))
	postData.Set("access_token", al._session.OAuthToken)
	postData.Set("activation_code", smsCode)
	postData.Set("authenticator_code", "")
	retryCount := 30
	for tries := 0; tries <= retryCount; tries++ {
		var steamGuardCode string
		if tries != 0 {
			var err error
			steamGuardCode, err = al.LinkedAccount.GenerateSteamGuardCode()
			if err != nil {
				return fmt.Errorf("failed to generate steam guard code: %v", err)
			}
		}
		postData.Set("authenticator_code", steamGuardCode)
		postData.Set("authenticator_time", strconv.FormatInt(GetSteamTime(), 10))

		if isSmsCodeGood {
			postData.Set("activation_code", "")
		}

		respBody, err := MobileLoginRequest(UrlSteamApiBase+"/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", &postData, nil, nil)
		if err != nil {
			return err
		}

		r := finalizeAuthenticatorResponse{}
		if err := json.Unmarshal(respBody, &r); err != nil {
			return err
		}

		if r.Response == nil {
			return errors.New("malformed json response")
		}

		if r.Response.Status == 89 {
			return ErrBadSMSCode
		}

		if r.Response.Status == 88 {
			if tries >= retryCount {
				return ErrUnableToGenerateCorrectCodes
			}
		}

		if !r.Response.Success {
			return errors.New("steam returned success false")
		}

		if r.Response.WantMore {
			isSmsCodeGood = true
			continue
		}

		al.LinkedAccount.FullyEnrolled = true
		return nil
	}

	return fmt.Errorf("failed to finalize authenticator in %v tries\n", retryCount)
}

func (al *AuthenticatorLinker) _addPhoneNumber() error {
	postData := url.Values{}
	postData.Set("op", "add_phone_number")
	postData.Set("arg", al.PhoneNumber)
	postData.Set("sessionid", al._session.SessionID)

	respBody, err := WebRequest(UrlCommunityBase+"/steamguard/phoneajax", "POST", &postData, al._cookies, nil, nil)
	if err != nil {
		return err
	}

	r := addPhoneResponse{}
	if err := json.Unmarshal(respBody, &r); err != nil {
		return err
	}
	if r.Success != true {
		return errors.New("steam returned success false")
	}
	return nil
}

func (al *AuthenticatorLinker) _hasPhoneAttached() (bool, error) {
	postData := url.Values{}
	postData.Set("op", "has_phone")
	postData.Set("arg", "null")
	postData.Set("sessionid", al._session.SessionID)

	respBody, err := MobileLoginRequest(UrlCommunityBase+"/steamguard/phoneajax", "POST", &postData, al._cookies, nil)
	if err != nil {
		return false, err
	}

	r := hasPhoneResponse{}
	if err := json.Unmarshal(respBody, &r); err != nil {
		return false, err
	}

	return r.HasPhone, nil
}

type addAuthenticatorResponse struct {
	Response *SteamGuardAccount
}

type finalizeAuthenticatorResponse struct {
	Response *finalizeAuthenticatorResult
}

type finalizeAuthenticatorResult struct {
	Status     int32
	ServerTime int64 `json:"server_time,string"`
	WantMore   bool  `json:"want_more"`
	Success    bool
}

type hasPhoneResponse struct {
	HasPhone bool `json:"has_phone"`
}

type addPhoneResponse struct {
	Success bool
}

func generateDeviceID() string {
	// Generate 8 random bytes
	b := make([]byte, 8)
	if _, err := rand.Read(b); err != nil {
		panic("Failed to read from source of random bytes")
	}
	// Generate sha1 hash
	hasher := sha1.New()
	hasher.Write(b)
	return "android:" + hex.EncodeToString(hasher.Sum(nil))
}
<commit_msg>Fix generated device IDs now match that of Steam's<commit_after>package mobileauth

import (
	"crypto/rand"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net/http/cookiejar"
	"net/url"
	"strconv"
)

// ErrMustProvidePhoneNumber is returned by AddAuthenticator
// when no phone set on both authentication linker and account
var ErrMustProvidePhoneNumber = errors.New("no phone number on the account")

// ErrMustRemovePhoneNumber is returned by AddAuthenticator
// when phone is set on both authentication linker and account
var ErrMustRemovePhoneNumber = errors.New("a phone number is already on the account")

// ErrAuthenticatorPresent is returned by AddAuthenticator
var ErrAuthenticatorPresent = errors.New("authenticator already present")

// ErrBadSMSCode is returned by FinalizeAddAuthenticator
// when steam rejects supplied SMS code
var ErrBadSMSCode = errors.New("bad sms code")

// ErrUnableToGenerateCorrectCodes is returned by FinalizeAddAuthenticator
var ErrUnableToGenerateCorrectCodes = errors.New("unable to generate correct codes")

// Handles the linking process for a new mobile authenticator
type AuthenticatorLinker struct {
	// Set to register a new phone number when linking.
	// If a phone number is not set on the account, this must be set.
	// If a phone number is set on the account, this must be null.
	PhoneNumber string
	// Randomly-generated device ID. Should only be generated once per linker.
	DeviceID string
	// After the initial link step, if successful, this will be
	// the SteamGuard data for the account. PLEASE save this somewhere
	// after generating it; it's vital data.
	LinkedAccount *SteamGuardAccount
	// True if the authenticator has been fully finalized.
	finalized bool

	_session *SessionData
	_cookies *cookiejar.Jar
}

func NewAuthenticatorLinker(session *SessionData) *AuthenticatorLinker {
	cookies, _ := cookiejar.New(nil)
	session.AddCookies(cookies)

	return &AuthenticatorLinker{
		DeviceID: generateDeviceID(),
		_session: session,
		_cookies: cookies,
	}
}

func (al *AuthenticatorLinker) AddAuthenticator() error {
	hasPhone, _ := al._hasPhoneAttached()
	if hasPhone && al.PhoneNumber != "" {
		return ErrMustRemovePhoneNumber
	}
	if !hasPhone && al.PhoneNumber == "" {
		return ErrMustProvidePhoneNumber
	}

	if !hasPhone {
		if err := al._addPhoneNumber(); err != nil {
			return fmt.Errorf("failed to add phone number: %v", err)
		}
	}

	postData := url.Values{}
	postData.Set("access_token", al._session.OAuthToken)
	postData.Set("steamid", strconv.FormatUint(al._session.SteamID, 10))
	postData.Set("authenticator_type", "1")
	postData.Set("device_identifier", al.DeviceID)
	postData.Set("sms_phone_id", "1")

	respBody, err := MobileLoginRequest(UrlSteamApiBase+"/ITwoFactorService/AddAuthenticator/v0001", "POST", &postData, nil, nil)
	if err != nil {
		return err
	}

	// Unmarshal response
	r := addAuthenticatorResponse{}
	if err := json.Unmarshal(respBody, &r); err != nil {
		return err
	}
	if r.Response == nil {
		return errors.New("malformed json response")
	}
	if r.Response.Status == 29 {
		return ErrAuthenticatorPresent
	}
	if r.Response.Status != 1 {
		return fmt.Errorf("authenticator response status: %v\n", r.Response.Status)
	}

	al.LinkedAccount = r.Response
	al.LinkedAccount.Session = al._session
	al.LinkedAccount.DeviceID = al.DeviceID

	return nil
}

func (al *AuthenticatorLinker) FinalizeAddAuthenticator(smsCode string) error {
	var isSmsCodeGood bool
	postData := url.Values{}
	postData.Set("steamid", strconv.FormatUint(al._session.SteamID, 10))
	postData.Set("access_token", al._session.OAuthToken)
	postData.Set("activation_code", smsCode)
	postData.Set("authenticator_code", "")
	retryCount := 30
	for tries := 0; tries <= retryCount; tries++ {
		var steamGuardCode string
		if tries != 0 {
			var err error
			steamGuardCode, err = al.LinkedAccount.GenerateSteamGuardCode()
			if err != nil {
				return fmt.Errorf("failed to generate steam guard code: %v", err)
			}
		}
		postData.Set("authenticator_code", steamGuardCode)
		postData.Set("authenticator_time", strconv.FormatInt(GetSteamTime(), 10))

		if isSmsCodeGood {
			postData.Set("activation_code", "")
		}

		respBody, err := MobileLoginRequest(UrlSteamApiBase+"/ITwoFactorService/FinalizeAddAuthenticator/v0001", "POST", &postData, nil, nil)
		if err != nil {
			return err
		}

		r := finalizeAuthenticatorResponse{}
		if err := json.Unmarshal(respBody, &r); err != nil {
			return err
		}

		if r.Response == nil {
			return errors.New("malformed json response")
		}

		if r.Response.Status == 89 {
			return ErrBadSMSCode
		}

		if r.Response.Status == 88 {
			if tries >= retryCount {
				return ErrUnableToGenerateCorrectCodes
			}
		}

		if !r.Response.Success {
			return errors.New("steam returned success false")
		}

		if r.Response.WantMore {
			isSmsCodeGood = true
			continue
		}

		al.LinkedAccount.FullyEnrolled = true
		return nil
	}

	return fmt.Errorf("failed to finalize authenticator in %v tries\n", retryCount)
}

func (al *AuthenticatorLinker) _addPhoneNumber() error {
	postData := url.Values{}
	postData.Set("op", "add_phone_number")
	postData.Set("arg", al.PhoneNumber)
	postData.Set("sessionid", al._session.SessionID)

	respBody, err := WebRequest(UrlCommunityBase+"/steamguard/phoneajax", "POST", &postData, al._cookies, nil, nil)
	if err != nil {
		return err
	}

	r := addPhoneResponse{}
	if err := json.Unmarshal(respBody, &r); err != nil {
		return err
	}
	if r.Success != true {
		return errors.New("steam returned success false")
	}
	return nil
}

func (al *AuthenticatorLinker) _hasPhoneAttached() (bool, error) {
	postData := url.Values{}
	postData.Set("op", "has_phone")
	postData.Set("arg", "null")
	postData.Set("sessionid", al._session.SessionID)

	respBody, err := MobileLoginRequest(UrlCommunityBase+"/steamguard/phoneajax", "POST", &postData, al._cookies, nil)
	if err != nil {
		return false, err
	}

	r := hasPhoneResponse{}
	if err := json.Unmarshal(respBody, &r); err != nil {
		return false, err
	}

	return r.HasPhone, nil
}

type addAuthenticatorResponse struct {
	Response *SteamGuardAccount
}

type finalizeAuthenticatorResponse struct {
	Response *finalizeAuthenticatorResult
}

type finalizeAuthenticatorResult struct {
	Status     int32
	ServerTime int64 `json:"server_time,string"`
	WantMore   bool  `json:"want_more"`
	Success    bool
}

type hasPhoneResponse struct {
	HasPhone bool `json:"has_phone"`
}

type addPhoneResponse struct {
	Success bool
}

func generateDeviceID() string {
	// Generate 8 random bytes
	b := make([]byte, 8)
	if _, err := rand.Read(b); err != nil {
		panic("Failed to read from source of random bytes")
	}
	// Generate sha1 hash
	hasher := sha1.New()
	hasher.Write(b)
	deviceId := make([]byte, 40)
	hex.Encode(deviceId, hasher.Sum(nil))
	deviceId = deviceId[:32]
	// Insert "-" at 8,12,16,20 positions
	for i, pos := range []int{8, 12, 16, 20} {
		deviceId = append(deviceId[0:pos+i],
			append([]byte{'-'}, deviceId[pos+i:]...)...)
	}
	return "android:" + string(deviceId)
}
<|endoftext|>


<commit_before>package jobs

import (
	"time"

	log "github.com/Sirupsen/logrus"

	"strings"

	"fmt"

	"github.com/liam-lai/ptt-alertor/line"
	"github.com/liam-lai/ptt-alertor/mail"
	"github.com/liam-lai/ptt-alertor/messenger"
	"github.com/liam-lai/ptt-alertor/models/ptt/article"
	board "github.com/liam-lai/ptt-alertor/models/ptt/board/redis"
	user "github.com/liam-lai/ptt-alertor/models/user/redis"
)

type Message struct {
	email      string
	line       string
	lineNotify string
	messenger  string
	board      string
	keyword    string
	author     string
	articles   article.Articles
}

func (msg Message) Run() {
	bds := new(board.Board).All()
	for _, bd := range bds {
		bd.WithNewArticles()
	}
	bds = deleteNonNewArticleBoard(bds)
	users := new(user.User).All()
	msgCh := make(chan Message)
	for _, user := range users {
		if user.Enable {
			msg.email = user.Profile.Email
			msg.line = user.Profile.Line
			msg.lineNotify = user.Profile.LineAccessToken
			msg.messenger = user.Profile.Messenger
			log.WithField("user", user.Profile.Account).Info("Checking User Subscribes")
			go userChecker(user, bds, msg, msgCh)
		}
	}

	for {
		select {
		case m := <-msgCh:
			sendMessage(m)
		case <-time.After(time.Second * 3):
			log.Info("Message Done")
			return
		}
	}
}

func deleteNonNewArticleBoard(bds []*board.Board) []*board.Board {
	for i := 0; i < len(bds); i++ {
		if len(bds[i].NewArticles) == 0 {
			bds = append(bds[:i], bds[i+1:]...)
			i--
		}
	}
	return bds
}

func userChecker(user *user.User, bds []*board.Board, msg Message, msgCh chan Message) {
	for _, bd := range bds {
		go subscribeChecker(user, bd, msg, msgCh)
	}
}

func subscribeChecker(user *user.User, bd *board.Board, msg Message, msgCh chan Message) {
	for _, sub := range user.Subscribes {
		if bd.Name == sub.Board {
			msg.board = sub.Board
			for _, keyword := range sub.Keywords {
				go keywordChecker(keyword, bd, msg, msgCh)
			}
			for _, author := range sub.Authors {
				go authorChecker(author, bd, msg, msgCh)
			}
		}
	}
}

func keywordChecker(keyword string, bd *board.Board, msg Message, msgCh chan Message) {
	keywordArticles := make(article.Articles, 0)
	for _, newAtcl := range bd.NewArticles {
		if newAtcl.MatchKeyword(keyword) {
			newAtcl.Author = ""
			keywordArticles = append(keywordArticles, newAtcl)
		}
	}
	if len(keywordArticles) != 0 {
		msg.keyword = keyword
		msg.articles = keywordArticles
		msgCh <- msg
	}
}

func authorChecker(author string, bd *board.Board, msg Message, msgCh chan Message) {
	authorArticles := make(article.Articles, 0)
	for _, newAtcl := range bd.NewArticles {
		if strings.EqualFold(newAtcl.Author, author) {
			authorArticles = append(authorArticles, newAtcl)
		}
	}
	if len(authorArticles) != 0 {
		msg.author = author
		msg.articles = authorArticles
		msgCh <- msg
	}

}

func sendMessage(msg Message) {
	var account string
	if msg.email != "" {
		account = msg.email
		sendMail(msg)
	}
	if msg.lineNotify != "" {
		account = msg.line
		sendLineNotify(msg)
	}
	if msg.messenger != "" {
		account = msg.messenger
		sendMessenger(msg)
	}
	log.WithFields(log.Fields{
		"account": account,
		"board":   msg.board,
		"keyword": msg.keyword,
		"author":  msg.author,
	}).Info("Message Sent")
}

func sendMail(msg Message) {
	m := new(mail.Mail)
	m.Title.BoardName = msg.board
	m.Title.Keyword = msg.keyword
	m.Body.Articles = msg.articles
	m.Receiver = msg.email

	m.Send()
}

func sendLine(msg Message) {
	line.PushTextMessage(msg.line, msg.String())
}

func sendLineNotify(msg Message) {
	line.Notify(msg.lineNotify, msg.String())
}

func sendMessenger(msg Message) {
	m := messenger.New()
	m.SendTextMessage(msg.messenger, msg.String())
}

func (msg Message) String() string {
	subType := "關鍵字"
	subText := msg.keyword
	if msg.author != "" {
		subType = "作者"
		subText = msg.author
	}
	return fmt.Sprintf("\r\n看版：%s；%s：%s%s", msg.board, subType, subText, msg.articles.String())
}
<commit_msg>change message operation time to 10s<commit_after>package jobs

import (
	"time"

	log "github.com/Sirupsen/logrus"

	"strings"

	"fmt"

	"github.com/liam-lai/ptt-alertor/line"
	"github.com/liam-lai/ptt-alertor/mail"
	"github.com/liam-lai/ptt-alertor/messenger"
	"github.com/liam-lai/ptt-alertor/models/ptt/article"
	board "github.com/liam-lai/ptt-alertor/models/ptt/board/redis"
	user "github.com/liam-lai/ptt-alertor/models/user/redis"
)

type Message struct {
	email      string
	line       string
	lineNotify string
	messenger  string
	board      string
	keyword    string
	author     string
	articles   article.Articles
}

func (msg Message) Run() {
	bds := new(board.Board).All()
	for _, bd := range bds {
		bd.WithNewArticles()
	}
	bds = deleteNonNewArticleBoard(bds)
	users := new(user.User).All()
	msgCh := make(chan Message)
	for _, user := range users {
		if user.Enable {
			msg.email = user.Profile.Email
			msg.line = user.Profile.Line
			msg.lineNotify = user.Profile.LineAccessToken
			msg.messenger = user.Profile.Messenger
			log.WithField("user", user.Profile.Account).Info("Checking User Subscribes")
			go userChecker(user, bds, msg, msgCh)
		}
	}

	for {
		select {
		case m := <-msgCh:
			sendMessage(m)
		case <-time.After(time.Second * 10):
			log.Info("Message Done")
			return
		}
	}
}

func deleteNonNewArticleBoard(bds []*board.Board) []*board.Board {
	for i := 0; i < len(bds); i++ {
		if len(bds[i].NewArticles) == 0 {
			bds = append(bds[:i], bds[i+1:]...)
			i--
		}
	}
	return bds
}

func userChecker(user *user.User, bds []*board.Board, msg Message, msgCh chan Message) {
	for _, bd := range bds {
		go subscribeChecker(user, bd, msg, msgCh)
	}
}

func subscribeChecker(user *user.User, bd *board.Board, msg Message, msgCh chan Message) {
	for _, sub := range user.Subscribes {
		if bd.Name == sub.Board {
			msg.board = sub.Board
			for _, keyword := range sub.Keywords {
				go keywordChecker(keyword, bd, msg, msgCh)
			}
			for _, author := range sub.Authors {
				go authorChecker(author, bd, msg, msgCh)
			}
		}
	}
}

func keywordChecker(keyword string, bd *board.Board, msg Message, msgCh chan Message) {
	keywordArticles := make(article.Articles, 0)
	for _, newAtcl := range bd.NewArticles {
		if newAtcl.MatchKeyword(keyword) {
			newAtcl.Author = ""
			keywordArticles = append(keywordArticles, newAtcl)
		}
	}
	if len(keywordArticles) != 0 {
		msg.keyword = keyword
		msg.articles = keywordArticles
		msgCh <- msg
	}
}

func authorChecker(author string, bd *board.Board, msg Message, msgCh chan Message) {
	authorArticles := make(article.Articles, 0)
	for _, newAtcl := range bd.NewArticles {
		if strings.EqualFold(newAtcl.Author, author) {
			authorArticles = append(authorArticles, newAtcl)
		}
	}
	if len(authorArticles) != 0 {
		msg.author = author
		msg.articles = authorArticles
		msgCh <- msg
	}

}

func sendMessage(msg Message) {
	var account string
	if msg.email != "" {
		account = msg.email
		sendMail(msg)
	}
	if msg.lineNotify != "" {
		account = msg.line
		sendLineNotify(msg)
	}
	if msg.messenger != "" {
		account = msg.messenger
		sendMessenger(msg)
	}
	log.WithFields(log.Fields{
		"account": account,
		"board":   msg.board,
		"keyword": msg.keyword,
		"author":  msg.author,
	}).Info("Message Sent")
}

func sendMail(msg Message) {
	m := new(mail.Mail)
	m.Title.BoardName = msg.board
	m.Title.Keyword = msg.keyword
	m.Body.Articles = msg.articles
	m.Receiver = msg.email

	m.Send()
}

func sendLine(msg Message) {
	line.PushTextMessage(msg.line, msg.String())
}

func sendLineNotify(msg Message) {
	line.Notify(msg.lineNotify, msg.String())
}

func sendMessenger(msg Message) {
	m := messenger.New()
	m.SendTextMessage(msg.messenger, msg.String())
}

func (msg Message) String() string {
	subType := "關鍵字"
	subText := msg.keyword
	if msg.author != "" {
		subType = "作者"
		subText = msg.author
	}
	return fmt.Sprintf("\r\n看版：%s；%s：%s%s", msg.board, subType, subText, msg.articles.String())
}
<|endoftext|>


<commit_before>package authredirect

import (
	"net/http"
	"net/url"

	"github.com/concourse/atc"
	"github.com/concourse/atc/web"
	"github.com/concourse/go-concourse/concourse"
	"github.com/gorilla/context"
	"github.com/tedsuo/rata"
)

type Handler struct {
	web.HTTPHandlerWithError
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	err := handler.HTTPHandlerWithError.ServeHTTP(w, r)
	if err == concourse.ErrUnauthorized {
		teamName := r.FormValue(":team_name")
		if teamName == "" {
			teamName = atc.DefaultTeamName
		}
		path, err := web.Routes.CreatePathForRoute(web.TeamLogIn, rata.Params{
			"team_name": teamName,
		})
		if err != nil {
			return
		}

		if redirectURL, ok := handler.redirectTargetFor(r); ok {
			path += "?" + url.Values{
				"redirect": {redirectURL},
			}.Encode()
		}

		http.Redirect(w, r, path, http.StatusFound)
	} else if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func (handler Handler) redirectTargetFor(r *http.Request) (string, bool) {
	if r.Method == "GET" {
		reqURL := context.Get(r, requestURLKey)
		reqURLStr, ok := reqURL.(string)
		return reqURLStr, ok
	}

	referer := r.Referer()
	if referer != "" {
		return referer, true
	}

	return "", false
}
<commit_msg>returns 403 Forbidden on CheckAuthorizationHandler<commit_after>package authredirect

import (
	"net/http"
	"net/url"

	"github.com/concourse/atc"
	"github.com/concourse/atc/web"
	"github.com/concourse/go-concourse/concourse"
	"github.com/gorilla/context"
	"github.com/tedsuo/rata"
)

type Handler struct {
	web.HTTPHandlerWithError
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	err := handler.HTTPHandlerWithError.ServeHTTP(w, r)
	if err == concourse.ErrUnauthorized || err == concourse.ErrForbidden {
		teamName := r.FormValue(":team_name")
		if teamName == "" {
			teamName = atc.DefaultTeamName
		}
		path, err := web.Routes.CreatePathForRoute(web.TeamLogIn, rata.Params{
			"team_name": teamName,
		})
		if err != nil {
			return
		}

		if redirectURL, ok := handler.redirectTargetFor(r); ok {
			path += "?" + url.Values{
				"redirect": {redirectURL},
			}.Encode()
		}

		http.Redirect(w, r, path, http.StatusFound)
	} else if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func (handler Handler) redirectTargetFor(r *http.Request) (string, bool) {
	if r.Method == "GET" {
		reqURL := context.Get(r, requestURLKey)
		reqURLStr, ok := reqURL.(string)
		return reqURLStr, ok
	}

	referer := r.Referer()
	if referer != "" {
		return referer, true
	}

	return "", false
}
<|endoftext|>


<commit_before>package api

import (
	"net/http"
	"path/filepath"
	"syscall"

	"github.com/vmware/harbor/src/common/api"
	"github.com/vmware/harbor/src/common/dao"
	"github.com/vmware/harbor/src/common/utils/log"
)

//SystemInfoAPI handle requests for getting system info /api/systeminfo
type SystemInfoAPI struct {
	api.BaseAPI
	currentUserID int
	isAdmin       bool
}

const harborStoragePath = "/harbor_storage"

//SystemInfo models for system info.
type SystemInfo struct {
	HarborStorage Storage `json:"storage"`
}

//Storage models for storage.
type Storage struct {
	Total uint64 `json:"total"`
	Free  uint64 `json:"free"`
}

// Prepare for validating user if an admin.
func (sia *SystemInfoAPI) Prepare() {
	sia.currentUserID = sia.ValidateUser()

	var err error
	sia.isAdmin, err = dao.IsAdminRole(sia.currentUserID)
	if err != nil {
		log.Errorf("Error occurred in IsAdminRole:%v", err)
		sia.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
}

// GetVolumeInfo gets specific volume storage info.
func (sia *SystemInfoAPI) GetVolumeInfo() {
	if !sia.isAdmin {
		sia.RenderError(http.StatusForbidden, "User does not have admin role.")
		return
	}
	var stat syscall.Statfs_t
	err := syscall.Statfs(filepath.Join("/", harborStoragePath), &stat)
	if err != nil {
		log.Errorf("Error occurred in syscall.Statfs: %v", err)
		sia.CustomAbort(http.StatusInternalServerError, "Internal error.")
		return
	}

	systemInfo := SystemInfo{
		HarborStorage: Storage{
			Total: stat.Blocks * uint64(stat.Bsize),
			Free:  stat.Bfree * uint64(stat.Bsize),
		},
	}

	sia.Data["json"] = systemInfo
	sia.ServeJSON()
}
<commit_msg>Update for precising volume size.<commit_after>package api

import (
	"net/http"
	"path/filepath"
	"syscall"

	"github.com/vmware/harbor/src/common/api"
	"github.com/vmware/harbor/src/common/dao"
	"github.com/vmware/harbor/src/common/utils/log"
)

//SystemInfoAPI handle requests for getting system info /api/systeminfo
type SystemInfoAPI struct {
	api.BaseAPI
	currentUserID int
	isAdmin       bool
}

const harborStoragePath = "/harbor_storage"

//SystemInfo models for system info.
type SystemInfo struct {
	HarborStorage Storage `json:"storage"`
}

//Storage models for storage.
type Storage struct {
	Total uint64 `json:"total"`
	Free  uint64 `json:"free"`
}

// Prepare for validating user if an admin.
func (sia *SystemInfoAPI) Prepare() {
	sia.currentUserID = sia.ValidateUser()

	var err error
	sia.isAdmin, err = dao.IsAdminRole(sia.currentUserID)
	if err != nil {
		log.Errorf("Error occurred in IsAdminRole:%v", err)
		sia.CustomAbort(http.StatusInternalServerError, "Internal error.")
	}
}

// GetVolumeInfo gets specific volume storage info.
func (sia *SystemInfoAPI) GetVolumeInfo() {
	if !sia.isAdmin {
		sia.RenderError(http.StatusForbidden, "User does not have admin role.")
		return
	}
	var stat syscall.Statfs_t
	err := syscall.Statfs(filepath.Join("/", harborStoragePath), &stat)
	if err != nil {
		log.Errorf("Error occurred in syscall.Statfs: %v", err)
		sia.CustomAbort(http.StatusInternalServerError, "Internal error.")
		return
	}

	systemInfo := SystemInfo{
		HarborStorage: Storage{
			Total: stat.Blocks * uint64(stat.Bsize),
			Free:  stat.Bavail * uint64(stat.Bsize),
		},
	}

	sia.Data["json"] = systemInfo
	sia.ServeJSON()
}
<|endoftext|>


<commit_before>package engine

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
)

type (
	Ctx struct {
		engine  *Engine
		group   *Group
		rwmem   responseWriter
		RW      ResponseWriter
		Request *http.Request
		Params  Params
		Form    url.Values
		// Files
		Errors errorMsgs
	}
)

func (engine *Engine) newContext() interface{} {
	c := &Ctx{engine: engine}
	c.RW = &c.rwmem
	return c
}

func (engine *Engine) getContext(w http.ResponseWriter, req *http.Request) *Ctx {
	c := engine.cache.Get().(*Ctx)
	c.rwmem.reset(w)
	c.Request = req
	req.ParseMultipartForm(engine.MaxFormMemory)
	c.Form = req.Form
	return c
}

func (engine *Engine) putContext(c *Ctx) {
	if engine.LoggingOn {
		engine.SendSignal(fmt.Sprintf("%d %s %s", c.RW.Status(), c.Request.Method, c.Request.URL.Path))
	}
	c.group = nil
	c.Request = nil
	c.Params = nil
	c.Form = nil
	engine.cache.Put(c)
}

func (c *Ctx) errorTyped(err error, typ uint32, meta interface{}) {
	c.Errors = append(c.Errors, errorMsg{
		Err:  err.Error(),
		Type: typ,
		Meta: meta,
	})
}

// Attaches an error that is pushed to a list of errors. It's a good idea
// to call Error for each error that occurred during the resolution of a request.
// A middleware can be used to collect all the errors and push them to a database
// together, print a log, or append it in the HTTP response.
func (c *Ctx) Error(err error, meta interface{}) {
	c.errorTyped(err, ErrorTypeExternal, meta)
}

// Returns the last error for the Ctx.
func (c *Ctx) LastError() error {
	s := len(c.Errors)
	if s > 0 {
		return errors.New(c.Errors[s-1].Err)
	} else {
		return nil
	}
}

// Immediately abort the context writing out the code to the response
func (c *Ctx) Abort(code int) {
	if code >= 0 {
		c.RW.WriteHeader(code)
	}
}

// Fail is the same as Abort plus an error message.
// Calling `c.Fail(500, err)` is equivalent to:
// ```
// c.Error(err, "Failed.")
// c.Abort(500)
// ```
func (c *Ctx) Fail(code int, err error) {
	c.Error(err, err.Error())
	c.Abort(code)
}

// Calls an HttpException in the current group by integer code from the Context,
// if the status exists.
func (c *Ctx) Status(code int) {
	if status, ok := c.group.HttpStatuses[code]; ok {
		s := len(status.handlers)
		for i := 0; i < s; i++ {
			status.handlers[i](c)
		}
	}
}
<commit_msg>go SendSignal<commit_after>package engine

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
)

type (
	Ctx struct {
		engine  *Engine
		group   *Group
		rwmem   responseWriter
		RW      ResponseWriter
		Request *http.Request
		Params  Params
		Form    url.Values
		// Files
		Errors errorMsgs
	}
)

func (engine *Engine) newContext() interface{} {
	c := &Ctx{engine: engine}
	c.RW = &c.rwmem
	return c
}

func (engine *Engine) getContext(w http.ResponseWriter, req *http.Request) *Ctx {
	c := engine.cache.Get().(*Ctx)
	c.rwmem.reset(w)
	c.Request = req
	req.ParseMultipartForm(engine.MaxFormMemory)
	c.Form = req.Form
	return c
}

func (engine *Engine) putContext(c *Ctx) {
	go engine.SendSignal(fmt.Sprintf("%d %s %s", c.RW.Status(), c.Request.Method, c.Request.URL.Path))
	c.group = nil
	c.Request = nil
	c.Params = nil
	c.Form = nil
	engine.cache.Put(c)
}

func (c *Ctx) errorTyped(err error, typ uint32, meta interface{}) {
	c.Errors = append(c.Errors, errorMsg{
		Err:  err.Error(),
		Type: typ,
		Meta: meta,
	})
}

// Attaches an error that is pushed to a list of errors. It's a good idea
// to call Error for each error that occurred during the resolution of a request.
// A middleware can be used to collect all the errors and push them to a database
// together, print a log, or append it in the HTTP response.
func (c *Ctx) Error(err error, meta interface{}) {
	c.errorTyped(err, ErrorTypeExternal, meta)
}

// Returns the last error for the Ctx.
func (c *Ctx) LastError() error {
	s := len(c.Errors)
	if s > 0 {
		return errors.New(c.Errors[s-1].Err)
	} else {
		return nil
	}
}

// Immediately abort the context writing out the code to the response
func (c *Ctx) Abort(code int) {
	if code >= 0 {
		c.RW.WriteHeader(code)
	}
}

// Fail is the same as Abort plus an error message.
// Calling `c.Fail(500, err)` is equivalent to:
// ```
// c.Error(err, "Failed.")
// c.Abort(500)
// ```
func (c *Ctx) Fail(code int, err error) {
	c.Error(err, err.Error())
	c.Abort(code)
}

// Calls an HttpException in the current group by integer code from the Context,
// if the status exists.
func (c *Ctx) Status(code int) {
	if status, ok := c.group.HttpStatuses[code]; ok {
		s := len(status.handlers)
		for i := 0; i < s; i++ {
			status.handlers[i](c)
		}
	}
}
<|endoftext|>


<commit_before>package telebot

import (
	"strings"

	"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

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

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

	// 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() Recipient

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

	// SenderOrChat 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.
	SenderOrChat() 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 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

	// 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
}

// 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
	chosenInlineResult *ChosenInlineResult
	shippingQuery      *ShippingQuery
	preCheckoutQuery   *PreCheckoutQuery
	poll               *Poll
	pollAnswer         *PollAnswer
}

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) ChosenInlineResult() *ChosenInlineResult {
	return c.chosenInlineResult
}

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() Recipient {
	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.chosenInlineResult != nil:
		return c.chosenInlineResult.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) SenderOrChat() Recipient {
	sender := c.Sender()
	if sender != (*User)(nil) {
		return sender
	}
	return c.Chat()
}

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.callback != nil:
		return c.callback.Data
	case c.query != nil:
		return c.query.Text
	case c.chosenInlineResult != nil:
		return c.chosenInlineResult.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 {
		return strings.Split(c.message.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.SenderOrChat(), what, opts...)
	return err
}

func (c *nativeContext) SendAlbum(a Album, opts ...interface{}) error {
	_, err := c.b.SendAlbum(c.SenderOrChat(), 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.SenderOrChat(), 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 {
	clb := c.callback
	if clb == nil || clb.Message == nil {
		return ErrBadContext
	}
	_, err := c.b.Edit(clb.Message, what, opts...)
	return err
}

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) 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.SenderOrChat(), 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...)
}
<commit_msg>context: rename SenderOrChat to Recipient, change Sender return type to User<commit_after>package telebot

import (
	"strings"

	"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

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

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

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

	// 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
}

// 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
	chosenInlineResult *ChosenInlineResult
	shippingQuery      *ShippingQuery
	preCheckoutQuery   *PreCheckoutQuery
	poll               *Poll
	pollAnswer         *PollAnswer
}

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) ChosenInlineResult() *ChosenInlineResult {
	return c.chosenInlineResult
}

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.chosenInlineResult != nil:
		return c.chosenInlineResult.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 {
	sender := c.Sender()
	if sender != nil {
		return sender
	}
	return c.Chat()
}

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.callback != nil:
		return c.callback.Data
	case c.query != nil:
		return c.query.Text
	case c.chosenInlineResult != nil:
		return c.chosenInlineResult.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 {
		return strings.Split(c.message.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 {
	clb := c.callback
	if clb == nil || clb.Message == nil {
		return ErrBadContext
	}
	_, err := c.b.Edit(clb.Message, what, opts...)
	return err
}

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) 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...)
}
<|endoftext|>


<commit_before>package endly

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/viant/endly/common"
	"github.com/viant/toolbox"
	"math/rand"
	"net/url"
	"os"
	"os/exec"
	"path"
	"strings"
	"time"
)

//TODO Execution detail Tracking of all run (time taken, request, response)

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

var serviceManagerKey = (*manager)(nil)
var deferFunctionsKey = (*[]func())(nil)
var stateKey = (*common.Map)(nil)
var sessionInfoKey = (*SessionInfo)(nil)
var workflowKey = (*Workflow)(nil)

type Context struct {
	toolbox.Context
}

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

func (c *Context) ExpandResource(resource *Resource) (*Resource, error) {
	var err 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, "://") {
		if workflow := c.Workflow(); workflow != nil && workflow.source != nil {
			baseURL, _ := toolbox.URLSplit(workflow.source.URL)
			resource.URL = toolbox.URLPathJoin(baseURL, resource.URL)
		}
	}

	var result = &Resource{
		URL:         c.Expand(resource.URL),
		Credential:  c.Expand(resource.Credential),
		Name:        c.Expand(resource.Name),
		Version:     resource.Version,
		Type:        c.Expand(resource.Type),
		Cache:       c.Expand(resource.Cache),
		CacheExpiry: resource.CacheExpiry,
	}
	result.ParsedURL, err = url.Parse(result.URL)
	if err != nil {
		return nil, reportError(fmt.Errorf("Failed to parse URL: %v %v", result.URL, err))
	}
	return result, nil
}

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
}

func (c *Context) Sessions() ClientSessions {
	var result *ClientSessions
	if !c.Contains(clientSessionKey) {
		var sessions ClientSessions = make(map[string]*ClientSession)
		result = &sessions
		c.Put(clientSessionKey, result)
	} else {
		c.GetInto(clientSessionKey, &result)
	}
	return *result
}

func (c *Context) Service(name string) (Service, error) {
	manager, err := c.Manager()
	if err != nil {
		return nil, err
	}
	return manager.Service(name)
}

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
}

func (c *Context) State() common.Map {
	var result *common.Map
	if !c.Contains(stateKey) {
		aMap := NewDefaultState()
		result = &aMap
		c.Put(stateKey, result)
	} else {
		c.GetInto(stateKey, &result)
	}
	return *result
}

func (c *Context) SessionInfo() *SessionInfo {
	var result *SessionInfo
	if !c.Contains(sessionInfoKey) {

		result = &SessionInfo{}
		c.Put(sessionInfoKey, result)
	} else {
		c.GetInto(sessionInfoKey, &result)
	}
	return result
}

func (c *Context) Workflow() *Workflow {
	var result *Workflow
	if !c.Contains(workflowKey) {
		return nil
	} else {
		c.GetInto(workflowKey, &result)
	}
	return result
}

func (c *Context) OperatingSystem(sessionName string) *OperatingSystem {
	var sessions = c.Sessions()
	if session, has := sessions[sessionName]; has {
		return session.OperatingSystem
	}
	return nil
}

func (c *Context) ExecuteAsSuperUser(target *Resource, command *ManagedCommand) (*CommandInfo, 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) Execute(target *Resource, command interface{}) (*CommandInfo, error) {
	if command == nil {
		return nil, nil
	}
	var commandRequest *ManagedCommandRequest
	switch actualCommand := command.(type) {
	case *CommandRequest:
		actualCommand.Target = target
		commandRequest = actualCommand.AsManagedCommandRequest()
	case *ManagedCommand:
		commandRequest = NewCommandRequest(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.(*CommandInfo); ok {
		return commandResult, nil
	}
	return nil, nil
}

func (c *Context) Copy(expand bool, source, target *Resource) (interface{}, error) {
	return c.Transfer([]*Transfer{{
		Source: source,
		Target: target,
		Expand: expand}}...)
}

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
}

func (c *Context) Log(logEntry interface{}) error {
	sessionInfo := c.SessionInfo()
	return sessionInfo.Log(logEntry)
}

func (c *Context) Expand(text string) string {
	state := c.State()
	return ExpandAsText(state, text)
}

func (c *Context) AsRequest(serviceName, requestName string, source map[string]interface{}) (interface{}, error) {
	service, err := c.Service(serviceName)
	if err != nil {
		return nil, err
	}
	request, err := service.NewRequest(requestName)
	if err != nil {
		return nil, err
	}
	err = converter.AssignConverted(request, source)
	return request, err
}

func (c *Context) Close() {
	for _, function := range c.Deffer() {
		function()
	}
}

func NewDefaultState() common.Map {
	var result = common.NewMap()
	var now = time.Now()
	source := rand.NewSource(now.UnixNano())
	result.Put("endlyURL", "http://github.com/viant/endly")
	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
	})

	result.Put("env", func(key string) interface{} {
		return os.Getenv(key)
	})
	return result
}
<commit_msg>added uuid<commit_after>package endly

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/viant/endly/common"
	"github.com/viant/toolbox"
	"math/rand"
	"net/url"
	"os"
	"os/exec"
	uuid "github.com/satori/go.uuid"
	"path"
	"strings"
	"time"
)

//TODO Execution detail Tracking of all run (time taken, request, response)

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

var serviceManagerKey = (*manager)(nil)
var deferFunctionsKey = (*[]func())(nil)
var stateKey = (*common.Map)(nil)
var sessionInfoKey = (*SessionInfo)(nil)
var workflowKey = (*Workflow)(nil)

type Context struct {
	toolbox.Context
}

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

func (c *Context) ExpandResource(resource *Resource) (*Resource, error) {
	var err 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, "://") {
		if workflow := c.Workflow(); workflow != nil && workflow.source != nil {
			baseURL, _ := toolbox.URLSplit(workflow.source.URL)
			resource.URL = toolbox.URLPathJoin(baseURL, resource.URL)
		}
	}

	var result = &Resource{
		URL:         c.Expand(resource.URL),
		Credential:  c.Expand(resource.Credential),
		Name:        c.Expand(resource.Name),
		Version:     resource.Version,
		Type:        c.Expand(resource.Type),
		Cache:       c.Expand(resource.Cache),
		CacheExpiry: resource.CacheExpiry,
	}
	result.ParsedURL, err = url.Parse(result.URL)
	if err != nil {
		return nil, reportError(fmt.Errorf("Failed to parse URL: %v %v", result.URL, err))
	}
	return result, nil
}

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
}

func (c *Context) Sessions() ClientSessions {
	var result *ClientSessions
	if !c.Contains(clientSessionKey) {
		var sessions ClientSessions = make(map[string]*ClientSession)
		result = &sessions
		c.Put(clientSessionKey, result)
	} else {
		c.GetInto(clientSessionKey, &result)
	}
	return *result
}

func (c *Context) Service(name string) (Service, error) {
	manager, err := c.Manager()
	if err != nil {
		return nil, err
	}
	return manager.Service(name)
}

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
}

func (c *Context) State() common.Map {
	var result *common.Map
	if !c.Contains(stateKey) {
		aMap := NewDefaultState()
		result = &aMap
		c.Put(stateKey, result)
	} else {
		c.GetInto(stateKey, &result)
	}
	return *result
}

func (c *Context) SessionInfo() *SessionInfo {
	var result *SessionInfo
	if !c.Contains(sessionInfoKey) {

		result = &SessionInfo{}
		c.Put(sessionInfoKey, result)
	} else {
		c.GetInto(sessionInfoKey, &result)
	}
	return result
}

func (c *Context) Workflow() *Workflow {
	var result *Workflow
	if !c.Contains(workflowKey) {
		return nil
	} else {
		c.GetInto(workflowKey, &result)
	}
	return result
}

func (c *Context) OperatingSystem(sessionName string) *OperatingSystem {
	var sessions = c.Sessions()
	if session, has := sessions[sessionName]; has {
		return session.OperatingSystem
	}
	return nil
}

func (c *Context) ExecuteAsSuperUser(target *Resource, command *ManagedCommand) (*CommandInfo, 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) Execute(target *Resource, command interface{}) (*CommandInfo, error) {
	if command == nil {
		return nil, nil
	}
	var commandRequest *ManagedCommandRequest
	switch actualCommand := command.(type) {
	case *CommandRequest:
		actualCommand.Target = target
		commandRequest = actualCommand.AsManagedCommandRequest()
	case *ManagedCommand:
		commandRequest = NewCommandRequest(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.(*CommandInfo); ok {
		return commandResult, nil
	}
	return nil, nil
}

func (c *Context) Copy(expand bool, source, target *Resource) (interface{}, error) {
	return c.Transfer([]*Transfer{{
		Source: source,
		Target: target,
		Expand: expand}}...)
}

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
}

func (c *Context) Log(logEntry interface{}) error {
	sessionInfo := c.SessionInfo()
	return sessionInfo.Log(logEntry)
}

func (c *Context) Expand(text string) string {
	state := c.State()
	return ExpandAsText(state, text)
}

func (c *Context) AsRequest(serviceName, requestName string, source map[string]interface{}) (interface{}, error) {
	service, err := c.Service(serviceName)
	if err != nil {
		return nil, err
	}
	request, err := service.NewRequest(requestName)
	if err != nil {
		return nil, err
	}
	err = converter.AssignConverted(request, source)
	return request, err
}

func (c *Context) Close() {
	for _, function := range c.Deffer() {
		function()
	}
}

func NewDefaultState() common.Map {
	var result = common.NewMap()
	var now = time.Now()
	source := rand.NewSource(now.UnixNano())
	result.Put("endlyURL", "http://github.com/viant/endly")
	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 == "new" {
			cachedUUID = uuid.NewV1()
		}
		if len(cachedUUID) > 0 {
			return cachedUUID.String()
		}
		return ""
	})
	result.Put("env", func(key string) interface{} {
		return os.Getenv(key)
	})
	return result
}
<|endoftext|>


<commit_before>package continuity

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/docker/distribution/digest"
)

var (
	ErrNotFound = fmt.Errorf("not found")
)

type Context interface {
	Verify(Resource) error
	Resource(string, os.FileInfo) (Resource, error)
	Sanitize(string) (string, error)
	Walk(filepath.WalkFunc) error
}

// context represents a file system context for accessing resources.
// Generally, all path qualified access and system considerations should land
// here.
type context struct {
	root string

	// TODO(stevvooe): Define a "context driver" type that can be used to
	// switch out the backing context implementation. This should handle
	// paths, digesting files, opening files, resolving system specific
	// attributes in addition to being a place where we can isolate system
	// specific operations.
}

// NewContext returns a Context associated with root.
func NewContext(root string) (Context, error) {
	// normalize to absolute path
	root, err := filepath.Abs(filepath.Clean(root))
	if err != nil {
		return nil, err
	}

	// Check the root directory. Need to be a little careful here. We are
	// allowing a link for now, but this may have odd behavior when
	// canonicalizing paths. As long as all files are opened through the link
	// path, this should be okay.
	fi, err := os.Stat(root)
	if err != nil {
		return nil, err
	}

	if !fi.IsDir() {
		return nil, &os.PathError{Op: "NewContext", Path: root, Err: os.ErrInvalid}
	}

	return &context{root: root}, nil
}

// Sanitize validates that the path p points to a resource inside the context
// and sanitizes it. If the path cannot be sanitized or points outside of the
// context, an error is returned.
// TODO(stevvooe): This method name needs to be changed.
func (c *context) Sanitize(p string) (string, error) {
	return sanitize(c.root, p)
}

// Path returns the full path of p within the context. If the path escapes the
// context root, an error is returned.
func (c *context) Path(p string) (string, error) {
	p = filepath.Join(c.root, p)
	if !strings.HasPrefix(p, c.root) {
		return "", fmt.Errorf("invalid context path")
	}

	return p, nil
}

// Digest returns the digest of the file at path p, relative to the root.
func (c *context) Digest(p string) (digest.Digest, error) {
	return digestPath(filepath.Join(c.root, p))
}

// Resource returns the resource as path p, populating the entry with info
// from fi. The path p should be the path of the resource in the context,
// typically obtained from a call to Sanitize or the value of Resource.Path().
// If fi is nil, os.Lstat will be used resolve it.
func (c *context) Resource(p string, fi os.FileInfo) (Resource, error) {
	fp, err := c.Path(p)
	if err != nil {
		return nil, err
	}

	log.Println("resource", p, fp)
	if fi == nil {
		fi, err = os.Lstat(fp)
		if err != nil {
			return nil, err
		}
	}

	base, err := newBaseResource(p, fi)
	if err != nil {
		return nil, err
	}

	base.xattrs, err = c.resolveXAttrs(fp, fi, base)
	if err != nil {
		return nil, err
	}

	// TODO(stevvooe): Handle windows alternate data streams.

	if fi.Mode().IsRegular() {
		dgst, err := digestPath(fp)
		if err != nil {
			return nil, err
		}

		return newRegularFile(p, fi, base, dgst)
	}

	if fi.Mode().IsDir() {
		return newDirectory(p, fi, base)
	}

	if fi.Mode()&os.ModeSymlink != 0 {
		// We handle relative links vs absolute links by including a
		// beginning slash for absolute links. Effectively, the bundle's
		// root is treated as the absolute link anchor.
		target, err := os.Readlink(fp)
		if err != nil {
			return nil, err
		}

		// TODO(stevvooe): Re-do this when we have fully vetted path
		// management methods on context.
		if filepath.IsAbs(target) {

			// TODO(stevvooe): This path needs to be "sanitized" (or contained
			// or whatever we end up calling it).

			// When path is absolute, we make it relative to the bundle root.
			target, err = filepath.Rel(c.root, target)
			if err != nil {
				return nil, err
			}

			// now make the target absolute, since we want to maintain that.
			target = filepath.Join("/", target)
		} else {
			// make sure the target is contained in the root.
			real := filepath.Join(fp, target)
			if !strings.HasPrefix(real, c.root) {
				return nil, fmt.Errorf("link refers to file outside of root: %q -> %q, real = %q", fp, target, real)
			}
		}

		return newSymLink(p, fi, base, target)
	}

	if fi.Mode()&os.ModeNamedPipe != 0 {
		return newNamedPipe(p, fi, base)
	}

	if fi.Mode()&os.ModeDevice != 0 {
		// character and block devices merely need to recover the
		// major/minor device number.
		major, minor, err := deviceInfo(fi)
		if err != nil {
			return nil, err
		}

		return newDevice(p, fi, base, major, minor)
	}

	log.Printf("%q (%v) is not supported", fp, fi.Mode())
	return nil, ErrNotFound
}

// Verify the resource in the context. An error will be returned a discrepancy
// is found.
func (c *context) Verify(resource Resource) error {
	panic("not implemented")
}

// Apply the resource to the contexts. An error will be returned in the
// operation fails. Depending on the resource type, the resource may be
// created. For resource that cannot be resolved, an error will be returned.
func (c *context) Apply(resource Resource) error {
	panic("not implemented")
}

// Walk provides a convenience function to call filepath.Walk correctly for
// the context. The behavior is otherwise identical.
func (c *context) Walk(fn filepath.WalkFunc) error {
	return filepath.Walk(c.root, fn)
}

// resolveXAttrs attempts to resolve the extended attributes for the resource
// at the path fp, which is the full path to the resource. If the resource
// cannot have xattrs, nil will be returned.
func (c *context) resolveXAttrs(fp string, fi os.FileInfo, base *resource) (map[string][]byte, error) {
	// Restricts xattrs to only the below file types. This allowance of
	// symlinks is slightly questionable, since their support is spotty on
	// most file systems and xattrs are generally stored in the inode. This
	// may belong elsewhere.
	if !(fi.Mode().IsRegular() || fi.Mode().IsDir()) {
		return nil, nil
	}

	// TODO(stevvooe): This is very preliminary support for xattrs. We
	// still need to ensure that links aren't being followed.
	xattrs, err := Listxattr(fp)
	if err != nil {
		log.Println("error listing xattrs ", fp)
		return nil, err
	}

	sort.Strings(xattrs)
	m := make(map[string][]byte, len(xattrs))

	for _, attr := range xattrs {
		value, err := Getxattr(fp, attr)
		if err != nil {
			log.Printf("error getting xattrs: %v %q %v %v", fp, attr, xattrs, len(xattrs))
			return nil, err
		}

		// NOTE(stevvooe): This append/copy tricky relies on unique
		// xattrs. Break this out into an alloc/copy if xattrs are no
		// longer unique.
		m[attr] = append(base.xattrs[attr], value...)
	}

	return m, nil
}

// sanitize and clean the path relative to root.
func sanitize(root, p string) (string, error) {
	sanitized, err := filepath.Rel(root, p)
	if err != nil {
		return "", err
	}

	return filepath.Join("/", filepath.Clean(sanitized)), nil
}
<commit_msg>Add missing methods to Context<commit_after>package continuity

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

	"github.com/docker/distribution/digest"
)

var (
	ErrNotFound = fmt.Errorf("not found")
)

type Context interface {
	Apply(Resource) error
	Verify(Resource) error
	Resource(string, os.FileInfo) (Resource, error)
	Sanitize(string) (string, error)
	Walk(filepath.WalkFunc) error
	Digest(string) (digest.Digest, error)
}

// context represents a file system context for accessing resources.
// Generally, all path qualified access and system considerations should land
// here.
type context struct {
	root string

	// TODO(stevvooe): Define a "context driver" type that can be used to
	// switch out the backing context implementation. This should handle
	// paths, digesting files, opening files, resolving system specific
	// attributes in addition to being a place where we can isolate system
	// specific operations.
}

// NewContext returns a Context associated with root.
func NewContext(root string) (Context, error) {
	// normalize to absolute path
	root, err := filepath.Abs(filepath.Clean(root))
	if err != nil {
		return nil, err
	}

	// Check the root directory. Need to be a little careful here. We are
	// allowing a link for now, but this may have odd behavior when
	// canonicalizing paths. As long as all files are opened through the link
	// path, this should be okay.
	fi, err := os.Stat(root)
	if err != nil {
		return nil, err
	}

	if !fi.IsDir() {
		return nil, &os.PathError{Op: "NewContext", Path: root, Err: os.ErrInvalid}
	}

	return &context{root: root}, nil
}

// Sanitize validates that the path p points to a resource inside the context
// and sanitizes it. If the path cannot be sanitized or points outside of the
// context, an error is returned.
// TODO(stevvooe): This method name needs to be changed.
func (c *context) Sanitize(p string) (string, error) {
	return sanitize(c.root, p)
}

// Path returns the full path of p within the context. If the path escapes the
// context root, an error is returned.
func (c *context) Path(p string) (string, error) {
	p = filepath.Join(c.root, p)
	if !strings.HasPrefix(p, c.root) {
		return "", fmt.Errorf("invalid context path")
	}

	return p, nil
}

// Digest returns the digest of the file at path p, relative to the root.
func (c *context) Digest(p string) (digest.Digest, error) {
	return digestPath(filepath.Join(c.root, p))
}

// Resource returns the resource as path p, populating the entry with info
// from fi. The path p should be the path of the resource in the context,
// typically obtained from a call to Sanitize or the value of Resource.Path().
// If fi is nil, os.Lstat will be used resolve it.
func (c *context) Resource(p string, fi os.FileInfo) (Resource, error) {
	fp, err := c.Path(p)
	if err != nil {
		return nil, err
	}

	if fi == nil {
		fi, err = os.Lstat(fp)
		if err != nil {
			return nil, err
		}
	}

	base, err := newBaseResource(p, fi)
	if err != nil {
		return nil, err
	}

	base.xattrs, err = c.resolveXAttrs(fp, fi, base)
	if err != nil {
		return nil, err
	}

	// TODO(stevvooe): Handle windows alternate data streams.

	if fi.Mode().IsRegular() {
		dgst, err := digestPath(fp)
		if err != nil {
			return nil, err
		}

		return newRegularFile(p, fi, base, dgst)
	}

	if fi.Mode().IsDir() {
		return newDirectory(p, fi, base)
	}

	if fi.Mode()&os.ModeSymlink != 0 {
		// We handle relative links vs absolute links by including a
		// beginning slash for absolute links. Effectively, the bundle's
		// root is treated as the absolute link anchor.
		target, err := os.Readlink(fp)
		if err != nil {
			return nil, err
		}

		// TODO(stevvooe): Re-do this when we have fully vetted path
		// management methods on context.
		if filepath.IsAbs(target) {

			// TODO(stevvooe): This path needs to be "sanitized" (or contained
			// or whatever we end up calling it).

			// When path is absolute, we make it relative to the bundle root.
			target, err = filepath.Rel(c.root, target)
			if err != nil {
				return nil, err
			}

			// now make the target absolute, since we want to maintain that.
			target = filepath.Join("/", target)
		} else {
			// make sure the target is contained in the root.
			real := filepath.Join(fp, target)
			if !strings.HasPrefix(real, c.root) {
				return nil, fmt.Errorf("link refers to file outside of root: %q -> %q, real = %q", fp, target, real)
			}
		}

		return newSymLink(p, fi, base, target)
	}

	if fi.Mode()&os.ModeNamedPipe != 0 {
		return newNamedPipe(p, fi, base)
	}

	if fi.Mode()&os.ModeDevice != 0 {
		// character and block devices merely need to recover the
		// major/minor device number.
		major, minor, err := deviceInfo(fi)
		if err != nil {
			return nil, err
		}

		return newDevice(p, fi, base, major, minor)
	}

	log.Printf("%q (%v) is not supported", fp, fi.Mode())
	return nil, ErrNotFound
}

// Verify the resource in the context. An error will be returned a discrepancy
// is found.
func (c *context) Verify(resource Resource) error {
	panic("not implemented")
}

// Apply the resource to the contexts. An error will be returned in the
// operation fails. Depending on the resource type, the resource may be
// created. For resource that cannot be resolved, an error will be returned.
func (c *context) Apply(resource Resource) error {
	panic("not implemented")
}

// Walk provides a convenience function to call filepath.Walk correctly for
// the context. The behavior is otherwise identical.
func (c *context) Walk(fn filepath.WalkFunc) error {
	return filepath.Walk(c.root, fn)
}

// resolveXAttrs attempts to resolve the extended attributes for the resource
// at the path fp, which is the full path to the resource. If the resource
// cannot have xattrs, nil will be returned.
func (c *context) resolveXAttrs(fp string, fi os.FileInfo, base *resource) (map[string][]byte, error) {
	// Restricts xattrs to only the below file types. This allowance of
	// symlinks is slightly questionable, since their support is spotty on
	// most file systems and xattrs are generally stored in the inode. This
	// may belong elsewhere.
	if !(fi.Mode().IsRegular() || fi.Mode().IsDir()) {
		return nil, nil
	}

	// TODO(stevvooe): This is very preliminary support for xattrs. We
	// still need to ensure that links aren't being followed.
	xattrs, err := Listxattr(fp)
	if err != nil {
		log.Println("error listing xattrs ", fp)
		return nil, err
	}

	sort.Strings(xattrs)
	m := make(map[string][]byte, len(xattrs))

	for _, attr := range xattrs {
		value, err := Getxattr(fp, attr)
		if err != nil {
			log.Printf("error getting xattrs: %v %q %v %v", fp, attr, xattrs, len(xattrs))
			return nil, err
		}

		// NOTE(stevvooe): This append/copy tricky relies on unique
		// xattrs. Break this out into an alloc/copy if xattrs are no
		// longer unique.
		m[attr] = append(base.xattrs[attr], value...)
	}

	return m, nil
}

// sanitize and clean the path relative to root.
func sanitize(root, p string) (string, error) {
	sanitized, err := filepath.Rel(root, p)
	if err != nil {
		return "", err
	}

	return filepath.Join("/", filepath.Clean(sanitized)), nil
}
<|endoftext|>


<commit_before>package appkit

import (
	"fmt"
)

type Context struct {
	Data map[string]interface{}
}

func NewContext() Context {
	c := Context{}
	c.Data = make(map[string]interface{})

	return c
}

func (c Context) Get(key string) (interface{}, bool) {
	x, ok := c.Data[key]
	return x, ok
}

func (c Context) MustGet(key string) interface{} {
	x, ok := c.Data[key]
	if !ok {
		panic("Context does not have key " + key)
	}

	return x
}

func (c *Context) Set(key string, data interface{}) {
	c.Data[key] = data
}

func (c Context) String(key string) string {
	x, ok := c.Data[key]
	if !ok {
		return ""
	}

	str, ok := x.(string)
	if !ok {
		return ""
	}
	return str
}

func (c Context) MustString(key string) string {
	val := c.MustGet(key)
	str, ok := val.(string)
	if !ok {
		panic(fmt.Sprintf("Context key %v is not a string", key))
	}

	return str
}

func (c *Context) SetString(key, val string) {
	c.Data[key] = val
}
<commit_msg>added .Has and .Int to Context<commit_after>package appkit

import (
	"errors"
	"fmt"
	"strconv"
)

type Context struct {
	Data map[string]interface{}
}

func NewContext() Context {
	c := Context{}
	c.Data = make(map[string]interface{})

	return c
}

func (c Context) Has(key string) bool {
	_, ok := c.Data[key]
	return ok
}

func (c Context) Get(key string) (interface{}, bool) {
	x, ok := c.Data[key]
	return x, ok
}

func (c Context) MustGet(key string) interface{} {
	x, ok := c.Data[key]
	if !ok {
		panic("Context does not have key " + key)
	}

	return x
}

func (c *Context) Set(key string, data interface{}) {
	c.Data[key] = data
}

func (c Context) String(key string) string {
	x, ok := c.Data[key]
	if !ok {
		return ""
	}

	str, ok := x.(string)
	if !ok {
		return ""
	}
	return str
}

func (c Context) MustString(key string) string {
	val := c.MustGet(key)
	str, ok := val.(string)
	if !ok {
		panic(fmt.Sprintf("Context key %v is not a string", key))
	}

	return str
}

func (c *Context) SetString(key, val string) {
	c.Data[key] = val
}

// Retrieve an int value from the context.
// Will auto-convert string values.
func (c *Context) Int(key string) (int, error) {
	val, ok := c.Get(key)
	if !ok {
		return 0, errors.New("inexistant_key")
	}

	if intVal, ok := val.(int); ok {
		return intVal, nil
	}

	if strVal, ok := val.(string); ok {
		intVal, err := strconv.ParseInt(strVal, 10, 64)
		if err != nil {
			return 0, err
		}

		return int(intVal), nil
	}

	return 0, errors.New("cant_convert_to_int")
}
<|endoftext|>