<commit_before>// Ogo

package ogo

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"time"

	"github.com/Odinman/gorp"
	_ "github.com/go-sql-driver/mysql"
)

var (
	DataAccessor = make(map[string]string) // tablename::{read/write} => tag
)

const (
	DBTAG      string = "db"
	READTAG    string = "read"
	WRITETAG   string = "write"
	base       string = "0000-00-00 00:00:00.0000000"
	timeFormat string = "2006-01-02 15:04:05.999999"
)

//出/入库转换器
type SelfConverter interface {
	ToDb() (interface{}, error)                    //入库
	FromDb(interface{}) (gorp.CustomScanner, bool) //出库
}

type BaseConverter struct{}

/* {{{ func OpenDB(tag,dns string) error
 *
 */
func OpenDB(tag, dns string) (err error) {
	Debug("open mysql: %s,%s", tag, dns)
	gorp.TraceOn(fmt.Sprintf("[%s]", tag), Logger())
	gorp.SetTypeConvert(BaseConverter{})
	if err = gorp.Open(tag, "mysql", dns); err != nil {
		Debug("open error: %s", err)
	}
	return
}

/* }}} */

/* {{{ func (_ BaseConverter) ToDb(val interface{}) (interface{}, error)
 *
 */
func (_ BaseConverter) ToDb(val interface{}) (interface{}, error) {
	switch t := val.(type) {
	case *[]string, []string, map[string]string, *map[string]string: //转为字符串
		c, _ := json.Marshal(t)
		return string(c), nil
	default:
		// 自定义的类型,如果实现了SelfConverter接口,则这里自动执行
		if _, ok := val.(SelfConverter); ok {
			Trace("selfconvert todb")
			return val.(SelfConverter).ToDb()
		} else if _, ok := reflect.Indirect(reflect.ValueOf(val)).Interface().(SelfConverter); ok { //如果采用了指针, 则到这里
			Trace("prt selfconvert todb")
			return val.(SelfConverter).ToDb()
		} else {
			Trace("not selfconvert todb")
		}
	}
	return val, nil
}

/* }}} */

/* {{{ func (_ BaseConverter) FromDb(target interface{}) (gorp.CustomScanner, bool)
 * 类型转换, 主要处理标准类型; 自定义类型通过SelfConverter实现
 */
func (_ BaseConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
	switch t := target.(type) {
	case **time.Time:
		binder := func(holder, target interface{}) error {
			var err error
			if holder.(*sql.NullString).Valid {
				var dt time.Time
				str := holder.(*sql.NullString).String
				switch len(str) {
				case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
					if str == base[:len(str)] {
						return nil
					}
					dt, err = time.ParseInLocation(timeFormat[:len(str)], str, time.Local)
				default:
					err = fmt.Errorf("Invalid Time-String: %s", str)
					return err
				}
				if err != nil {
					return err
				}
				//dt = dt.UTC()
				dt = dt.Local()
				*(target.(**time.Time)) = &dt
				return nil
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case *time.Time:
		binder := func(holder, target interface{}) error {
			var err error
			if holder.(*sql.NullString).Valid {
				var dt time.Time
				str := holder.(*sql.NullString).String
				switch len(str) {
				case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
					if str == base[:len(str)] {
						return nil
					}
					dt, err = time.ParseInLocation(timeFormat[:len(str)], str, time.Local)
				default:
					err = fmt.Errorf("Invalid Time-String: %s", str)
					return err
				}
				if err != nil {
					return err
				}
				//dt = dt.UTC()
				dt = dt.Local()
				*(target.(*time.Time)) = dt
				return nil
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **[]string:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st []string
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(**[]string)) = &st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case *[]string:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st []string
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(*[]string)) = st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **map[string]string:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st map[string]string
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(**map[string]string)) = &st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case *map[string]string:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st map[string]string
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(*map[string]string)) = st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **string:
		binder := func(holder, target interface{}) error {
			*t = &holder.(*sql.NullString).String
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **float64:
		binder := func(holder, target interface{}) error {
			*t = &holder.(*sql.NullFloat64).Float64
			return nil
		}
		return gorp.CustomScanner{new(sql.NullFloat64), target, binder}, true

	case **int64:
		binder := func(holder, target interface{}) error {
			*t = &holder.(*sql.NullInt64).Int64
			return nil
		}
		return gorp.CustomScanner{new(sql.NullInt64), target, binder}, true
	default:
		// 自定义的类型,如果实现了SelfConverter接口,则这里自动执行
		if t, ok := target.(SelfConverter); ok {
			Trace("selfconvert begin(value)")
			return t.FromDb(target)
		} else if t, ok := reflect.Indirect(reflect.ValueOf(target)).Interface().(SelfConverter); ok { //如果采用了指针, 则到这里
			Trace("ptr converter: %s", target)
			return t.FromDb(target)
		} else {
			Trace("no converter: %s", target)
		}
	}
	return gorp.CustomScanner{}, false
}

/* }}} */
<commit_msg>add map[string]interface{}<commit_after>// Ogo

package ogo

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"time"

	"github.com/Odinman/gorp"
	_ "github.com/go-sql-driver/mysql"
)

var (
	DataAccessor = make(map[string]string) // tablename::{read/write} => tag
)

const (
	DBTAG      string = "db"
	READTAG    string = "read"
	WRITETAG   string = "write"
	base       string = "0000-00-00 00:00:00.0000000"
	timeFormat string = "2006-01-02 15:04:05.999999"
)

//出/入库转换器
type SelfConverter interface {
	ToDb() (interface{}, error)                    //入库
	FromDb(interface{}) (gorp.CustomScanner, bool) //出库
}

type BaseConverter struct{}

/* {{{ func OpenDB(tag,dns string) error
 *
 */
func OpenDB(tag, dns string) (err error) {
	Debug("open mysql: %s,%s", tag, dns)
	gorp.TraceOn(fmt.Sprintf("[%s]", tag), Logger())
	gorp.SetTypeConvert(BaseConverter{})
	if err = gorp.Open(tag, "mysql", dns); err != nil {
		Debug("open error: %s", err)
	}
	return
}

/* }}} */

/* {{{ func (_ BaseConverter) ToDb(val interface{}) (interface{}, error)
 *
 */
func (_ BaseConverter) ToDb(val interface{}) (interface{}, error) {
	switch t := val.(type) {
	case *[]string, []string, map[string]string, *map[string]string, map[string]interface{}, *map[string]interface{}: //转为字符串
		c, _ := json.Marshal(t)
		return string(c), nil
	default:
		// 自定义的类型,如果实现了SelfConverter接口,则这里自动执行
		if _, ok := val.(SelfConverter); ok {
			Trace("selfconvert todb")
			return val.(SelfConverter).ToDb()
		} else if _, ok := reflect.Indirect(reflect.ValueOf(val)).Interface().(SelfConverter); ok { //如果采用了指针, 则到这里
			Trace("prt selfconvert todb")
			return val.(SelfConverter).ToDb()
		} else {
			Trace("not selfconvert todb")
		}
	}
	return val, nil
}

/* }}} */

/* {{{ func (_ BaseConverter) FromDb(target interface{}) (gorp.CustomScanner, bool)
 * 类型转换, 主要处理标准类型; 自定义类型通过SelfConverter实现
 */
func (_ BaseConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
	switch t := target.(type) {
	case **time.Time:
		binder := func(holder, target interface{}) error {
			var err error
			if holder.(*sql.NullString).Valid {
				var dt time.Time
				str := holder.(*sql.NullString).String
				switch len(str) {
				case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
					if str == base[:len(str)] {
						return nil
					}
					dt, err = time.ParseInLocation(timeFormat[:len(str)], str, time.Local)
				default:
					err = fmt.Errorf("Invalid Time-String: %s", str)
					return err
				}
				if err != nil {
					return err
				}
				//dt = dt.UTC()
				dt = dt.Local()
				*(target.(**time.Time)) = &dt
				return nil
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case *time.Time:
		binder := func(holder, target interface{}) error {
			var err error
			if holder.(*sql.NullString).Valid {
				var dt time.Time
				str := holder.(*sql.NullString).String
				switch len(str) {
				case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
					if str == base[:len(str)] {
						return nil
					}
					dt, err = time.ParseInLocation(timeFormat[:len(str)], str, time.Local)
				default:
					err = fmt.Errorf("Invalid Time-String: %s", str)
					return err
				}
				if err != nil {
					return err
				}
				//dt = dt.UTC()
				dt = dt.Local()
				*(target.(*time.Time)) = dt
				return nil
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **[]string:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st []string
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(**[]string)) = &st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case *[]string:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st []string
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(*[]string)) = st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **map[string]string:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st map[string]string
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(**map[string]string)) = &st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case *map[string]string:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st map[string]string
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(*map[string]string)) = st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **map[string]interface{}:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st map[string]interface{}
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(**map[string]interface{})) = &st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case *map[string]interface{}:
		binder := func(holder, target interface{}) error {
			if holder.(*sql.NullString).Valid {
				var st map[string]interface{}
				str := holder.(*sql.NullString).String
				//ogo.Debug("str: %s", str)
				if err := json.Unmarshal([]byte(str), &st); err != nil {
					return err
				}
				*(target.(*map[string]interface{})) = st
			}
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **string:
		binder := func(holder, target interface{}) error {
			*t = &holder.(*sql.NullString).String
			return nil
		}
		return gorp.CustomScanner{new(sql.NullString), target, binder}, true
	case **float64:
		binder := func(holder, target interface{}) error {
			*t = &holder.(*sql.NullFloat64).Float64
			return nil
		}
		return gorp.CustomScanner{new(sql.NullFloat64), target, binder}, true

	case **int64:
		binder := func(holder, target interface{}) error {
			*t = &holder.(*sql.NullInt64).Int64
			return nil
		}
		return gorp.CustomScanner{new(sql.NullInt64), target, binder}, true
	default:
		// 自定义的类型,如果实现了SelfConverter接口,则这里自动执行
		if t, ok := target.(SelfConverter); ok {
			Trace("selfconvert begin(value)")
			return t.FromDb(target)
		} else if t, ok := reflect.Indirect(reflect.ValueOf(target)).Interface().(SelfConverter); ok { //如果采用了指针, 则到这里
			Trace("ptr converter: %s", target)
			return t.FromDb(target)
		} else {
			Trace("no converter: %s", target)
		}
	}
	return gorp.CustomScanner{}, false
}

/* }}} */
<|endoftext|>


<commit_before>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

package service

import (
	"fmt"
)

// DataClient represents the RPC connection to the Data service.
type DataClient struct {
	*Client
}

// NewDataClient returns a new Data Client.
func NewDataClient() *DataClient {
	var service_ DataClient
	service_.Client = new(Client)
	return &service_
}

// GetNode returns the given node or nil if it does not exist.
func (s *DataClient) GetNode(site, path string) (*NodeInfo, error) {
	if s.Error != nil {
		return nil, s.Error
	}
	args := struct{ Site, Path string }{site, path}
	var reply *NodeInfo
	err := s.RPCClient.Call("Data.GetChildren", args, &reply)
	if err != nil {
		return nil, fmt.Errorf("service: GetChildren error:", err)
	}
	return reply, nil
}

// GetChildren returns the children of the given node.
func (s *DataClient) GetChildren(site, path string) ([]*NodeInfo, error) {
	if s.Error != nil {
		return nil, s.Error
	}
	args := struct{ Site, Path string }{site, path}
	var reply []*NodeInfo
	err := s.RPCClient.Call("Data.GetChildren", args, &reply)
	if err != nil {
		return nil, fmt.Errorf("service: GetChildren error:", err)
	}
	return reply, nil
}

type GetNodeDataArgs struct {
	Site, Path, File string
}

// GetNodeData requests data from some node.
//
// Returns a nil slice and nil error if the data does not exist.
func (s *DataClient) GetNodeData(site, path, file string) ([]byte, error) {
	if s.Error != nil {
		return nil, s.Error
	}
	args := &GetNodeDataArgs{site, path, file}
	var reply []byte
	err := s.RPCClient.Call("Data.GetNodeData", args, &reply)
	if err != nil {
		return nil, fmt.Errorf("service: GetNodeData error:", err)
	}
	return reply, nil
}

type WriteNodeDataArgs struct {
	Site, Path, File, Content string
}

// WriteNodeData writes data for some node.
func (s *DataClient) WriteNodeData(site, path, file, content string) error {
	if s.Error != nil {
		return nil
	}
	args := &WriteNodeDataArgs{site, path, file, content}
	if err := s.RPCClient.Call("Data.WriteNodeData", args, new(int)); err != nil {
		return fmt.Errorf("service: WriteNodeData error:", err)
	}
	return nil
}

type UpdateNodeArgs struct {
	Site string
	Node NodeInfo
}

// UpdateNode saves changes to given node.
func (s *DataClient) UpdateNode(site string, node_ NodeInfo) error {
	if s.Error != nil {
		return nil
	}
	args := &UpdateNodeArgs{site, node_}
	if err := s.RPCClient.Call("Data.UpdateNode", args, new(int)); err != nil {
		return fmt.Errorf("service: UpdateNode error:", err)
	}
	return nil
}

// RemoveNode recursively removes the given site's node.
func (s *DataClient) RemoveNode(site string, node string) error {
	if s.Error != nil {
		return nil
	}
	args := struct {
		Site, Node string
	}{site, node}
	if err := s.RPCClient.Call("Data.RemoveNode", args, new(int)); err != nil {
		return fmt.Errorf("service: RemoveNode error:", err)
	}
	return nil
}
<commit_msg>Clean up code and fix error construction.<commit_after>// This file is part of Monsti, a web content management system.
// Copyright 2012-2013 Christian Neumann
//
// Monsti is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// Monsti is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Monsti.  If not, see <http://www.gnu.org/licenses/>.

package service

import (
	"fmt"
)

// DataClient represents the RPC connection to the Data service.
type DataClient struct {
	*Client
}

// NewDataClient returns a new Data Client.
func NewDataClient() *DataClient {
	var service_ DataClient
	service_.Client = new(Client)
	return &service_
}

// GetNode returns the given node or nil if it does not exist.
func (s *DataClient) GetNode(site, path string) (*NodeInfo, error) {
	if s.Error != nil {
		return nil, s.Error
	}
	args := struct{ Site, Path string }{site, path}
	var reply *NodeInfo
	err := s.RPCClient.Call("Data.GetNode", args, &reply)
	if err != nil {
		return nil, fmt.Errorf("service: GetNode error: %v", err)
	}
	return reply, nil
}

// GetChildren returns the children of the given node.
func (s *DataClient) GetChildren(site, path string) ([]*NodeInfo, error) {
	if s.Error != nil {
		return nil, s.Error
	}
	args := struct{ Site, Path string }{site, path}
	var reply []*NodeInfo
	err := s.RPCClient.Call("Data.GetChildren", args, &reply)
	if err != nil {
		return nil, fmt.Errorf("service: GetChildren error: %v", err)
	}
	return reply, nil
}

// GetNodeData requests data from some node.
//
// Returns a nil slice and nil error if the data does not exist.
func (s *DataClient) GetNodeData(site, path, file string) ([]byte, error) {
	if s.Error != nil {
		return nil, s.Error
	}
	type GetNodeDataArgs struct {
	}
	args := struct{ Site, Path, File string }{
		site, path, file}
	var reply []byte
	err := s.RPCClient.Call("Data.GetNodeData", &args, &reply)
	if err != nil {
		return nil, fmt.Errorf("service: GetNodeData error:", err)
	}
	return reply, nil
}

// WriteNodeData writes data for some node.
func (s *DataClient) WriteNodeData(site, path, file, content string) error {
	if s.Error != nil {
		return nil
	}
	args := struct {
		Site, Path, File, Content string
	}{
		site, path, file, content}
	if err := s.RPCClient.Call("Data.WriteNodeData", &args, new(int)); err != nil {
		return fmt.Errorf("service: WriteNodeData error: %v", err)
	}
	return nil
}

// UpdateNode saves changes to given node.
func (s *DataClient) UpdateNode(site string, node_ NodeInfo) error {
	if s.Error != nil {
		return nil
	}
	args := struct {
		Site string
		Node NodeInfo
	}{
		site, node_}
	if err := s.RPCClient.Call("Data.UpdateNode", &args, new(int)); err != nil {
		return fmt.Errorf("service: UpdateNode error: %v", err)
	}
	return nil
}

// RemoveNode recursively removes the given site's node.
func (s *DataClient) RemoveNode(site string, node string) error {
	if s.Error != nil {
		return nil
	}
	args := struct {
		Site, Node string
	}{site, node}
	if err := s.RPCClient.Call("Data.RemoveNode", args, new(int)); err != nil {
		return fmt.Errorf("service: RemoveNode error: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package departure

import (
	"time"
	"fmt"
	"net/http"
	"encoding/json"
)

type direction struct {
	Status string
	Routes []struct {
		Legs []struct {
			Steps [] struct {
				TransitDetails struct {
					DepartureTime struct {
						Value int64
					} `json:"departure_time"`
					Line struct {
						ShortName string `json:"short_name"`
					}
				} `json:"transit_details"`
			}
		}
	}
}

func (d *direction) getDepartureTime(lineName string) (time.Time, error) {
	if d.Status != "OK" {
		return time.Time{}, fmt.Errorf("direction status was not OK but %s", d.Status)
	}

	for _, route := range d.Routes {
		if len(route.Legs) == 1 && len(route.Legs[0].Steps) == 1 && route.Legs[0].Steps[0].TransitDetails.Line.ShortName == lineName {
			departureTime := time.Unix(route.Legs[0].Steps[0].TransitDetails.DepartureTime.Value, 0)

			return departureTime, nil
		}
	}

	return time.Time{}, fmt.Errorf("No route found for line %s", lineName)
}

func GetDepartureTime(origin, destination, apiKey, transitMode, lineName string) (time.Time, error) {
	url := createURL(origin, destination, apiKey, transitMode)
	var direction direction

	getJson(url, &direction)

	depTime, err := direction.getDepartureTime(lineName)

	return depTime, err
}

func createURL(origin, destination, apiKey, transitMode string) string {
	baseUrl := "https://maps.googleapis.com/maps/api/directions/json"
	url := fmt.Sprintf("%s?origin=%s&destination=%s&key=%s&mode=transit&transit_mode=%s&language=en&alternatives=true", baseUrl, origin, destination, apiKey, transitMode)

	return url
}

func getJson(url string, target interface{}) error {
	r, err := http.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()

	return json.NewDecoder(r.Body).Decode(target)
}
<commit_msg>Escape URL parameters<commit_after>package departure

import (
	"time"
	"fmt"
	"net/http"
	"encoding/json"
	"net/url"
)

type direction struct {
	Status string
	Routes []struct {
		Legs []struct {
			Steps [] struct {
				TransitDetails struct {
					DepartureTime struct {
						Value int64
					} `json:"departure_time"`
					Line struct {
						ShortName string `json:"short_name"`
					}
				} `json:"transit_details"`
			}
		}
	}
}

func (d *direction) getDepartureTime(lineName string) (time.Time, error) {
	if d.Status != "OK" {
		return time.Time{}, fmt.Errorf("direction status was not OK but %s", d.Status)
	}

	for _, route := range d.Routes {
		if len(route.Legs) == 1 && len(route.Legs[0].Steps) == 1 && route.Legs[0].Steps[0].TransitDetails.Line.ShortName == lineName {
			departureTime := time.Unix(route.Legs[0].Steps[0].TransitDetails.DepartureTime.Value, 0)

			return departureTime, nil
		}
	}

	return time.Time{}, fmt.Errorf("No route found for line %s", lineName)
}

func GetDepartureTime(origin, destination, apiKey, transitMode, lineName string) (time.Time, error) {
	query := createQuery(origin, destination, apiKey, transitMode)

	var direction direction
	getJson(query, &direction)

	depTime, err := direction.getDepartureTime(lineName)

	return depTime, err
}

func createQuery(origin, destination, apiKey, transitMode string) string {
	baseUrl := "https://maps.googleapis.com/maps/api/directions/json"
	query := fmt.Sprintf(
		"%s?origin=%s&destination=%s&key=%s&mode=transit&transit_mode=%s&language=en&alternatives=true",
		baseUrl,
		url.QueryEscape(origin),
		url.QueryEscape(destination),
		url.QueryEscape(apiKey),
		url.QueryEscape(transitMode),
	)

	return query
}

func getJson(url string, target interface{}) error {
	r, err := http.Get(url)
	if err != nil {
		return err
	}
	defer r.Body.Close()

	return json.NewDecoder(r.Body).Decode(target)
}
<|endoftext|>


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

package api

import (
	"net/http"
	"strconv"
	"time"

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

//LogAPI handles request api/logs
type LogAPI struct {
	api.BaseAPI
	userID int
}

//Prepare validates the URL and the user
func (l *LogAPI) Prepare() {
	l.userID = l.ValidateUser()
}

//Get returns the recent logs according to parameters
func (l *LogAPI) Get() {
	var err error
	startTime := l.GetString("start_time")
	if len(startTime) != 0 {
		i, err := strconv.ParseInt(startTime, 10, 64)
		if err != nil {
			log.Errorf("Parse startTime to int error, err: %v", err)
			l.CustomAbort(http.StatusBadRequest, "startTime is not a valid integer")
		}
		startTime = time.Unix(i, 0).String()
	}

	endTime := l.GetString("end_time")
	if len(endTime) != 0 {
		j, err := strconv.ParseInt(endTime, 10, 64)
		if err != nil {
			log.Errorf("Parse endTime to int error, err: %v", err)
			l.CustomAbort(http.StatusBadRequest, "endTime is not a valid integer")
		}
		endTime = time.Unix(j, 0).String()
	}

	var linesNum int
	lines := l.GetString("lines")
	if len(lines) != 0 {
		linesNum, err = strconv.Atoi(lines)
		if err != nil {
			log.Errorf("Get parameters error--lines, err: %v", err)
			l.CustomAbort(http.StatusBadRequest, "bad request of lines")
		}
		if linesNum <= 0 {
			log.Warning("lines must be a positive integer")
			l.CustomAbort(http.StatusBadRequest, "lines is 0 or negative")
		}
	} else if len(startTime) == 0 && len(endTime) == 0 {
		linesNum = 10
	}

	user, err := dao.GetUser(models.User{
		UserID: l.userID,
	})
	if err != nil {
		log.Errorf("failed to get user by user ID %d: %v", l.userID, err)
		l.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
	}

	var logList []models.AccessLog
	logList, err = dao.GetRecentLogs(user.Username, linesNum, startTime, endTime)
	if err != nil {
		log.Errorf("Get recent logs error, err: %v", err)
		l.CustomAbort(http.StatusInternalServerError, "Internal error")
	}
	l.Data["json"] = logList
	l.ServeJSON()
}
<commit_msg>refactor log API<commit_after>// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
	"net/http"
	"strconv"
	"time"

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

//LogAPI handles request api/logs
type LogAPI struct {
	BaseController
}

//Prepare validates the URL and the user
func (l *LogAPI) Prepare() {
	l.BaseController.Prepare()
	if !l.SecurityCtx.IsAuthenticated() {
		l.HandleUnauthorized()
		return
	}
}

//Get returns the recent logs according to parameters
func (l *LogAPI) Get() {
	var err error
	startTime := l.GetString("start_time")
	if len(startTime) != 0 {
		i, err := strconv.ParseInt(startTime, 10, 64)
		if err != nil {
			log.Errorf("Parse startTime to int error, err: %v", err)
			l.CustomAbort(http.StatusBadRequest, "startTime is not a valid integer")
		}
		startTime = time.Unix(i, 0).String()
	}

	endTime := l.GetString("end_time")
	if len(endTime) != 0 {
		j, err := strconv.ParseInt(endTime, 10, 64)
		if err != nil {
			log.Errorf("Parse endTime to int error, err: %v", err)
			l.CustomAbort(http.StatusBadRequest, "endTime is not a valid integer")
		}
		endTime = time.Unix(j, 0).String()
	}

	var linesNum int
	lines := l.GetString("lines")
	if len(lines) != 0 {
		linesNum, err = strconv.Atoi(lines)
		if err != nil {
			log.Errorf("Get parameters error--lines, err: %v", err)
			l.CustomAbort(http.StatusBadRequest, "bad request of lines")
		}
		if linesNum <= 0 {
			log.Warning("lines must be a positive integer")
			l.CustomAbort(http.StatusBadRequest, "lines is 0 or negative")
		}
	} else if len(startTime) == 0 && len(endTime) == 0 {
		linesNum = 10
	}

	var logList []models.AccessLog
	logList, err = dao.GetRecentLogs(l.SecurityCtx.GetUsername(), linesNum, startTime, endTime)
	if err != nil {
		log.Errorf("Get recent logs error, err: %v", err)
		l.CustomAbort(http.StatusInternalServerError, "Internal error")
	}
	l.Data["json"] = logList
	l.ServeJSON()
}
<|endoftext|>


<commit_before>// Copyright 2013 Joyent Inc.
// Licensed under the AGPLv3, see LICENCE file for details.

package joyent

import (
	"sync"

	"github.com/joyent/gosign/auth"

	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/imagemetadata"
	"launchpad.net/juju-core/environs/simplestreams"
	"launchpad.net/juju-core/environs/storage"
	"launchpad.net/juju-core/provider/common"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
)

// This file contains the core of the Joyent Environ implementation.

type JoyentEnviron struct {
	name string

	// supportedArchitectures caches the architectures
	// for which images can be instantiated.
	supportedArchitectures []string

	// All mutating operations should lock the mutex. Non-mutating operations
	// should read all fields (other than name, which is immutable) from a
	// shallow copy taken with getSnapshot().
	// This advice is predicated on the goroutine-safety of the values of the
	// affected fields.
	lock    sync.Mutex
	ecfg    *environConfig
	creds   *auth.Credentials
	storage storage.Storage
	compute *joyentCompute
}

var _ environs.Environ = (*JoyentEnviron)(nil)

func NewEnviron(cfg *config.Config) (*JoyentEnviron, error) {
	env := new(JoyentEnviron)
	err := env.SetConfig(cfg)
	if err != nil {
		return nil, err
	}
	env.name = cfg.Name()
	env.creds = getCredentials(env)
	env.storage = NewStorage(env, "")
	env.compute = NewCompute(env)
	return env, nil
}

func (env *JoyentEnviron) SetName(envName string) {
	env.name = envName
}

func (env *JoyentEnviron) Name() string {
	return env.name
}

func (*JoyentEnviron) Provider() environs.EnvironProvider {
	return providerInstance
}

// SupportedArchitectures is specified on the EnvironCapability interface.
func (env *JoyentEnviron) SupportedArchitectures() ([]string, error) {
	env.lock.Lock()
	defer env.lock.Unlock()
	if env.supportedArchitectures != nil {
		return env.supportedArchitectures, nil
	}
	cfg := env.Ecfg()
	// Create a filter to get all images from our region and for the correct stream.
	cloudSpec := simplestreams.CloudSpec{
		Region:   cfg.Region(),
		Endpoint: cfg.SdcUrl(),
	}
	imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{
		CloudSpec: cloudSpec,
		Stream:    cfg.ImageStream(),
	})
	var err error
	env.supportedArchitectures, err = common.SupportedArchitectures(env, imageConstraint)
	return env.supportedArchitectures, err
}

func (env *JoyentEnviron) SetConfig(cfg *config.Config) error {
	env.lock.Lock()
	defer env.lock.Unlock()
	ecfg, err := providerInstance.newConfig(cfg)
	if err != nil {
		return err
	}
	env.ecfg = ecfg
	return nil
}

func (env *JoyentEnviron) getSnapshot() *JoyentEnviron {
	env.lock.Lock()
	clone := *env
	env.lock.Unlock()
	clone.lock = sync.Mutex{}
	return &clone
}

func (env *JoyentEnviron) Config() *config.Config {
	return env.getSnapshot().ecfg.Config
}

func (env *JoyentEnviron) Storage() storage.Storage {
	return env.getSnapshot().storage
}

func (env *JoyentEnviron) PublicStorage() storage.StorageReader {
	return environs.EmptyStorage
}

func (env *JoyentEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error {
	return common.Bootstrap(ctx, env, cons)
}

func (env *JoyentEnviron) StateInfo() (*state.Info, *api.Info, error) {
	return common.StateInfo(env)
}

func (env *JoyentEnviron) Destroy() error {
	return common.Destroy(env)
}

func (env *JoyentEnviron) Ecfg() *environConfig {
	return env.getSnapshot().ecfg
}

func (env *JoyentEnviron) Credentials() *auth.Credentials {
	return env.getSnapshot().creds
}

func (env *JoyentEnviron) SetCredentials() {
	env.creds = getCredentials(env)
}

func getCredentials(env *JoyentEnviron) *auth.Credentials {
	authentication := auth.Auth{User: env.ecfg.mantaUser(), KeyFile: env.ecfg.keyFile(), Algorithm: env.ecfg.algorithm()}

	return &auth.Credentials{
		UserAuthentication: authentication,
		MantaKeyId:         env.ecfg.mantaKeyId(),
		MantaEndpoint:      auth.Endpoint{URL: env.ecfg.mantaUrl()},
		SdcKeyId:           env.ecfg.sdcKeyId(),
		SdcEndpoint:        auth.Endpoint{URL: env.ecfg.sdcUrl()},
	}
}

// MetadataLookupParams returns parameters which are used to query simplestreams metadata.
func (env *JoyentEnviron) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) {
	if region == "" {
		region = env.Ecfg().Region()
	}
	return &simplestreams.MetadataLookupParams{
		Series:        env.Ecfg().DefaultSeries(),
		Region:        region,
		Endpoint:      env.Ecfg().sdcUrl(),
		Architectures: []string{"amd64", "arm"},
	}, nil
}

// Region is specified in the HasRegion interface.
func (env *JoyentEnviron) Region() (simplestreams.CloudSpec, error) {
	return simplestreams.CloudSpec{
		Region:   env.Ecfg().Region(),
		Endpoint: env.Ecfg().sdcUrl(),
	}, nil
}

// GetImageSources returns a list of sources which are used to search for simplestreams image metadata.
func (env *JoyentEnviron) GetImageSources() ([]simplestreams.DataSource, error) {
	// Add the simplestreams source off the control bucket.
	sources := []simplestreams.DataSource{
		storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseImagesPath)}
	return sources, nil
}

// GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.
func (env *JoyentEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {
	// Add the simplestreams source off the control bucket.
	sources := []simplestreams.DataSource{
		storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseToolsPath)}
	return sources, nil
}
<commit_msg>Fix supported arch deadlock<commit_after>// Copyright 2013 Joyent Inc.
// Licensed under the AGPLv3, see LICENCE file for details.

package joyent

import (
	"sync"

	"github.com/joyent/gosign/auth"

	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/imagemetadata"
	"launchpad.net/juju-core/environs/simplestreams"
	"launchpad.net/juju-core/environs/storage"
	"launchpad.net/juju-core/provider/common"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
)

// This file contains the core of the Joyent Environ implementation.

type JoyentEnviron struct {
	name string

	// supportedArchitectures caches the architectures
	// for which images can be instantiated.
	archLock               sync.Mutex
	supportedArchitectures []string

	// All mutating operations should lock the mutex. Non-mutating operations
	// should read all fields (other than name, which is immutable) from a
	// shallow copy taken with getSnapshot().
	// This advice is predicated on the goroutine-safety of the values of the
	// affected fields.
	lock    sync.Mutex
	ecfg    *environConfig
	creds   *auth.Credentials
	storage storage.Storage
	compute *joyentCompute
}

var _ environs.Environ = (*JoyentEnviron)(nil)

func NewEnviron(cfg *config.Config) (*JoyentEnviron, error) {
	env := new(JoyentEnviron)
	err := env.SetConfig(cfg)
	if err != nil {
		return nil, err
	}
	env.name = cfg.Name()
	env.creds = getCredentials(env)
	env.storage = NewStorage(env, "")
	env.compute = NewCompute(env)
	return env, nil
}

func (env *JoyentEnviron) SetName(envName string) {
	env.name = envName
}

func (env *JoyentEnviron) Name() string {
	return env.name
}

func (*JoyentEnviron) Provider() environs.EnvironProvider {
	return providerInstance
}

// SupportedArchitectures is specified on the EnvironCapability interface.
func (env *JoyentEnviron) SupportedArchitectures() ([]string, error) {
	env.archLock.Lock()
	defer env.archLock.Unlock()
	if env.supportedArchitectures != nil {
		return env.supportedArchitectures, nil
	}
	cfg := env.Ecfg()
	// Create a filter to get all images from our region and for the correct stream.
	cloudSpec := simplestreams.CloudSpec{
		Region:   cfg.Region(),
		Endpoint: cfg.SdcUrl(),
	}
	imageConstraint := imagemetadata.NewImageConstraint(simplestreams.LookupParams{
		CloudSpec: cloudSpec,
		Stream:    cfg.ImageStream(),
	})
	var err error
	env.supportedArchitectures, err = common.SupportedArchitectures(env, imageConstraint)
	return env.supportedArchitectures, err
}

func (env *JoyentEnviron) SetConfig(cfg *config.Config) error {
	env.lock.Lock()
	defer env.lock.Unlock()
	ecfg, err := providerInstance.newConfig(cfg)
	if err != nil {
		return err
	}
	env.ecfg = ecfg
	return nil
}

func (env *JoyentEnviron) getSnapshot() *JoyentEnviron {
	env.lock.Lock()
	clone := *env
	env.lock.Unlock()
	clone.lock = sync.Mutex{}
	return &clone
}

func (env *JoyentEnviron) Config() *config.Config {
	return env.getSnapshot().ecfg.Config
}

func (env *JoyentEnviron) Storage() storage.Storage {
	return env.getSnapshot().storage
}

func (env *JoyentEnviron) PublicStorage() storage.StorageReader {
	return environs.EmptyStorage
}

func (env *JoyentEnviron) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error {
	return common.Bootstrap(ctx, env, cons)
}

func (env *JoyentEnviron) StateInfo() (*state.Info, *api.Info, error) {
	return common.StateInfo(env)
}

func (env *JoyentEnviron) Destroy() error {
	return common.Destroy(env)
}

func (env *JoyentEnviron) Ecfg() *environConfig {
	return env.getSnapshot().ecfg
}

func (env *JoyentEnviron) Credentials() *auth.Credentials {
	return env.getSnapshot().creds
}

func (env *JoyentEnviron) SetCredentials() {
	env.creds = getCredentials(env)
}

func getCredentials(env *JoyentEnviron) *auth.Credentials {
	authentication := auth.Auth{User: env.ecfg.mantaUser(), KeyFile: env.ecfg.keyFile(), Algorithm: env.ecfg.algorithm()}

	return &auth.Credentials{
		UserAuthentication: authentication,
		MantaKeyId:         env.ecfg.mantaKeyId(),
		MantaEndpoint:      auth.Endpoint{URL: env.ecfg.mantaUrl()},
		SdcKeyId:           env.ecfg.sdcKeyId(),
		SdcEndpoint:        auth.Endpoint{URL: env.ecfg.sdcUrl()},
	}
}

// MetadataLookupParams returns parameters which are used to query simplestreams metadata.
func (env *JoyentEnviron) MetadataLookupParams(region string) (*simplestreams.MetadataLookupParams, error) {
	if region == "" {
		region = env.Ecfg().Region()
	}
	return &simplestreams.MetadataLookupParams{
		Series:        env.Ecfg().DefaultSeries(),
		Region:        region,
		Endpoint:      env.Ecfg().sdcUrl(),
		Architectures: []string{"amd64", "arm"},
	}, nil
}

// Region is specified in the HasRegion interface.
func (env *JoyentEnviron) Region() (simplestreams.CloudSpec, error) {
	return simplestreams.CloudSpec{
		Region:   env.Ecfg().Region(),
		Endpoint: env.Ecfg().sdcUrl(),
	}, nil
}

// GetImageSources returns a list of sources which are used to search for simplestreams image metadata.
func (env *JoyentEnviron) GetImageSources() ([]simplestreams.DataSource, error) {
	// Add the simplestreams source off the control bucket.
	sources := []simplestreams.DataSource{
		storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseImagesPath)}
	return sources, nil
}

// GetToolsSources returns a list of sources which are used to search for simplestreams tools metadata.
func (env *JoyentEnviron) GetToolsSources() ([]simplestreams.DataSource, error) {
	// Add the simplestreams source off the control bucket.
	sources := []simplestreams.DataSource{
		storage.NewStorageSimpleStreamsDataSource("cloud storage", env.Storage(), storage.BaseToolsPath)}
	return sources, nil
}
<|endoftext|>


<commit_before>// Package github implements the OAuth2 protocol for authenticating users through Github.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package github

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/markbates/goth"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

const (
	authURL         string = "https://github.com/login/oauth/authorize"
	tokenURL        string = "https://github.com/login/oauth/access_token"
	endpointProfile string = "https://api.github.com/user"
)

// New creates a new Github provider, and sets up important connection details.
// You should always call `github.New` to get a new Provider. Never try to create
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
	p := &Provider{
		ClientKey:   clientKey,
		Secret:      secret,
		CallbackURL: callbackURL,
	}
	p.config = newConfig(p, scopes)
	return p
}

// Provider is the implementation of `goth.Provider` for accessing Github.
type Provider struct {
	ClientKey   string
	Secret      string
	CallbackURL string
	config      *oauth2.Config
}

// Name is the name used to retrieve this provider later.
func (p *Provider) Name() string {
	return "github"
}

// Debug is a no-op for the github package.
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Github for an authentication end-point.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
	url := p.config.AuthCodeURL(state)
	session := &Session{
		AuthURL: url,
	}
	return session, nil
}

// FetchUser will go to Github and access basic information about the user.
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
	sess := session.(*Session)
	user := goth.User{
		AccessToken: sess.AccessToken,
		Provider:    p.Name(),
	}

	response, err := http.Get(endpointProfile + "?access_token=" + url.QueryEscape(sess.AccessToken))
	if err != nil {
		if response != nil {
			response.Body.Close()
		}
		return user, err
	}
	defer response.Body.Close()

	bits, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return user, err
	}

	err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
	if err != nil {
		return user, err
	}

	err = userFromReader(bytes.NewReader(bits), &user)
	return user, err
}

func userFromReader(reader io.Reader, user *goth.User) error {
	u := struct {
		ID       int    `json:"id"`
		Email    string `json:"email"`
		Bio      string `json:"bio"`
		Name     string `json:"name"`
		Login    string `json:"login"`
		Picture  string `json:"avatar_url"`
		Location string `json:"location"`
	}{}

	err := json.NewDecoder(reader).Decode(&u)
	if err != nil {
		return err
	}

	user.Name = u.Name
	user.NickName = u.Login
	user.Email = u.Email
	user.Description = u.Bio
	user.AvatarURL = u.Picture
	user.UserID = strconv.Itoa(u.ID)
	user.Location = u.Location

	return err
}

func newConfig(provider *Provider, scopes []string) *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     provider.ClientKey,
		ClientSecret: provider.Secret,
		RedirectURL:  provider.CallbackURL,
		Endpoint: oauth2.Endpoint{
			AuthURL:  authURL,
			TokenURL: tokenURL,
		},
		Scopes: []string{},
	}

	for _, scope := range scopes {
		c.Scopes = append(c.Scopes, scope)
	}

	return c
}

//RefreshToken refresh token is not provided by github
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
	return nil, errors.New("Refresh token is not provided by github")
}

//RefreshTokenAvailable refresh token is not provided by github
func (p *Provider) RefreshTokenAvailable() bool {
	return false
}
<commit_msg>Support GitHub Enterprise<commit_after>// Package github implements the OAuth2 protocol for authenticating users through Github.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package github

import (
	"bytes"
	"encoding/json"
	"errors"
	"github.com/markbates/goth"
	"golang.org/x/oauth2"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
)

// These vars define the Authentication, Token, and API URLS for GitHub. If
// using GitHub enterprise you should change these values before calling New.
//
// Examples:
//	github.AuthURL = "https://github.acme.com/login/oauth/authorize
//	github.TokenURL = "https://github.acme.com/login/oauth/access_token
//	github.ProfileURL = "https://github.acme.com/api/v3/user
var (
	AuthURL    = "https://github.com/login/oauth/authorize"
	TokenURL   = "https://github.com/login/oauth/access_token"
	ProfileURL = "https://api.github.com/user"
)

// New creates a new Github provider, and sets up important connection details.
// You should always call `github.New` to get a new Provider. Never try to create
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
	p := &Provider{
		ClientKey:   clientKey,
		Secret:      secret,
		CallbackURL: callbackURL,
	}
	p.config = newConfig(p, scopes)
	return p
}

// Provider is the implementation of `goth.Provider` for accessing Github.
type Provider struct {
	ClientKey   string
	Secret      string
	CallbackURL string
	config      *oauth2.Config
}

// Name is the name used to retrieve this provider later.
func (p *Provider) Name() string {
	return "github"
}

// Debug is a no-op for the github package.
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Github for an authentication end-point.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
	url := p.config.AuthCodeURL(state)
	session := &Session{
		AuthURL: url,
	}
	return session, nil
}

// FetchUser will go to Github and access basic information about the user.
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
	sess := session.(*Session)
	user := goth.User{
		AccessToken: sess.AccessToken,
		Provider:    p.Name(),
	}

	response, err := http.Get(ProfileURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
	if err != nil {
		if response != nil {
			response.Body.Close()
		}
		return user, err
	}
	defer response.Body.Close()

	bits, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return user, err
	}

	err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
	if err != nil {
		return user, err
	}

	err = userFromReader(bytes.NewReader(bits), &user)
	return user, err
}

func userFromReader(reader io.Reader, user *goth.User) error {
	u := struct {
		ID       int    `json:"id"`
		Email    string `json:"email"`
		Bio      string `json:"bio"`
		Name     string `json:"name"`
		Login    string `json:"login"`
		Picture  string `json:"avatar_url"`
		Location string `json:"location"`
	}{}

	err := json.NewDecoder(reader).Decode(&u)
	if err != nil {
		return err
	}

	user.Name = u.Name
	user.NickName = u.Login
	user.Email = u.Email
	user.Description = u.Bio
	user.AvatarURL = u.Picture
	user.UserID = strconv.Itoa(u.ID)
	user.Location = u.Location

	return err
}

func newConfig(provider *Provider, scopes []string) *oauth2.Config {
	c := &oauth2.Config{
		ClientID:     provider.ClientKey,
		ClientSecret: provider.Secret,
		RedirectURL:  provider.CallbackURL,
		Endpoint: oauth2.Endpoint{
			AuthURL:  AuthURL,
			TokenURL: TokenURL,
		},
		Scopes: []string{},
	}

	for _, scope := range scopes {
		c.Scopes = append(c.Scopes, scope)
	}

	return c
}

//RefreshToken refresh token is not provided by github
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
	return nil, errors.New("Refresh token is not provided by github")
}

//RefreshTokenAvailable refresh token is not provided by github
func (p *Provider) RefreshTokenAvailable() bool {
	return false
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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.
*/

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"strconv"
	"testing"
	"time"

	"github.com/ghodss/yaml"

	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/kube"
	"k8s.io/test-infra/prow/pluginhelp"
	"k8s.io/test-infra/prow/tide"
)

type flc int

func (f flc) GetJobLog(job, id string) ([]byte, error) {
	if job == "job" && id == "123" {
		return []byte("hello"), nil
	}
	return nil, errors.New("muahaha")
}

func (f flc) GetJobLogStream(job, id string, options map[string]string) (io.ReadCloser, error) {
	if job == "job" && id == "123" {
		return ioutil.NopCloser(bytes.NewBuffer([]byte("hello\r\n"))), nil
	}
	return nil, errors.New("muahaha")
}

func TestHandleLog(t *testing.T) {
	var testcases = []struct {
		name string
		path string
		code int
	}{
		{
			name: "no job name",
			path: "",
			code: http.StatusBadRequest,
		},
		{
			name: "job but no id",
			path: "?job=job",
			code: http.StatusBadRequest,
		},
		{
			name: "id but no job",
			path: "?id=123",
			code: http.StatusBadRequest,
		},
		{
			name: "id and job, found",
			path: "?job=job&id=123",
			code: http.StatusOK,
		},
		{
			name: "id and job, found",
			path: "?job=job&id=123&follow=true",
			code: http.StatusOK,
		},
		{
			name: "id and job, not found",
			path: "?job=ohno&id=123",
			code: http.StatusNotFound,
		},
	}
	handler := handleLog(flc(0))
	for _, tc := range testcases {
		req, err := http.NewRequest(http.MethodGet, "", nil)
		if err != nil {
			t.Fatalf("Error making request: %v", err)
		}
		u, err := url.Parse(tc.path)
		if err != nil {
			t.Fatalf("Error parsing URL: %v", err)
		}
		var follow = false
		if ok, _ := strconv.ParseBool(u.Query().Get("follow")); ok {
			follow = true
		}
		req.URL = u
		rr := httptest.NewRecorder()
		handler.ServeHTTP(rr, req)
		if rr.Code != tc.code {
			t.Errorf("Wrong error code. Got %v, want %v", rr.Code, tc.code)
		} else if rr.Code == http.StatusOK {
			if follow {
				//wait a little to get the chunks
				time.Sleep(2 * time.Millisecond)
				reader := bufio.NewReader(rr.Body)
				var buf bytes.Buffer
				for {
					line, err := reader.ReadBytes('\n')
					if err == io.EOF {
						break
					}
					if err != nil {
						t.Fatalf("Expecting reply with content but got error: %v", err)
					}
					buf.Write(line)
				}
				if !bytes.Contains(buf.Bytes(), []byte("hello")) {
					t.Errorf("Unexpected body: got %s.", buf.String())
				}
			} else {
				resp := rr.Result()
				defer resp.Body.Close()
				if body, err := ioutil.ReadAll(resp.Body); err != nil {
					t.Errorf("Error reading response body: %v", err)
				} else if string(body) != "hello" {
					t.Errorf("Unexpected body: got %s.", string(body))
				}
			}
		}
	}
}

type fpjc kube.ProwJob

func (fc *fpjc) GetProwJob(name string) (kube.ProwJob, error) {
	return kube.ProwJob(*fc), nil
}

// TestRerun just checks that the result can be unmarshaled properly, has an
// updated status, and has equal spec.
func TestRerun(t *testing.T) {
	fc := fpjc(kube.ProwJob{
		Spec: kube.ProwJobSpec{
			Job: "whoa",
		},
		Status: kube.ProwJobStatus{
			State: kube.PendingState,
		},
	})
	handler := handleRerun(&fc)
	req, err := http.NewRequest(http.MethodGet, "/rerun?prowjob=wowsuch", nil)
	if err != nil {
		t.Fatalf("Error making request: %v", err)
	}
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)
	if rr.Code != http.StatusOK {
		t.Fatalf("Bad error code: %d", rr.Code)
	}
	resp := rr.Result()
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Error reading response body: %v", err)
	}
	var res kube.ProwJob
	if err := yaml.Unmarshal(body, &res); err != nil {
		t.Fatalf("Error unmarshaling: %v", err)
	}
	if res.Spec.Job != "whoa" {
		t.Errorf("Wrong job, expected \"whoa\", got \"%s\"", res.Spec.Job)
	}
	if res.Status.State != kube.TriggeredState {
		t.Errorf("Wrong state, expected \"%v\", got \"%v\"", kube.TriggeredState, res.Status.State)
	}
}

func TestTide(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		pools := []tide.Pool{
			{
				Org: "o",
			},
		}
		b, err := json.Marshal(pools)
		if err != nil {
			t.Fatalf("Marshaling: %v", err)
		}
		fmt.Fprintf(w, string(b))
	}))
	ca := &config.Agent{}
	ca.Set(&config.Config{
		Tide: config.Tide{
			Queries: []config.TideQuery{
				{Repos: []string{"kubernetes/test-infra"}},
			},
		},
	})
	ta := tideAgent{
		path:         s.URL,
		updatePeriod: func() time.Duration { return time.Minute },
	}
	if err := ta.update(); err != nil {
		t.Fatalf("Updating: %v", err)
	}
	if len(ta.pools) != 1 {
		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(ta.pools), ta.pools)
	}
	if ta.pools[0].Org != "o" {
		t.Errorf("Wrong org in pool. Got %s, expected o in %v", ta.pools[0].Org, ta.pools)
	}
	handler := handleTide(ca, &ta)
	req, err := http.NewRequest(http.MethodGet, "/tide.js", nil)
	if err != nil {
		t.Fatalf("Error making request: %v", err)
	}
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)
	if rr.Code != http.StatusOK {
		t.Fatalf("Bad error code: %d", rr.Code)
	}
	resp := rr.Result()
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Error reading response body: %v", err)
	}
	res := tideData{}
	if err := yaml.Unmarshal(body, &res); err != nil {
		t.Fatalf("Error unmarshaling: %v", err)
	}
	if len(res.Pools) != 1 {
		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(res.Pools), res.Pools)
	}
	if res.Pools[0].Org != "o" {
		t.Errorf("Wrong org in pool. Got %s, expected o in %v", res.Pools[0].Org, res.Pools)
	}
	if len(res.Queries) != 1 {
		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(res.Queries), res.Queries)
	}
	if expected := "is:pr state:open repo:\"kubernetes/test-infra\""; res.Queries[0] != expected {
		t.Errorf("Wrong query. Got %s, expected %s", res.Queries[0], expected)
	}
}

func TestHelp(t *testing.T) {
	hitCount := 0
	help := pluginhelp.Help{
		AllRepos:            []string{"org/repo"},
		RepoPlugins:         map[string][]string{"org": {"plugin"}},
		RepoExternalPlugins: map[string][]string{"org/repo": {"external-plugin"}},
		PluginHelp:          map[string]pluginhelp.PluginHelp{"plugin": {Description: "plugin"}},
		ExternalPluginHelp:  map[string]pluginhelp.PluginHelp{"external-plugin": {Description: "external-plugin"}},
	}
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		hitCount++
		b, err := json.Marshal(help)
		if err != nil {
			t.Fatalf("Marshaling: %v", err)
		}
		fmt.Fprintf(w, string(b))
	}))
	ha := &helpAgent{
		path: s.URL,
	}
	handler := handlePluginHelp(ha)
	handleAndCheck := func() {
		req, err := http.NewRequest(http.MethodGet, "/plugin-help.js", nil)
		if err != nil {
			t.Fatalf("Error making request: %v", err)
		}
		rr := httptest.NewRecorder()
		handler.ServeHTTP(rr, req)
		if rr.Code != http.StatusOK {
			t.Fatalf("Bad error code: %d", rr.Code)
		}
		resp := rr.Result()
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("Error reading response body: %v", err)
		}
		var res pluginhelp.Help
		if err := yaml.Unmarshal(body, &res); err != nil {
			t.Fatalf("Error unmarshaling: %v", err)
		}
		if !reflect.DeepEqual(help, res) {
			t.Errorf("Invalid plugin help. Got %q, expected %q", res, help)
		}
		if hitCount != 1 {
			t.Errorf("Expected fake hook endpoint to be hit once, but endpoint was hit %d times.", hitCount)
		}
	}
	handleAndCheck()
	handleAndCheck()
}
<commit_msg>Changes printf %q verb to %v due to boolean field in the Command struct<commit_after>/*
Copyright 2016 The Kubernetes 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.
*/

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"strconv"
	"testing"
	"time"

	"github.com/ghodss/yaml"

	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/kube"
	"k8s.io/test-infra/prow/pluginhelp"
	"k8s.io/test-infra/prow/tide"
)

type flc int

func (f flc) GetJobLog(job, id string) ([]byte, error) {
	if job == "job" && id == "123" {
		return []byte("hello"), nil
	}
	return nil, errors.New("muahaha")
}

func (f flc) GetJobLogStream(job, id string, options map[string]string) (io.ReadCloser, error) {
	if job == "job" && id == "123" {
		return ioutil.NopCloser(bytes.NewBuffer([]byte("hello\r\n"))), nil
	}
	return nil, errors.New("muahaha")
}

func TestHandleLog(t *testing.T) {
	var testcases = []struct {
		name string
		path string
		code int
	}{
		{
			name: "no job name",
			path: "",
			code: http.StatusBadRequest,
		},
		{
			name: "job but no id",
			path: "?job=job",
			code: http.StatusBadRequest,
		},
		{
			name: "id but no job",
			path: "?id=123",
			code: http.StatusBadRequest,
		},
		{
			name: "id and job, found",
			path: "?job=job&id=123",
			code: http.StatusOK,
		},
		{
			name: "id and job, found",
			path: "?job=job&id=123&follow=true",
			code: http.StatusOK,
		},
		{
			name: "id and job, not found",
			path: "?job=ohno&id=123",
			code: http.StatusNotFound,
		},
	}
	handler := handleLog(flc(0))
	for _, tc := range testcases {
		req, err := http.NewRequest(http.MethodGet, "", nil)
		if err != nil {
			t.Fatalf("Error making request: %v", err)
		}
		u, err := url.Parse(tc.path)
		if err != nil {
			t.Fatalf("Error parsing URL: %v", err)
		}
		var follow = false
		if ok, _ := strconv.ParseBool(u.Query().Get("follow")); ok {
			follow = true
		}
		req.URL = u
		rr := httptest.NewRecorder()
		handler.ServeHTTP(rr, req)
		if rr.Code != tc.code {
			t.Errorf("Wrong error code. Got %v, want %v", rr.Code, tc.code)
		} else if rr.Code == http.StatusOK {
			if follow {
				//wait a little to get the chunks
				time.Sleep(2 * time.Millisecond)
				reader := bufio.NewReader(rr.Body)
				var buf bytes.Buffer
				for {
					line, err := reader.ReadBytes('\n')
					if err == io.EOF {
						break
					}
					if err != nil {
						t.Fatalf("Expecting reply with content but got error: %v", err)
					}
					buf.Write(line)
				}
				if !bytes.Contains(buf.Bytes(), []byte("hello")) {
					t.Errorf("Unexpected body: got %s.", buf.String())
				}
			} else {
				resp := rr.Result()
				defer resp.Body.Close()
				if body, err := ioutil.ReadAll(resp.Body); err != nil {
					t.Errorf("Error reading response body: %v", err)
				} else if string(body) != "hello" {
					t.Errorf("Unexpected body: got %s.", string(body))
				}
			}
		}
	}
}

type fpjc kube.ProwJob

func (fc *fpjc) GetProwJob(name string) (kube.ProwJob, error) {
	return kube.ProwJob(*fc), nil
}

// TestRerun just checks that the result can be unmarshaled properly, has an
// updated status, and has equal spec.
func TestRerun(t *testing.T) {
	fc := fpjc(kube.ProwJob{
		Spec: kube.ProwJobSpec{
			Job: "whoa",
		},
		Status: kube.ProwJobStatus{
			State: kube.PendingState,
		},
	})
	handler := handleRerun(&fc)
	req, err := http.NewRequest(http.MethodGet, "/rerun?prowjob=wowsuch", nil)
	if err != nil {
		t.Fatalf("Error making request: %v", err)
	}
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)
	if rr.Code != http.StatusOK {
		t.Fatalf("Bad error code: %d", rr.Code)
	}
	resp := rr.Result()
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Error reading response body: %v", err)
	}
	var res kube.ProwJob
	if err := yaml.Unmarshal(body, &res); err != nil {
		t.Fatalf("Error unmarshaling: %v", err)
	}
	if res.Spec.Job != "whoa" {
		t.Errorf("Wrong job, expected \"whoa\", got \"%s\"", res.Spec.Job)
	}
	if res.Status.State != kube.TriggeredState {
		t.Errorf("Wrong state, expected \"%v\", got \"%v\"", kube.TriggeredState, res.Status.State)
	}
}

func TestTide(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		pools := []tide.Pool{
			{
				Org: "o",
			},
		}
		b, err := json.Marshal(pools)
		if err != nil {
			t.Fatalf("Marshaling: %v", err)
		}
		fmt.Fprintf(w, string(b))
	}))
	ca := &config.Agent{}
	ca.Set(&config.Config{
		Tide: config.Tide{
			Queries: []config.TideQuery{
				{Repos: []string{"kubernetes/test-infra"}},
			},
		},
	})
	ta := tideAgent{
		path:         s.URL,
		updatePeriod: func() time.Duration { return time.Minute },
	}
	if err := ta.update(); err != nil {
		t.Fatalf("Updating: %v", err)
	}
	if len(ta.pools) != 1 {
		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(ta.pools), ta.pools)
	}
	if ta.pools[0].Org != "o" {
		t.Errorf("Wrong org in pool. Got %s, expected o in %v", ta.pools[0].Org, ta.pools)
	}
	handler := handleTide(ca, &ta)
	req, err := http.NewRequest(http.MethodGet, "/tide.js", nil)
	if err != nil {
		t.Fatalf("Error making request: %v", err)
	}
	rr := httptest.NewRecorder()
	handler.ServeHTTP(rr, req)
	if rr.Code != http.StatusOK {
		t.Fatalf("Bad error code: %d", rr.Code)
	}
	resp := rr.Result()
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Fatalf("Error reading response body: %v", err)
	}
	res := tideData{}
	if err := yaml.Unmarshal(body, &res); err != nil {
		t.Fatalf("Error unmarshaling: %v", err)
	}
	if len(res.Pools) != 1 {
		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(res.Pools), res.Pools)
	}
	if res.Pools[0].Org != "o" {
		t.Errorf("Wrong org in pool. Got %s, expected o in %v", res.Pools[0].Org, res.Pools)
	}
	if len(res.Queries) != 1 {
		t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(res.Queries), res.Queries)
	}
	if expected := "is:pr state:open repo:\"kubernetes/test-infra\""; res.Queries[0] != expected {
		t.Errorf("Wrong query. Got %s, expected %s", res.Queries[0], expected)
	}
}

func TestHelp(t *testing.T) {
	hitCount := 0
	help := pluginhelp.Help{
		AllRepos:            []string{"org/repo"},
		RepoPlugins:         map[string][]string{"org": {"plugin"}},
		RepoExternalPlugins: map[string][]string{"org/repo": {"external-plugin"}},
		PluginHelp:          map[string]pluginhelp.PluginHelp{"plugin": {Description: "plugin"}},
		ExternalPluginHelp:  map[string]pluginhelp.PluginHelp{"external-plugin": {Description: "external-plugin"}},
	}
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		hitCount++
		b, err := json.Marshal(help)
		if err != nil {
			t.Fatalf("Marshaling: %v", err)
		}
		fmt.Fprintf(w, string(b))
	}))
	ha := &helpAgent{
		path: s.URL,
	}
	handler := handlePluginHelp(ha)
	handleAndCheck := func() {
		req, err := http.NewRequest(http.MethodGet, "/plugin-help.js", nil)
		if err != nil {
			t.Fatalf("Error making request: %v", err)
		}
		rr := httptest.NewRecorder()
		handler.ServeHTTP(rr, req)
		if rr.Code != http.StatusOK {
			t.Fatalf("Bad error code: %d", rr.Code)
		}
		resp := rr.Result()
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("Error reading response body: %v", err)
		}
		var res pluginhelp.Help
		if err := yaml.Unmarshal(body, &res); err != nil {
			t.Fatalf("Error unmarshaling: %v", err)
		}
		if !reflect.DeepEqual(help, res) {
			t.Errorf("Invalid plugin help. Got %v, expected %v", res, help)
		}
		if hitCount != 1 {
			t.Errorf("Expected fake hook endpoint to be hit once, but endpoint was hit %d times.", hitCount)
		}
	}
	handleAndCheck()
	handleAndCheck()
}
<|endoftext|>


<commit_before>package dokodemo

import (
	"sync"

	"github.com/v2ray/v2ray-core/app"
	"github.com/v2ray/v2ray-core/app/dispatcher"
	"github.com/v2ray/v2ray-core/common/alloc"
	v2io "github.com/v2ray/v2ray-core/common/io"
	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	"github.com/v2ray/v2ray-core/proxy"
	"github.com/v2ray/v2ray-core/proxy/internal"
	"github.com/v2ray/v2ray-core/transport/internet"
	"github.com/v2ray/v2ray-core/transport/internet/udp"
)

type DokodemoDoor struct {
	tcpMutex         sync.RWMutex
	udpMutex         sync.RWMutex
	config           *Config
	accepting        bool
	address          v2net.Address
	port             v2net.Port
	packetDispatcher dispatcher.PacketDispatcher
	tcpListener      *internet.TCPHub
	udpHub           *udp.UDPHub
	udpServer        *udp.UDPServer
	meta             *proxy.InboundHandlerMeta
}

func NewDokodemoDoor(config *Config, space app.Space, meta *proxy.InboundHandlerMeta) *DokodemoDoor {
	d := &DokodemoDoor{
		config:  config,
		address: config.Address,
		port:    config.Port,
		meta:    meta,
	}
	space.InitializeApplication(func() error {
		if !space.HasApp(dispatcher.APP_ID) {
			log.Error("Dokodemo: Dispatcher is not found in the space.")
			return app.ErrMissingApplication
		}
		d.packetDispatcher = space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)
		return nil
	})
	return d
}

func (this *DokodemoDoor) Port() v2net.Port {
	return this.meta.Port
}

func (this *DokodemoDoor) Close() {
	this.accepting = false
	if this.tcpListener != nil {
		this.tcpMutex.Lock()
		this.tcpListener.Close()
		this.tcpListener = nil
		this.tcpMutex.Unlock()
	}
	if this.udpHub != nil {
		this.udpMutex.Lock()
		this.udpHub.Close()
		this.udpHub = nil
		this.udpMutex.Unlock()
	}
}

func (this *DokodemoDoor) Start() error {
	if this.accepting {
		return nil
	}
	this.accepting = true

	if this.config.Network.HasNetwork(v2net.TCPNetwork) {
		err := this.ListenTCP()
		if err != nil {
			return err
		}
	}
	if this.config.Network.HasNetwork(v2net.UDPNetwork) {
		err := this.ListenUDP()
		if err != nil {
			return err
		}
	}
	return nil
}

func (this *DokodemoDoor) ListenUDP() error {
	this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher)
	udpHub, err := udp.ListenUDP(
		this.meta.Address, this.meta.Port, udp.ListenOption{
			Callback:            this.handleUDPPackets,
			ReceiveOriginalDest: this.config.FollowRedirect,
		})
	if err != nil {
		log.Error("Dokodemo failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
		return err
	}
	this.udpMutex.Lock()
	this.udpHub = udpHub
	this.udpMutex.Unlock()
	return nil
}

func (this *DokodemoDoor) handleUDPPackets(payload *alloc.Buffer, session *proxy.SessionInfo) {
	if session.Destination == nil && this.address != nil && this.port > 0 {
		session.Destination = v2net.UDPDestination(this.address, this.port)
	}
	if session.Destination == nil {
		log.Info("Dokodemo: Unknown destination, stop forwarding...")
		return
	}
	this.udpServer.Dispatch(session, payload, this.handleUDPResponse)
}

func (this *DokodemoDoor) handleUDPResponse(dest v2net.Destination, payload *alloc.Buffer) {
	defer payload.Release()
	this.udpMutex.RLock()
	defer this.udpMutex.RUnlock()
	if !this.accepting {
		return
	}
	this.udpHub.WriteTo(payload.Value, dest)
}

func (this *DokodemoDoor) ListenTCP() error {
	tcpListener, err := internet.ListenTCP(this.meta.Address, this.meta.Port, this.HandleTCPConnection, this.meta.StreamSettings)
	if err != nil {
		log.Error("Dokodemo: Failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
		return err
	}
	this.tcpMutex.Lock()
	this.tcpListener = tcpListener
	this.tcpMutex.Unlock()
	return nil
}

func (this *DokodemoDoor) HandleTCPConnection(conn internet.Connection) {
	defer conn.Close()

	var dest v2net.Destination
	if this.config.FollowRedirect {
		originalDest := GetOriginalDestination(conn)
		if originalDest != nil {
			log.Info("Dokodemo: Following redirect to: ", originalDest)
			dest = originalDest
		}
	}
	if dest == nil && this.address != nil && this.port > v2net.Port(0) {
		dest = v2net.TCPDestination(this.address, this.port)
	}

	if dest == nil {
		log.Info("Dokodemo: Unknown destination, stop forwarding...")
		return
	}
	log.Info("Dokodemo: Handling request to ", dest)

	ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{
		Source:      v2net.DestinationFromAddr(conn.RemoteAddr()),
		Destination: dest,
	})
	defer ray.InboundOutput().Release()

	var inputFinish, outputFinish sync.Mutex
	inputFinish.Lock()
	outputFinish.Lock()

	reader := v2net.NewTimeOutReader(this.config.Timeout, conn)
	defer reader.Release()

	go func() {
		v2reader := v2io.NewAdaptiveReader(reader)
		defer v2reader.Release()

		v2io.Pipe(v2reader, ray.InboundInput())
		inputFinish.Unlock()
		ray.InboundInput().Close()
	}()

	go func() {
		v2writer := v2io.NewAdaptiveWriter(conn)
		defer v2writer.Release()

		v2io.Pipe(ray.InboundOutput(), v2writer)
		outputFinish.Unlock()
	}()

	outputFinish.Lock()
	inputFinish.Lock()
}

type Factory struct{}

func (this *Factory) StreamCapability() internet.StreamConnectionType {
	return internet.StreamConnectionTypeRawTCP
}

func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {
	return NewDokodemoDoor(rawConfig.(*Config), space, meta), nil
}

func init() {
	internal.MustRegisterInboundHandlerCreator("dokodemo-door", new(Factory))
}
<commit_msg>change to wait group<commit_after>package dokodemo

import (
	"sync"

	"github.com/v2ray/v2ray-core/app"
	"github.com/v2ray/v2ray-core/app/dispatcher"
	"github.com/v2ray/v2ray-core/common/alloc"
	v2io "github.com/v2ray/v2ray-core/common/io"
	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	"github.com/v2ray/v2ray-core/proxy"
	"github.com/v2ray/v2ray-core/proxy/internal"
	"github.com/v2ray/v2ray-core/transport/internet"
	"github.com/v2ray/v2ray-core/transport/internet/udp"
)

type DokodemoDoor struct {
	tcpMutex         sync.RWMutex
	udpMutex         sync.RWMutex
	config           *Config
	accepting        bool
	address          v2net.Address
	port             v2net.Port
	packetDispatcher dispatcher.PacketDispatcher
	tcpListener      *internet.TCPHub
	udpHub           *udp.UDPHub
	udpServer        *udp.UDPServer
	meta             *proxy.InboundHandlerMeta
}

func NewDokodemoDoor(config *Config, space app.Space, meta *proxy.InboundHandlerMeta) *DokodemoDoor {
	d := &DokodemoDoor{
		config:  config,
		address: config.Address,
		port:    config.Port,
		meta:    meta,
	}
	space.InitializeApplication(func() error {
		if !space.HasApp(dispatcher.APP_ID) {
			log.Error("Dokodemo: Dispatcher is not found in the space.")
			return app.ErrMissingApplication
		}
		d.packetDispatcher = space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher)
		return nil
	})
	return d
}

func (this *DokodemoDoor) Port() v2net.Port {
	return this.meta.Port
}

func (this *DokodemoDoor) Close() {
	this.accepting = false
	if this.tcpListener != nil {
		this.tcpMutex.Lock()
		this.tcpListener.Close()
		this.tcpListener = nil
		this.tcpMutex.Unlock()
	}
	if this.udpHub != nil {
		this.udpMutex.Lock()
		this.udpHub.Close()
		this.udpHub = nil
		this.udpMutex.Unlock()
	}
}

func (this *DokodemoDoor) Start() error {
	if this.accepting {
		return nil
	}
	this.accepting = true

	if this.config.Network.HasNetwork(v2net.TCPNetwork) {
		err := this.ListenTCP()
		if err != nil {
			return err
		}
	}
	if this.config.Network.HasNetwork(v2net.UDPNetwork) {
		err := this.ListenUDP()
		if err != nil {
			return err
		}
	}
	return nil
}

func (this *DokodemoDoor) ListenUDP() error {
	this.udpServer = udp.NewUDPServer(this.meta, this.packetDispatcher)
	udpHub, err := udp.ListenUDP(
		this.meta.Address, this.meta.Port, udp.ListenOption{
			Callback:            this.handleUDPPackets,
			ReceiveOriginalDest: this.config.FollowRedirect,
		})
	if err != nil {
		log.Error("Dokodemo failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
		return err
	}
	this.udpMutex.Lock()
	this.udpHub = udpHub
	this.udpMutex.Unlock()
	return nil
}

func (this *DokodemoDoor) handleUDPPackets(payload *alloc.Buffer, session *proxy.SessionInfo) {
	if session.Destination == nil && this.address != nil && this.port > 0 {
		session.Destination = v2net.UDPDestination(this.address, this.port)
	}
	if session.Destination == nil {
		log.Info("Dokodemo: Unknown destination, stop forwarding...")
		return
	}
	this.udpServer.Dispatch(session, payload, this.handleUDPResponse)
}

func (this *DokodemoDoor) handleUDPResponse(dest v2net.Destination, payload *alloc.Buffer) {
	defer payload.Release()
	this.udpMutex.RLock()
	defer this.udpMutex.RUnlock()
	if !this.accepting {
		return
	}
	this.udpHub.WriteTo(payload.Value, dest)
}

func (this *DokodemoDoor) ListenTCP() error {
	tcpListener, err := internet.ListenTCP(this.meta.Address, this.meta.Port, this.HandleTCPConnection, this.meta.StreamSettings)
	if err != nil {
		log.Error("Dokodemo: Failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)
		return err
	}
	this.tcpMutex.Lock()
	this.tcpListener = tcpListener
	this.tcpMutex.Unlock()
	return nil
}

func (this *DokodemoDoor) HandleTCPConnection(conn internet.Connection) {
	defer conn.Close()

	var dest v2net.Destination
	if this.config.FollowRedirect {
		originalDest := GetOriginalDestination(conn)
		if originalDest != nil {
			log.Info("Dokodemo: Following redirect to: ", originalDest)
			dest = originalDest
		}
	}
	if dest == nil && this.address != nil && this.port > v2net.Port(0) {
		dest = v2net.TCPDestination(this.address, this.port)
	}

	if dest == nil {
		log.Info("Dokodemo: Unknown destination, stop forwarding...")
		return
	}
	log.Info("Dokodemo: Handling request to ", dest)

	ray := this.packetDispatcher.DispatchToOutbound(this.meta, &proxy.SessionInfo{
		Source:      v2net.DestinationFromAddr(conn.RemoteAddr()),
		Destination: dest,
	})
	defer ray.InboundOutput().Release()

	var wg sync.WaitGroup

	reader := v2net.NewTimeOutReader(this.config.Timeout, conn)
	defer reader.Release()

	wg.Add(1)
	go func() {
		v2reader := v2io.NewAdaptiveReader(reader)
		defer v2reader.Release()

		v2io.Pipe(v2reader, ray.InboundInput())
		wg.Done()
		ray.InboundInput().Close()
	}()

	wg.Add(1)
	go func() {
		v2writer := v2io.NewAdaptiveWriter(conn)
		defer v2writer.Release()

		v2io.Pipe(ray.InboundOutput(), v2writer)
		wg.Done()
	}()

	wg.Wait()
}

type Factory struct{}

func (this *Factory) StreamCapability() internet.StreamConnectionType {
	return internet.StreamConnectionTypeRawTCP
}

func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.InboundHandlerMeta) (proxy.InboundHandler, error) {
	return NewDokodemoDoor(rawConfig.(*Config), space, meta), nil
}

func init() {
	internal.MustRegisterInboundHandlerCreator("dokodemo-door", new(Factory))
}
<|endoftext|>


<commit_before>package web

import (
	"bytes"
	"config"
	"core"
	"db"
	"encoding/json"
	"errrs"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"html/template"
	log "logger"
	"mime"
	"net/http"
	_ "net/http/pprof"
	"os"
	"strings"
	"util"
)

var allowedTemplates = []string{".html"}

type NetCmdLine struct {
	core       core.Core
	db         *db.DB
	martini    *martini.ClassicMartini
	renderInfo WebTemplate
	templates  map[string]*template.Template
}

// context information for web templates
type WebTemplate struct {
	ApiPath string
}

func (n *NetCmdLine) getCmd(r *http.Request) (err error, cmd string, args core.ArgMap) {
	path := r.URL.Path[1:]
	parts := strings.Split(path, "/")
	// ignore parts[2:]
	if err := r.ParseForm(); err != nil {
		return errrs.New("bad request: " + err.Error()), "", nil
	}

	return nil, parts[1], core.ArgMap(r.Form)
}

func (n *NetCmdLine) api(params martini.Params, r render.Render, req *http.Request) (
	int, string) {
	if err := req.ParseForm(); err != nil {
		return http.StatusBadRequest, "bad request: " + err.Error()
	}

	var ctx core.CommandContext
	ctx.Cmd = params["cmd"]
	ctx.Args = core.ArgMap(req.Form)

	// check auth token
	token, err := util.GetArg(ctx.Args, "auth_token", false, nil)
	if err != nil {
		return http.StatusBadRequest, err.Error()
	}
	ctx.AuthLevel = core.AuthGuest
	if token != nil {
		ctx.AuthLevel, ctx.UserId, err = n.db.Authenticate(*token)
		if err != nil {
			return http.StatusUnauthorized, err.Error()
		}
	}

	// execute command
	result := n.core.Cmd(ctx)
	if result.Error == core.ErrorCmdNotFound {
		return http.StatusNotFound, result.Error.Error()
	}
	if result.Error != nil {
		return http.StatusInternalServerError, result.Error.Error()
	}
	if result.IsRaw {
		str := &bytes.Buffer{}
		if result.Result != nil {
			if array, ok := result.Result.([]interface{}); ok {
				for _, v := range array {
					fmt.Fprintln(str, v)
				}
			} else {
				fmt.Fprintln(str, result.Result)
			}
		}
		return 200, str.String()
	} else {
		r.JSON(200, result.Result)
		return 200, ""
	}
}

func (n *NetCmdLine) printResult(w http.ResponseWriter, result core.Result) {
	if result.Error != nil {
		log.Log.Println("api error:", result.Status, result.Error)
	}
	if result.IsRaw {
		fmt.Fprint(w, result.Status)
		if result.Error != nil {
			fmt.Fprintln(w, "", result.Error)
		} else {
			fmt.Fprintln(w)
		}
		if result.Result != nil {
			if array, ok := result.Result.([]interface{}); ok {
				for _, v := range array {
					fmt.Fprintln(w, v)
				}
			} else {
				fmt.Fprintln(w, result.Result)
			}
		}
	} else {
		if bytes, err := json.MarshalIndent(result, "", "  "); err != nil {
			http.Error(w, err.Error(), 500)
		} else {
			w.Write(bytes)
			fmt.Fprintln(w)
		}
	}
}

func (n *NetCmdLine) serveAsset(w http.ResponseWriter, r *http.Request) {
	r.URL.Path = strings.TrimLeft(r.URL.Path, "/")
	if r.URL.Path == "" {
		r.URL.Path = "index.html"
	}

	cache := false
	templateAble := false
	for _, suffix := range allowedTemplates {
		if strings.HasSuffix(r.URL.Path, suffix) {
			cache = config.Current.CacheWebTemplates
			templateAble = true
			break
		}
	}
	if templateAble {
		var tmpl *template.Template
		if cache {
			tmpl = n.templates[r.URL.Path]
		}
		if tmpl == nil {
			data, err := Asset(r.URL.Path)
			if len(data) == 0 || err != nil {
				//TODO handle 404's more nicely
				w.WriteHeader(http.StatusNotFound)
				fmt.Fprintln(w, err)
				return
			}
			tmpl, err = template.New(r.URL.Path).Parse(string(data))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				fmt.Fprintln(w, err)
				log.Log.Println(err)
				return
			}
			n.templates[r.URL.Path] = tmpl
		}
		n.setMime(w, r)
		err := tmpl.Execute(w, n.renderInfo)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
			log.Log.Println(err)
		}
	} else {
		data, err := Asset(r.URL.Path)
		if len(data) == 0 || err != nil {
			//TODO handle 404's more nicely
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintln(w, err)
			return
		}
		n.setMime(w, r)
		_, err = w.Write(data)
		if err != nil {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintln(w, err)
		}
	}
}

func (n *NetCmdLine) setMime(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", mime.TypeByExtension(r.URL.Path[strings.LastIndex(r.URL.Path, "."):]))
}

func (n *NetCmdLine) Start(core core.Core, db *db.DB) {
	n.core = core
	n.db = db
	n.templates = make(map[string]*template.Template)
	//TODO insert hostname here
	n.renderInfo = WebTemplate{
		ApiPath: fmt.Sprintf("http://localhost:%d/api/", config.Current.WebPort),
	}

	m := martini.Classic()
	n.martini = m
	m.Use(render.Renderer())
	m.Group("/api", func(r martini.Router) {
		r.Get("/:cmd", n.api)
		r.Post("/:cmd", n.api)
	})
	m.Get("/", index_html)
	m.Get("/js/**", n.serveAsset)
	m.Get("/css/**", n.serveAsset)
	m.Get("/fonts/**", n.serveAsset)

	os.Setenv("PORT", fmt.Sprint(config.Current.WebPort))
	m.Run()
}
<commit_msg>a lot of martini, deleting some code<commit_after>package web

import (
	"bytes"
	"config"
	"core"
	"db"
	"errrs"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"html/template"
	"mime"
	"net/http"
	_ "net/http/pprof"
	"os"
	"strings"
	"util"
)

var allowedTemplates = []string{".html"}

type NetCmdLine struct {
	core       core.Core
	db         *db.DB
	martini    *martini.ClassicMartini
	renderInfo WebTemplate
	templates  map[string]*template.Template
}

// context information for web templates
type WebTemplate struct {
	ApiPath string
}

func (n *NetCmdLine) api(params martini.Params, r render.Render, req *http.Request) (
	int, string) {
	if err := req.ParseForm(); err != nil {
		return http.StatusBadRequest, "bad request: " + err.Error()
	}

	var ctx core.CommandContext
	ctx.Cmd = params["cmd"]
	ctx.Args = core.ArgMap(req.Form)

	// check auth token
	token, err := util.GetArg(ctx.Args, "auth_token", false, nil)
	if err != nil {
		return http.StatusBadRequest, err.Error()
	}
	ctx.AuthLevel = core.AuthGuest
	if token != nil {
		ctx.AuthLevel, ctx.UserId, err = n.db.Authenticate(*token)
		if err != nil {
			return http.StatusUnauthorized, err.Error()
		}
	}

	// execute command
	result := n.core.Cmd(ctx)
	if result.Error == core.ErrorCmdNotFound {
		return http.StatusNotFound, result.Error.Error()
	}
	if result.Error != nil {
		return http.StatusInternalServerError, result.Error.Error()
	}
	if result.IsRaw {
		str := &bytes.Buffer{}
		if result.Result != nil {
			if array, ok := result.Result.([]interface{}); ok {
				for _, v := range array {
					fmt.Fprintln(str, v)
				}
			} else {
				fmt.Fprintln(str, result.Result)
			}
		}
		return 200, str.String()
	} else {
		r.JSON(200, result.Result)
		return 200, ""
	}
}

func (n *NetCmdLine) Start(core core.Core, db *db.DB) {
	n.core = core
	n.db = db
	n.templates = make(map[string]*template.Template)
	//TODO insert hostname here
	n.renderInfo = WebTemplate{
		ApiPath: fmt.Sprintf("http://localhost:%d/api/", config.Current.WebPort),
	}

	m := martini.Classic()
	n.martini = m
	m.Use(render.Renderer())

	m.Group("/api", func(r martini.Router) {
		r.Get("/:cmd", n.api)
		r.Post("/:cmd", n.api)
	})

	m.Use(func(c martini.Context, r *http.Request) {
		c.Map(r.URL.Path[1:])
	})
	m.Get("/", index_html)
	m.Get("/**", setMime, Asset)

	os.Setenv("PORT", fmt.Sprint(config.Current.WebPort))
	m.Run()
}

func setMime(w http.ResponseWriter, path string) {
	w.Header().Set("Content-Type", mime.TypeByExtension(path[strings.LastIndex(path, "."):]))
}
<|endoftext|>


<commit_before>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// Package ast is the abstract syntax tree parsed from a SQL statement by parser.
// It can be analysed and transformed by optimizer.
package ast

import (
	"github.com/pingcap/tidb/model"
	"github.com/pingcap/tidb/util/types"
)

// Node is the basic element of the AST.
// Interfaces embed Node should have 'Node' name suffix.
type Node interface {
	// Accept accepts Visitor to visit itself.
	// The returned node should replace original node.
	// ok returns false to stop visiting.
	Accept(v Visitor) (node Node, ok bool)
	// Text returns the original text of the element.
	Text() string
	// SetText sets original text to the Node.
	SetText(text string)
}

// ExprNode is a node that can be evaluated.
// Name of implementations should have 'Expr' suffix.
type ExprNode interface {
	// Node is embeded in ExprNode.
	Node
	// IsStatic means it can be evaluated independently.
	IsStatic() bool
	// SetType sets evaluation type to the expression.
	SetType(tp *types.FieldType)
	// GetType gets the evaluation type of the expression.
	GetType() *types.FieldType
	// SetValue sets value to the expression.
	SetValue(val interface{})
	// GetValue gets value of the expression.
	GetValue() interface{}
}

// FuncNode represents function call expression node.
type FuncNode interface {
	ExprNode
	functionExpression()
}

// StmtNode represents statement node.
// Name of implementations should have 'Stmt' suffix.
type StmtNode interface {
	Node
	statement()
}

// DDLNode represents DDL statement node.
type DDLNode interface {
	StmtNode
	ddlStatement()
}

// DMLNode represents DML statement node.
type DMLNode interface {
	StmtNode
	dmlStatement()
}

// ResultField is computed from a parsed select statement.
type ResultField struct {
	Column       *model.ColumnInfo
	ColumnAsName model.CIStr
	Table        *model.TableInfo
	TableAsName  model.CIStr
	DBName       model.CIStr
}

// ResultSetNode interface has ResultFields property which is computed and set by visitor.
// Implementations include SelectStmt, SubqueryExpr, TableSource, TableName and Join.
type ResultSetNode interface {
	Node
	// GetResultFields gets result fields of the result set node.
	GetResultFields() []*ResultField
	// SetResultFields sets result fields of the result set node.
	SetResultFields(fields []*ResultField)
}

// Visitor visits a Node.
type Visitor interface {
	// VisitEnter is called before children nodes is visited.
	// The returned node should be the same type as the input node n.
	// skipChildren returns true means children nodes should be skipped,
	// this is useful when work is done in Enter and there is no need to visit children.
	Enter(n Node) (node Node, skipChildren bool)
	// VisitLeave is called after children nodes has been visited.
	// The returned node should be the same type as the input node n.
	// ok returns false to stop visiting.
	Leave(n Node) (node Node, ok bool)
}
<commit_msg>ast: add comments about implementation rule on Accept method.<commit_after>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

// Package ast is the abstract syntax tree parsed from a SQL statement by parser.
// It can be analysed and transformed by optimizer.
package ast

import (
	"github.com/pingcap/tidb/model"
	"github.com/pingcap/tidb/util/types"
)

// Node is the basic element of the AST.
// Interfaces embed Node should have 'Node' name suffix.
type Node interface {
	// Accept accepts Visitor to visit itself.
	// The returned node should replace original node.
	// ok returns false to stop visiting.
	//
	// Implementation of this method should first call visitor.Enter,
	// assign the returned node to its method receiver, if skipChildren returns true,
	// children should be skipped. Otherwise, call its children in particular order that
	// later elements depends on on former elements. Finally, return visitor.Leave.
	Accept(v Visitor) (node Node, ok bool)
	// Text returns the original text of the element.
	Text() string
	// SetText sets original text to the Node.
	SetText(text string)
}

// ExprNode is a node that can be evaluated.
// Name of implementations should have 'Expr' suffix.
type ExprNode interface {
	// Node is embeded in ExprNode.
	Node
	// IsStatic means it can be evaluated independently.
	IsStatic() bool
	// SetType sets evaluation type to the expression.
	SetType(tp *types.FieldType)
	// GetType gets the evaluation type of the expression.
	GetType() *types.FieldType
	// SetValue sets value to the expression.
	SetValue(val interface{})
	// GetValue gets value of the expression.
	GetValue() interface{}
}

// FuncNode represents function call expression node.
type FuncNode interface {
	ExprNode
	functionExpression()
}

// StmtNode represents statement node.
// Name of implementations should have 'Stmt' suffix.
type StmtNode interface {
	Node
	statement()
}

// DDLNode represents DDL statement node.
type DDLNode interface {
	StmtNode
	ddlStatement()
}

// DMLNode represents DML statement node.
type DMLNode interface {
	StmtNode
	dmlStatement()
}

// ResultField is computed from a parsed select statement.
type ResultField struct {
	Column       *model.ColumnInfo
	ColumnAsName model.CIStr
	Table        *model.TableInfo
	TableAsName  model.CIStr
	DBName       model.CIStr
}

// ResultSetNode interface has ResultFields property which is computed and set by visitor.
// Implementations include SelectStmt, SubqueryExpr, TableSource, TableName and Join.
type ResultSetNode interface {
	Node
	// GetResultFields gets result fields of the result set node.
	GetResultFields() []*ResultField
	// SetResultFields sets result fields of the result set node.
	SetResultFields(fields []*ResultField)
}

// Visitor visits a Node.
type Visitor interface {
	// VisitEnter is called before children nodes is visited.
	// The returned node should be the same type as the input node n.
	// skipChildren returns true means children nodes should be skipped,
	// this is useful when work is done in Enter and there is no need to visit children.
	Enter(n Node) (node Node, skipChildren bool)
	// VisitLeave is called after children nodes has been visited.
	// The returned node should be the same type as the input node n.
	// ok returns false to stop visiting.
	Leave(n Node) (node Node, ok bool)
}
<|endoftext|>