<commit_before>package gost

import (
	"bufio"
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/go-log/log"
)

type httpConnector struct {
	User *url.Userinfo
}

// HTTPConnector creates a Connector for HTTP proxy client.
// It accepts an optional auth info for HTTP Basic Authentication.
func HTTPConnector(user *url.Userinfo) Connector {
	return &httpConnector{User: user}
}

func (c *httpConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {
	return c.ConnectContext(context.Background(), conn, "tcp", address, options...)
}

func (c *httpConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {
	switch network {
	case "udp", "udp4", "udp6":
		return nil, fmt.Errorf("%s unsupported", network)
	}

	opts := &ConnectOptions{}
	for _, option := range options {
		option(opts)
	}

	timeout := opts.Timeout
	if timeout <= 0 {
		timeout = ConnectTimeout
	}
	ua := opts.UserAgent
	if ua == "" {
		ua = DefaultUserAgent
	}

	conn.SetDeadline(time.Now().Add(timeout))
	defer conn.SetDeadline(time.Time{})

	req := &http.Request{
		Method:     http.MethodConnect,
		URL:        &url.URL{Host: address},
		Host:       address,
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
	}
	req.Header.Set("User-Agent", ua)
	req.Header.Set("Proxy-Connection", "keep-alive")

	user := opts.User
	if user == nil {
		user = c.User
	}

	if user != nil {
		u := user.Username()
		p, _ := user.Password()
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p)))
	}

	if err := req.Write(conn); err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Log(string(dump))
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Log(string(dump))
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%s", resp.Status)
	}

	return conn, nil
}

type httpHandler struct {
	options *HandlerOptions
}

// HTTPHandler creates a server Handler for HTTP proxy server.
func HTTPHandler(opts ...HandlerOption) Handler {
	h := &httpHandler{}
	h.Init(opts...)
	return h
}

func (h *httpHandler) Init(options ...HandlerOption) {
	if h.options == nil {
		h.options = &HandlerOptions{}
	}
	for _, opt := range options {
		opt(h.options)
	}
}

func (h *httpHandler) Handle(conn net.Conn) {
	defer conn.Close()

	req, err := http.ReadRequest(bufio.NewReader(conn))
	if err != nil {
		log.Logf("[http] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
		return
	}
	defer req.Body.Close()

	h.handleRequest(conn, req)
}

func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
	if req == nil {
		return
	}

	// try to get the actual host.
	if v := req.Header.Get("Gost-Target"); v != "" {
		if h, err := decodeServerName(v); err == nil {
			req.Host = h
		}
	}

	host := req.Host
	if _, port, _ := net.SplitHostPort(host); port == "" {
		host = net.JoinHostPort(host, "80")
	}

	u, _, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
	if u != "" {
		u += "@"
	}
	log.Logf("[http] %s%s -> %s -> %s",
		u, conn.RemoteAddr(), h.options.Node.String(), host)

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
	}

	req.Header.Del("Gost-Target")

	resp := &http.Response{
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     http.Header{},
	}
	resp.Header.Add("Proxy-Agent", "gost/"+Version)

	if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) {
		log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s",
			conn.RemoteAddr(), conn.LocalAddr(), host)
		resp.StatusCode = http.StatusForbidden

		if Debug {
			dump, _ := httputil.DumpResponse(resp, false)
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
		}

		resp.Write(conn)
		return
	}

	if h.options.Bypass.Contains(host) {
		resp.StatusCode = http.StatusForbidden

		log.Logf("[http] %s - %s bypass %s",
			conn.RemoteAddr(), conn.LocalAddr(), host)
		if Debug {
			dump, _ := httputil.DumpResponse(resp, false)
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
		}

		resp.Write(conn)
		return
	}

	if !h.authenticate(conn, req, resp) {
		return
	}

	if req.Method == "PRI" || (req.Method != http.MethodConnect && req.URL.Scheme != "http") {
		resp.StatusCode = http.StatusBadRequest

		if Debug {
			dump, _ := httputil.DumpResponse(resp, false)
			log.Logf("[http] %s <- %s\n%s",
				conn.RemoteAddr(), conn.LocalAddr(), string(dump))
		}

		resp.Write(conn)
		return
	}

	req.Header.Del("Proxy-Authorization")

	retries := 1
	if h.options.Chain != nil && h.options.Chain.Retries > 0 {
		retries = h.options.Chain.Retries
	}
	if h.options.Retries > 0 {
		retries = h.options.Retries
	}

	var err error
	var cc net.Conn
	var route *Chain
	for i := 0; i < retries; i++ {
		route, err = h.options.Chain.selectRouteFor(host)
		if err != nil {
			log.Logf("[http] %s -> %s : %s",
				conn.RemoteAddr(), conn.LocalAddr(), err)
			continue
		}

		buf := bytes.Buffer{}
		fmt.Fprintf(&buf, "%s -> %s -> ",
			conn.RemoteAddr(), h.options.Node.String())
		for _, nd := range route.route {
			fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String())
		}
		fmt.Fprintf(&buf, "%s", host)
		log.Log("[route]", buf.String())

		// forward http request
		lastNode := route.LastNode()
		if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
			err = h.forwardRequest(conn, req, route)
			if err == nil {
				return
			}
			log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
			continue
		}

		cc, err = route.Dial(host,
			TimeoutChainOption(h.options.Timeout),
			HostsChainOption(h.options.Hosts),
			ResolverChainOption(h.options.Resolver),
		)
		if err == nil {
			break
		}
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
	}

	if err != nil {
		resp.StatusCode = http.StatusServiceUnavailable

		if Debug {
			dump, _ := httputil.DumpResponse(resp, false)
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
		}

		resp.Write(conn)
		return
	}
	defer cc.Close()

	if req.Method == http.MethodConnect {
		b := []byte("HTTP/1.1 200 Connection established\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(b))
		}
		conn.Write(b)
	} else {
		req.Header.Del("Proxy-Connection")

		if err = req.Write(cc); err != nil {
			log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
			return
		}
	}

	log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", conn.RemoteAddr(), host)
}

func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response) (ok bool) {
	u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
	if Debug && (u != "" || p != "") {
		log.Logf("[http] %s -> %s : Authorization '%s' '%s'",
			conn.RemoteAddr(), conn.LocalAddr(), u, p)
	}
	if h.options.Authenticator == nil || h.options.Authenticator.Authenticate(u, p) {
		return true
	}

	// probing resistance is enabled, and knocking host is mismatch.
	if ss := strings.SplitN(h.options.ProbeResist, ":", 2); len(ss) == 2 &&
		(h.options.KnockingHost == "" || !strings.EqualFold(req.URL.Hostname(), h.options.KnockingHost)) {
		resp.StatusCode = http.StatusServiceUnavailable // default status code

		switch ss[0] {
		case "code":
			resp.StatusCode, _ = strconv.Atoi(ss[1])
		case "web":
			url := ss[1]
			if !strings.HasPrefix(url, "http") {
				url = "http://" + url
			}
			if r, err := http.Get(url); err == nil {
				resp = r
			}
		case "host":
			cc, err := net.Dial("tcp", ss[1])
			if err == nil {
				defer cc.Close()

				req.Write(cc)
				log.Logf("[http] %s <-> %s : forward to %s",
					conn.RemoteAddr(), conn.LocalAddr(), ss[1])
				transport(conn, cc)
				log.Logf("[http] %s >-< %s : forward to %s",
					conn.RemoteAddr(), conn.LocalAddr(), ss[1])
				return
			}
		case "file":
			f, _ := os.Open(ss[1])
			if f != nil {
				resp.StatusCode = http.StatusOK
				if finfo, _ := f.Stat(); finfo != nil {
					resp.ContentLength = finfo.Size()
				}
				resp.Header.Set("Content-Type", "text/html")
				resp.Body = f
			}
		}
	}

	if resp.StatusCode == 0 {
		log.Logf("[http] %s <- %s : proxy authentication required",
			conn.RemoteAddr(), conn.LocalAddr())
		resp.StatusCode = http.StatusProxyAuthRequired
		resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
	} else {
		resp.Header = http.Header{}
		resp.Header.Set("Server", "nginx/1.14.1")
		resp.Header.Set("Date", time.Now().Format(http.TimeFormat))
		if resp.StatusCode == http.StatusOK {
			resp.Header.Set("Connection", "keep-alive")
		}
	}

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Logf("[http] %s <- %s\n%s",
			conn.RemoteAddr(), conn.LocalAddr(), string(dump))
	}

	resp.Write(conn)
	return
}

func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Chain) error {
	if route.IsEmpty() {
		return nil
	}

	host := req.Host
	var userpass string

	if user := route.LastNode().User; user != nil {
		u := user.Username()
		p, _ := user.Password()
		userpass = base64.StdEncoding.EncodeToString([]byte(u + ":" + p))
	}

	cc, err := route.Conn()
	if err != nil {
		return err
	}
	defer cc.Close()

	errc := make(chan error, 1)
	go func() {
		errc <- copyBuffer(conn, cc)
	}()

	go func() {
		for {
			if userpass != "" {
				req.Header.Set("Proxy-Authorization", "Basic "+userpass)
			}

			cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
			if !req.URL.IsAbs() {
				req.URL.Scheme = "http" // make sure that the URL is absolute
			}
			err := req.WriteProxy(cc)
			if err != nil {
				log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
				errc <- err
				return
			}
			cc.SetWriteDeadline(time.Time{})

			req, err = http.ReadRequest(bufio.NewReader(conn))
			if err != nil {
				errc <- err
				return
			}

			if Debug {
				dump, _ := httputil.DumpRequest(req, false)
				log.Logf("[http] %s -> %s\n%s",
					conn.RemoteAddr(), conn.LocalAddr(), string(dump))
			}
		}
	}()

	log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host)
	<-errc
	log.Logf("[http] %s >-< %s", conn.RemoteAddr(), host)

	return nil
}

func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
	if proxyAuth == "" {
		return
	}

	if !strings.HasPrefix(proxyAuth, "Basic ") {
		return
	}
	c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
	if err != nil {
		return
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return
	}

	return cs[:s], cs[s+1:], true
}
<commit_msg>Close connection if authentication failed<commit_after>package gost

import (
	"bufio"
	"bytes"
	"context"
	"encoding/base64"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/go-log/log"
)

type httpConnector struct {
	User *url.Userinfo
}

// HTTPConnector creates a Connector for HTTP proxy client.
// It accepts an optional auth info for HTTP Basic Authentication.
func HTTPConnector(user *url.Userinfo) Connector {
	return &httpConnector{User: user}
}

func (c *httpConnector) Connect(conn net.Conn, address string, options ...ConnectOption) (net.Conn, error) {
	return c.ConnectContext(context.Background(), conn, "tcp", address, options...)
}

func (c *httpConnector) ConnectContext(ctx context.Context, conn net.Conn, network, address string, options ...ConnectOption) (net.Conn, error) {
	switch network {
	case "udp", "udp4", "udp6":
		return nil, fmt.Errorf("%s unsupported", network)
	}

	opts := &ConnectOptions{}
	for _, option := range options {
		option(opts)
	}

	timeout := opts.Timeout
	if timeout <= 0 {
		timeout = ConnectTimeout
	}
	ua := opts.UserAgent
	if ua == "" {
		ua = DefaultUserAgent
	}

	conn.SetDeadline(time.Now().Add(timeout))
	defer conn.SetDeadline(time.Time{})

	req := &http.Request{
		Method:     http.MethodConnect,
		URL:        &url.URL{Host: address},
		Host:       address,
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(http.Header),
	}
	req.Header.Set("User-Agent", ua)
	req.Header.Set("Proxy-Connection", "keep-alive")

	user := opts.User
	if user == nil {
		user = c.User
	}

	if user != nil {
		u := user.Username()
		p, _ := user.Password()
		req.Header.Set("Proxy-Authorization",
			"Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p)))
	}

	if err := req.Write(conn); err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Log(string(dump))
	}

	resp, err := http.ReadResponse(bufio.NewReader(conn), req)
	if err != nil {
		return nil, err
	}

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Log(string(dump))
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%s", resp.Status)
	}

	return conn, nil
}

type httpHandler struct {
	options *HandlerOptions
}

// HTTPHandler creates a server Handler for HTTP proxy server.
func HTTPHandler(opts ...HandlerOption) Handler {
	h := &httpHandler{}
	h.Init(opts...)
	return h
}

func (h *httpHandler) Init(options ...HandlerOption) {
	if h.options == nil {
		h.options = &HandlerOptions{}
	}
	for _, opt := range options {
		opt(h.options)
	}
}

func (h *httpHandler) Handle(conn net.Conn) {
	defer conn.Close()

	req, err := http.ReadRequest(bufio.NewReader(conn))
	if err != nil {
		log.Logf("[http] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
		return
	}
	defer req.Body.Close()

	h.handleRequest(conn, req)
}

func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
	if req == nil {
		return
	}

	// try to get the actual host.
	if v := req.Header.Get("Gost-Target"); v != "" {
		if h, err := decodeServerName(v); err == nil {
			req.Host = h
		}
	}

	host := req.Host
	if _, port, _ := net.SplitHostPort(host); port == "" {
		host = net.JoinHostPort(host, "80")
	}

	u, _, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
	if u != "" {
		u += "@"
	}
	log.Logf("[http] %s%s -> %s -> %s",
		u, conn.RemoteAddr(), h.options.Node.String(), host)

	if Debug {
		dump, _ := httputil.DumpRequest(req, false)
		log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
	}

	req.Header.Del("Gost-Target")

	resp := &http.Response{
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     http.Header{},
	}
	resp.Header.Add("Proxy-Agent", "gost/"+Version)

	if !Can("tcp", host, h.options.Whitelist, h.options.Blacklist) {
		log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s",
			conn.RemoteAddr(), conn.LocalAddr(), host)
		resp.StatusCode = http.StatusForbidden

		if Debug {
			dump, _ := httputil.DumpResponse(resp, false)
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
		}

		resp.Write(conn)
		return
	}

	if h.options.Bypass.Contains(host) {
		resp.StatusCode = http.StatusForbidden

		log.Logf("[http] %s - %s bypass %s",
			conn.RemoteAddr(), conn.LocalAddr(), host)
		if Debug {
			dump, _ := httputil.DumpResponse(resp, false)
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
		}

		resp.Write(conn)
		return
	}

	if !h.authenticate(conn, req, resp) {
		return
	}

	if req.Method == "PRI" || (req.Method != http.MethodConnect && req.URL.Scheme != "http") {
		resp.StatusCode = http.StatusBadRequest

		if Debug {
			dump, _ := httputil.DumpResponse(resp, false)
			log.Logf("[http] %s <- %s\n%s",
				conn.RemoteAddr(), conn.LocalAddr(), string(dump))
		}

		resp.Write(conn)
		return
	}

	req.Header.Del("Proxy-Authorization")

	retries := 1
	if h.options.Chain != nil && h.options.Chain.Retries > 0 {
		retries = h.options.Chain.Retries
	}
	if h.options.Retries > 0 {
		retries = h.options.Retries
	}

	var err error
	var cc net.Conn
	var route *Chain
	for i := 0; i < retries; i++ {
		route, err = h.options.Chain.selectRouteFor(host)
		if err != nil {
			log.Logf("[http] %s -> %s : %s",
				conn.RemoteAddr(), conn.LocalAddr(), err)
			continue
		}

		buf := bytes.Buffer{}
		fmt.Fprintf(&buf, "%s -> %s -> ",
			conn.RemoteAddr(), h.options.Node.String())
		for _, nd := range route.route {
			fmt.Fprintf(&buf, "%d@%s -> ", nd.ID, nd.String())
		}
		fmt.Fprintf(&buf, "%s", host)
		log.Log("[route]", buf.String())

		// forward http request
		lastNode := route.LastNode()
		if req.Method != http.MethodConnect && lastNode.Protocol == "http" {
			err = h.forwardRequest(conn, req, route)
			if err == nil {
				return
			}
			log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
			continue
		}

		cc, err = route.Dial(host,
			TimeoutChainOption(h.options.Timeout),
			HostsChainOption(h.options.Hosts),
			ResolverChainOption(h.options.Resolver),
		)
		if err == nil {
			break
		}
		log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
	}

	if err != nil {
		resp.StatusCode = http.StatusServiceUnavailable

		if Debug {
			dump, _ := httputil.DumpResponse(resp, false)
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(dump))
		}

		resp.Write(conn)
		return
	}
	defer cc.Close()

	if req.Method == http.MethodConnect {
		b := []byte("HTTP/1.1 200 Connection established\r\n" +
			"Proxy-Agent: gost/" + Version + "\r\n\r\n")
		if Debug {
			log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), string(b))
		}
		conn.Write(b)
	} else {
		req.Header.Del("Proxy-Connection")

		if err = req.Write(cc); err != nil {
			log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
			return
		}
	}

	log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host)
	transport(conn, cc)
	log.Logf("[http] %s >-< %s", conn.RemoteAddr(), host)
}

func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response) (ok bool) {
	u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
	if Debug && (u != "" || p != "") {
		log.Logf("[http] %s -> %s : Authorization '%s' '%s'",
			conn.RemoteAddr(), conn.LocalAddr(), u, p)
	}
	if h.options.Authenticator == nil || h.options.Authenticator.Authenticate(u, p) {
		return true
	}

	// probing resistance is enabled, and knocking host is mismatch.
	if ss := strings.SplitN(h.options.ProbeResist, ":", 2); len(ss) == 2 &&
		(h.options.KnockingHost == "" || !strings.EqualFold(req.URL.Hostname(), h.options.KnockingHost)) {
		resp.StatusCode = http.StatusServiceUnavailable // default status code

		switch ss[0] {
		case "code":
			resp.StatusCode, _ = strconv.Atoi(ss[1])
		case "web":
			url := ss[1]
			if !strings.HasPrefix(url, "http") {
				url = "http://" + url
			}
			if r, err := http.Get(url); err == nil {
				resp = r
			}
		case "host":
			cc, err := net.Dial("tcp", ss[1])
			if err == nil {
				defer cc.Close()

				req.Write(cc)
				log.Logf("[http] %s <-> %s : forward to %s",
					conn.RemoteAddr(), conn.LocalAddr(), ss[1])
				transport(conn, cc)
				log.Logf("[http] %s >-< %s : forward to %s",
					conn.RemoteAddr(), conn.LocalAddr(), ss[1])
				return
			}
		case "file":
			f, _ := os.Open(ss[1])
			if f != nil {
				resp.StatusCode = http.StatusOK
				if finfo, _ := f.Stat(); finfo != nil {
					resp.ContentLength = finfo.Size()
				}
				resp.Header.Set("Content-Type", "text/html")
				resp.Body = f
			}
		}
	}

	if resp.StatusCode == 0 {
		log.Logf("[http] %s <- %s : proxy authentication required",
			conn.RemoteAddr(), conn.LocalAddr())
		resp.StatusCode = http.StatusProxyAuthRequired
		resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
		if strings.ToLower(req.Header.Get("Proxy-Connection")) == "keep-alive" {
			// XXX libcurl will keep sending auth request in same conn
			// which we don't supported yet.
			resp.Header.Add("Connection", "close")
			resp.Header.Add("Proxy-Connection", "close")
		}
	} else {
		resp.Header = http.Header{}
		resp.Header.Set("Server", "nginx/1.14.1")
		resp.Header.Set("Date", time.Now().Format(http.TimeFormat))
		if resp.StatusCode == http.StatusOK {
			resp.Header.Set("Connection", "keep-alive")
		}
	}

	if Debug {
		dump, _ := httputil.DumpResponse(resp, false)
		log.Logf("[http] %s <- %s\n%s",
			conn.RemoteAddr(), conn.LocalAddr(), string(dump))
	}

	resp.Write(conn)
	return
}

func (h *httpHandler) forwardRequest(conn net.Conn, req *http.Request, route *Chain) error {
	if route.IsEmpty() {
		return nil
	}

	host := req.Host
	var userpass string

	if user := route.LastNode().User; user != nil {
		u := user.Username()
		p, _ := user.Password()
		userpass = base64.StdEncoding.EncodeToString([]byte(u + ":" + p))
	}

	cc, err := route.Conn()
	if err != nil {
		return err
	}
	defer cc.Close()

	errc := make(chan error, 1)
	go func() {
		errc <- copyBuffer(conn, cc)
	}()

	go func() {
		for {
			if userpass != "" {
				req.Header.Set("Proxy-Authorization", "Basic "+userpass)
			}

			cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
			if !req.URL.IsAbs() {
				req.URL.Scheme = "http" // make sure that the URL is absolute
			}
			err := req.WriteProxy(cc)
			if err != nil {
				log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
				errc <- err
				return
			}
			cc.SetWriteDeadline(time.Time{})

			req, err = http.ReadRequest(bufio.NewReader(conn))
			if err != nil {
				errc <- err
				return
			}

			if Debug {
				dump, _ := httputil.DumpRequest(req, false)
				log.Logf("[http] %s -> %s\n%s",
					conn.RemoteAddr(), conn.LocalAddr(), string(dump))
			}
		}
	}()

	log.Logf("[http] %s <-> %s", conn.RemoteAddr(), host)
	<-errc
	log.Logf("[http] %s >-< %s", conn.RemoteAddr(), host)

	return nil
}

func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
	if proxyAuth == "" {
		return
	}

	if !strings.HasPrefix(proxyAuth, "Basic ") {
		return
	}
	c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
	if err != nil {
		return
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return
	}

	return cs[:s], cs[s+1:], true
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Nan Deng
 *
 * 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 (
	"encoding/json"
	"fmt"
	"github.com/uniqush/uniqush-conn/msgcenter"
	"github.com/uniqush/uniqush-conn/proto"
	"io"
	"net/http"
	"time"
)

type sendMessageRequest struct {
	Service   string            `json:"service"`
	Username  string            `json:"username"`
	Header    map[string]string `json:"header,omitempty"`
	Body      []byte            `json:"body,omitempty"`
	TTL       string            `json:"ttl,omitempty"`
	PosterKey string            `json:"key,omitempty"`
}

func parseJson(input io.Reader) (req *sendMessageRequest, err error) {
	req = new(sendMessageRequest)
	decoder := json.NewDecoder(input)
	err = decoder.Decode(req)
	if err != nil {
		req = nil
	}
	return
}

type RequestProcessor struct {
	center *msgcenter.MessageCenter
}

func isPrefix(prefix, str string) bool {
	if len(str) > len(prefix) {
		if str[:len(prefix)] == prefix {
			return true
		}
	}
	return false
}

func (self *RequestProcessor) sendMessage(req *sendMessageRequest) (errs []error, res []*msgcenter.Result) {
	ttl := 24 * time.Hour
	if len(req.TTL) > 0 {
		var e error
		ttl, e = time.ParseDuration(req.TTL)
		if e != nil {
			errs = append(errs, e)
			return
		}
	}

	msg := new(proto.Message)
	msg.Header = make(map[string]string, len(req.Header))
	extra := make(map[string]string, len(req.Header))
	if len(req.Body) > 0 {
		msg.Body = []byte(req.Body)
	}

	for k, v := range req.Header {
		if isPrefix("notif.", k) {
			if isPrefix("notif.uniqush.", k) {
				errs = append(errs, fmt.Errorf("invalid key %v: notif.uniqush.* are reserved keys", k))
				return
			}
			extra[k] = v
		} else {
			msg.Header[k] = v
		}
	}
	if msg.IsEmpty() {
		errs = append(errs, fmt.Errorf("empty message"))
		return
	}

	if len(req.PosterKey) == 0 {
		res = self.center.SendMail(req.Service, req.Username, msg, extra, ttl)
	} else {
		res = self.center.SendPoster(req.Service, req.Username, msg, extra, req.PosterKey, ttl)
	}
	return
}

type HttpRequestProcessor struct {
	RequestProcessor
	addr string
}

func NewHttpRequestProcessor(addr string, center *msgcenter.MessageCenter) *HttpRequestProcessor {
	ret := new(HttpRequestProcessor)
	ret.addr = addr
	ret.center = center
	return ret
}

func (self *HttpRequestProcessor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	req, err := parseJson(r.Body)
	if err != nil {
		fmt.Fprintf(w, "Invalid input: %v\r\n", err)
		return
	}
	errs, res := self.sendMessage(req)
	for _, e := range errs {
		fmt.Fprintf(w, "%v\r\n", e)
	}
	for _, r := range res {
		fmt.Fprintf(w, "%v\r\n", r)
	}
	return
}

func (self *HttpRequestProcessor) Start() error {
	http.Handle("/send.json", self)
	err := http.ListenAndServe(self.addr, nil)
	return err
}

<commit_msg>go fmt<commit_after>/*
 * Copyright 2013 Nan Deng
 *
 * 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 (
	"encoding/json"
	"fmt"
	"github.com/uniqush/uniqush-conn/msgcenter"
	"github.com/uniqush/uniqush-conn/proto"
	"io"
	"net/http"
	"time"
)

type sendMessageRequest struct {
	Service   string            `json:"service"`
	Username  string            `json:"username"`
	Header    map[string]string `json:"header,omitempty"`
	Body      []byte            `json:"body,omitempty"`
	TTL       string            `json:"ttl,omitempty"`
	PosterKey string            `json:"key,omitempty"`
}

func parseJson(input io.Reader) (req *sendMessageRequest, err error) {
	req = new(sendMessageRequest)
	decoder := json.NewDecoder(input)
	err = decoder.Decode(req)
	if err != nil {
		req = nil
	}
	return
}

type RequestProcessor struct {
	center *msgcenter.MessageCenter
}

func isPrefix(prefix, str string) bool {
	if len(str) > len(prefix) {
		if str[:len(prefix)] == prefix {
			return true
		}
	}
	return false
}

func (self *RequestProcessor) sendMessage(req *sendMessageRequest) (errs []error, res []*msgcenter.Result) {
	ttl := 24 * time.Hour
	if len(req.TTL) > 0 {
		var e error
		ttl, e = time.ParseDuration(req.TTL)
		if e != nil {
			errs = append(errs, e)
			return
		}
	}

	msg := new(proto.Message)
	msg.Header = make(map[string]string, len(req.Header))
	extra := make(map[string]string, len(req.Header))
	if len(req.Body) > 0 {
		msg.Body = []byte(req.Body)
	}

	for k, v := range req.Header {
		if isPrefix("notif.", k) {
			if isPrefix("notif.uniqush.", k) {
				errs = append(errs, fmt.Errorf("invalid key %v: notif.uniqush.* are reserved keys", k))
				return
			}
			extra[k] = v
		} else {
			msg.Header[k] = v
		}
	}
	if msg.IsEmpty() {
		errs = append(errs, fmt.Errorf("empty message"))
		return
	}

	if len(req.PosterKey) == 0 {
		res = self.center.SendMail(req.Service, req.Username, msg, extra, ttl)
	} else {
		res = self.center.SendPoster(req.Service, req.Username, msg, extra, req.PosterKey, ttl)
	}
	return
}

type HttpRequestProcessor struct {
	RequestProcessor
	addr string
}

func NewHttpRequestProcessor(addr string, center *msgcenter.MessageCenter) *HttpRequestProcessor {
	ret := new(HttpRequestProcessor)
	ret.addr = addr
	ret.center = center
	return ret
}

func (self *HttpRequestProcessor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	req, err := parseJson(r.Body)
	if err != nil {
		fmt.Fprintf(w, "Invalid input: %v\r\n", err)
		return
	}
	errs, res := self.sendMessage(req)
	for _, e := range errs {
		fmt.Fprintf(w, "%v\r\n", e)
	}
	for _, r := range res {
		fmt.Fprintf(w, "%v\r\n", r)
	}
	return
}

func (self *HttpRequestProcessor) Start() error {
	http.Handle("/send.json", self)
	err := http.ListenAndServe(self.addr, nil)
	return err
}
<|endoftext|>


<commit_before><commit_msg>[backend] Fixed commits for bolt<commit_after><|endoftext|>


<commit_before>package goversioninfo

import (
	"encoding/binary"
	"io"
	"os"

	"github.com/akavel/rsrc/coff"
	"github.com/akavel/rsrc/ico"
)

// *****************************************************************************
/*
Code from https://github.com/akavel/rsrc

The MIT License (MIT)

Copyright (c) 2013-2014 The rsrc Authors.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// *****************************************************************************

const (
	rtIcon      = coff.RT_ICON
	rtGroupIcon = coff.RT_GROUP_ICON
	rtManifest  = coff.RT_MANIFEST
)

// on storing icons, see: http://blogs.msdn.com/b/oldnewthing/archive/2012/07/20/10331787.aspx
type gRPICONDIR struct {
	ico.ICONDIR
	Entries []gRPICONDIRENTRY
}

func (group gRPICONDIR) Size() int64 {
	return int64(binary.Size(group.ICONDIR) + len(group.Entries)*binary.Size(group.Entries[0]))
}

type gRPICONDIRENTRY struct {
	ico.IconDirEntryCommon
	ID uint16
}

func addIcon(coff *coff.Coff, fname string, newID <-chan uint16) error {
	f, err := os.Open(fname)
	if err != nil {
		return err
	}
	//defer f.Close() don't defer, files will be closed by OS when app closes

	icons, err := ico.DecodeHeaders(f)
	if err != nil {
		return err
	}

	if len(icons) > 0 {
		// RT_ICONs
		group := gRPICONDIR{ICONDIR: ico.ICONDIR{
			Reserved: 0, // magic num.
			Type:     1, // magic num.
			Count:    uint16(len(icons)),
		}}
		gid := <-newID
		for _, icon := range icons {
			id := <-newID
			r := io.NewSectionReader(f, int64(icon.ImageOffset), int64(icon.BytesInRes))
			coff.AddResource(rtIcon, id, r)
			group.Entries = append(group.Entries, gRPICONDIRENTRY{IconDirEntryCommon: icon.IconDirEntryCommon, ID: id})
		}
		coff.AddResource(rtGroupIcon, gid, group)
	}

	return nil
}
<commit_msg>We should close files, be a good citizen<commit_after>package goversioninfo

import (
	"encoding/binary"
	"io"
	"os"

	"github.com/akavel/rsrc/coff"
	"github.com/akavel/rsrc/ico"
)

// *****************************************************************************
/*
Code from https://github.com/akavel/rsrc

The MIT License (MIT)

Copyright (c) 2013-2014 The rsrc Authors.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// *****************************************************************************

const (
	rtIcon      = coff.RT_ICON
	rtGroupIcon = coff.RT_GROUP_ICON
	rtManifest  = coff.RT_MANIFEST
)

// on storing icons, see: http://blogs.msdn.com/b/oldnewthing/archive/2012/07/20/10331787.aspx
type gRPICONDIR struct {
	ico.ICONDIR
	Entries []gRPICONDIRENTRY
}

func (group gRPICONDIR) Size() int64 {
	return int64(binary.Size(group.ICONDIR) + len(group.Entries)*binary.Size(group.Entries[0]))
}

type gRPICONDIRENTRY struct {
	ico.IconDirEntryCommon
	ID uint16
}

func addIcon(coff *coff.Coff, fname string, newID <-chan uint16) error {
	f, err := os.Open(fname)
	if err != nil {
		return err
	}
	defer f.Close()

	icons, err := ico.DecodeHeaders(f)
	if err != nil {
		return err
	}

	if len(icons) > 0 {
		// RT_ICONs
		group := gRPICONDIR{ICONDIR: ico.ICONDIR{
			Reserved: 0, // magic num.
			Type:     1, // magic num.
			Count:    uint16(len(icons)),
		}}
		gid := <-newID
		for _, icon := range icons {
			id := <-newID
			r := io.NewSectionReader(f, int64(icon.ImageOffset), int64(icon.BytesInRes))
			coff.AddResource(rtIcon, id, r)
			group.Entries = append(group.Entries, gRPICONDIRENTRY{IconDirEntryCommon: icon.IconDirEntryCommon, ID: id})
		}
		coff.AddResource(rtGroupIcon, gid, group)
	}

	return nil
}
<|endoftext|>


<commit_before>//the icws_golib package wraps CIC ICWS fuctionality in a GO library.
//To get started, call icws_golib.NewIcws() to instansiate a new Icws
//struct.  Then call icws.Login(...) and continue on from there.
package icws_golib

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

type Icws struct {
	CurrentToken, CurrentCookie, CurrentSession, CurrentServer, UserId, HttpScheme string
	Port                                                                           int
}

//Version information for the server
type ServerVersion struct {
	//The product's two-digit release year. For the release "CIC 2015 R1" this value will be "15"
	MajorVersion string
	//The product's release number. For the release "CIC 2015 R1" this value will be "1".
	MinorVersion string
	//The patch number of the release. The value "0" indicates the release without any patches. For the release "CIC 2015 R1" this value will be "0", and for the release "CIC 2015 R1 Patch2" this value will be "2".
	Su string
	//The product line identifier.
	ProductId string
	//The codebase identifier.
	CodeBaseId string
	//The build number.
	Build string
	//The display string for the release. This does not include patch information.
	ProductReleaseDisplayString string
	//The display string for the release including patch information. This string is recommended for use on application "About" screens.
	ProductPatchDisplayString string
}

//Definition of a server feature
type ServerFeature struct {
	//Id of the features
	FeatureId string
	//version of the feature
	Version int
}

//Creates a new ICWS struct
func NewIcws() (icws *Icws) {
	icws = &Icws{}
	icws.HttpScheme = "https"
	icws.Port = 8019
	return
}

func (i *Icws) loginWithData(applicationName, server, username, password string, loginData map[string]string) (err error) {

	urlFormat := "%s://%s:%d"
	server = fmt.Sprintf(urlFormat, i.HttpScheme, server, i.Port)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf(urlFormat, nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//logs into CIC through a reverse proxy
func (i *Icws) ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password string, loginData map[string]string) (err error) {

	urlFormat := "%s://%s/%s"
	server := fmt.Sprintf(urlFormat, i.HttpScheme, proxyUrl, serverName)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf(urlFormat, i.HttpScheme, proxyUrl, nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//logs into a CIC server through a reverse proxy where the url to reach the server would be proxyUrl/serverName
func (i *Icws) ProxyLogin(applicationName, proxyUrl, serverName, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) ProxyLoginMarketPlaceApp(applicationName, proxyUrl, serverName, username, password, marketplaceLicense, marketplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
		"marketPlaceApplicationLicenseName": marketplaceLicense,
		"marketPlaceApplicationCode" : marketplaceAppKey,
	}

	return i.ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password, loginData)
}

//Logs into a CIC server.  Server should be a server name e.g. MyServer.domain.com
func (i *Icws) Login(applicationName, server, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) LoginMarketPlaceApp(applicationName, server, username, password, marketplaceLicense, marketplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
		"marketPlaceApplicationLicenseName": marketplaceLicense,
		"marketPlaceApplicationCode" : marketplaceAppKey,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//gets server version details.  Does not need to be connected to the server
func (i *Icws) GetVersion() (version ServerVersion, err error) {

	body, err := i.httpNoConnectionGet("/connection/version")

	err = json.Unmarshal(body, &version)

	return
}

//Gets a list of features and their version from the server
func (i *Icws) GetFeatures() (features []ServerFeature, err error) {

	body, err := i.httpNoConnectionGet("/connection/features")

	if err != nil {
		return nil, err
	}

	var featureMap map[string][]ServerFeature
	err = json.Unmarshal(body, &featureMap)

	features = featureMap["featureInfoList"]
	return

}

func (i *Icws) httpDelete(url string) (err error) {

	req, err := i.httpRequest("DELETE", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, _ := ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpGet(url string) (body []byte, err error) {

	req, err := i.httpRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpNoConnectionGet(url string) (body []byte, err error) {

	req, err := i.httpNoConnectionRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpPost(url string, attrs map[string]string) (body []byte, err error) {
	body, _, err, _ = i.httpPostAdvanced(url, attrs)
	return
}

func (i *Icws) httpPostAdvanced(url string, attrs map[string]string) (body []byte, statusCode int, err error, cookie string) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("POST", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()

	statusCode = response.StatusCode

	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	if response.Header["Set-Cookie"] != nil {
		cookie = response.Header["Set-Cookie"][0]
	}
	return
}

func (i *Icws) httpPut(url string, attrs map[string]string) (body []byte, err error) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("PUT", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpClient() (client *http.Client) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}
	return
}

func (i *Icws) httpNoConnectionRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws"+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	return
}

func (i *Icws) httpRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws/"+i.CurrentSession+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	if len(i.CurrentCookie) > 0 {
		request.Header.Add("Cookie", i.CurrentCookie)
	} else {
		return
	}

	if len(i.CurrentToken) > 0 {
		request.Header.Add("ININ-ICWS-CSRF-Token", i.CurrentToken)
	} else {
		return
	}

	// request.Header.Add("User-Agent", fmt.Sprintf("cic cli (%s-%s)", runtime.GOOS, runtime.GOARCH))
	return
}

func createErrorMessage(statusCode int, body []byte) string {

	var errorDescription string

	switch statusCode {
	case 400:
		errorDescription = "Bad Request (400)"
	case 401:
		errorDescription = "Unauthorized (401)"
	case 403:
		errorDescription = "Forbidden (403)"
	case 404:
		errorDescription = "Not Found (404)"
	case 410:
		errorDescription = "Gone (410)"
	case 500:
		errorDescription = "Internal Server Error (500)"
	}

	var message map[string]interface{}
	json.Unmarshal(body, &message)

	if message["errorId"] != nil {
		errorDescription += ": " + message["errorId"].(string)
	}

	if message["message"] != nil {
		errorDescription += " " + message["message"].(string)
	}
	return errorDescription
}
<commit_msg>changing url<commit_after>//the icws_golib package wraps CIC ICWS fuctionality in a GO library.
//To get started, call icws_golib.NewIcws() to instansiate a new Icws
//struct.  Then call icws.Login(...) and continue on from there.
package icws_golib

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

type Icws struct {
	CurrentToken, CurrentCookie, CurrentSession, CurrentServer, UserId, HttpScheme string
	Port                                                                           int
}

//Version information for the server
type ServerVersion struct {
	//The product's two-digit release year. For the release "CIC 2015 R1" this value will be "15"
	MajorVersion string
	//The product's release number. For the release "CIC 2015 R1" this value will be "1".
	MinorVersion string
	//The patch number of the release. The value "0" indicates the release without any patches. For the release "CIC 2015 R1" this value will be "0", and for the release "CIC 2015 R1 Patch2" this value will be "2".
	Su string
	//The product line identifier.
	ProductId string
	//The codebase identifier.
	CodeBaseId string
	//The build number.
	Build string
	//The display string for the release. This does not include patch information.
	ProductReleaseDisplayString string
	//The display string for the release including patch information. This string is recommended for use on application "About" screens.
	ProductPatchDisplayString string
}

//Definition of a server feature
type ServerFeature struct {
	//Id of the features
	FeatureId string
	//version of the feature
	Version int
}

//Creates a new ICWS struct
func NewIcws() (icws *Icws) {
	icws = &Icws{}
	icws.HttpScheme = "https"
	icws.Port = 8019
	return
}

func (i *Icws) loginWithData(applicationName, server, username, password string, loginData map[string]string) (err error) {

	urlFormat := "%s://%s:%d"
	server = fmt.Sprintf(urlFormat, i.HttpScheme, server, i.Port)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf(urlFormat, nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//logs into CIC through a reverse proxy
func (i *Icws) ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password string, loginData map[string]string) (err error) {

	urlFormat := "%s/%s"
	server := fmt.Sprintf(urlFormat, i.HttpScheme, proxyUrl, serverName)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf(urlFormat, i.HttpScheme, proxyUrl, nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//logs into a CIC server through a reverse proxy where the url to reach the server would be proxyUrl/serverName
func (i *Icws) ProxyLogin(applicationName, proxyUrl, serverName, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) ProxyLoginMarketPlaceApp(applicationName, proxyUrl, serverName, username, password, marketplaceLicense, marketplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
		"marketPlaceApplicationLicenseName": marketplaceLicense,
		"marketPlaceApplicationCode" : marketplaceAppKey,
	}

	return i.ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password, loginData)
}

//Logs into a CIC server.  Server should be a server name e.g. MyServer.domain.com
func (i *Icws) Login(applicationName, server, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) LoginMarketPlaceApp(applicationName, server, username, password, marketplaceLicense, marketplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
		"marketPlaceApplicationLicenseName": marketplaceLicense,
		"marketPlaceApplicationCode" : marketplaceAppKey,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//gets server version details.  Does not need to be connected to the server
func (i *Icws) GetVersion() (version ServerVersion, err error) {

	body, err := i.httpNoConnectionGet("/connection/version")

	err = json.Unmarshal(body, &version)

	return
}

//Gets a list of features and their version from the server
func (i *Icws) GetFeatures() (features []ServerFeature, err error) {

	body, err := i.httpNoConnectionGet("/connection/features")

	if err != nil {
		return nil, err
	}

	var featureMap map[string][]ServerFeature
	err = json.Unmarshal(body, &featureMap)

	features = featureMap["featureInfoList"]
	return

}

func (i *Icws) httpDelete(url string) (err error) {

	req, err := i.httpRequest("DELETE", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, _ := ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpGet(url string) (body []byte, err error) {

	req, err := i.httpRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpNoConnectionGet(url string) (body []byte, err error) {

	req, err := i.httpNoConnectionRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpPost(url string, attrs map[string]string) (body []byte, err error) {
	body, _, err, _ = i.httpPostAdvanced(url, attrs)
	return
}

func (i *Icws) httpPostAdvanced(url string, attrs map[string]string) (body []byte, statusCode int, err error, cookie string) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("POST", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()

	statusCode = response.StatusCode

	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	if response.Header["Set-Cookie"] != nil {
		cookie = response.Header["Set-Cookie"][0]
	}
	return
}

func (i *Icws) httpPut(url string, attrs map[string]string) (body []byte, err error) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("PUT", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpClient() (client *http.Client) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}
	return
}

func (i *Icws) httpNoConnectionRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws"+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	return
}

func (i *Icws) httpRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws/"+i.CurrentSession+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	if len(i.CurrentCookie) > 0 {
		request.Header.Add("Cookie", i.CurrentCookie)
	} else {
		return
	}

	if len(i.CurrentToken) > 0 {
		request.Header.Add("ININ-ICWS-CSRF-Token", i.CurrentToken)
	} else {
		return
	}

	// request.Header.Add("User-Agent", fmt.Sprintf("cic cli (%s-%s)", runtime.GOOS, runtime.GOARCH))
	return
}

func createErrorMessage(statusCode int, body []byte) string {

	var errorDescription string

	switch statusCode {
	case 400:
		errorDescription = "Bad Request (400)"
	case 401:
		errorDescription = "Unauthorized (401)"
	case 403:
		errorDescription = "Forbidden (403)"
	case 404:
		errorDescription = "Not Found (404)"
	case 410:
		errorDescription = "Gone (410)"
	case 500:
		errorDescription = "Internal Server Error (500)"
	}

	var message map[string]interface{}
	json.Unmarshal(body, &message)

	if message["errorId"] != nil {
		errorDescription += ": " + message["errorId"].(string)
	}

	if message["message"] != nil {
		errorDescription += " " + message["message"].(string)
	}
	return errorDescription
}
<|endoftext|>


<commit_before>// Copyright 2016 ego 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 riot
<commit_msg>add system info<commit_after>// Copyright 2016 ego 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 riot

import (
	"sync"

	"github.com/go-vgo/gt/info"
)

var (
	lck sync.RWMutex
	// InitMemUsed init mem used
	InitMemUsed uint64
	// InitDiskUsed init disk used
	InitDiskUsed uint64
)

func init() {
	lck.Lock()
	InitMemUsed, _ = MemUsed()
	InitDiskUsed, _ = DiskUsed()
	lck.Unlock()
}

// MemPercent returns the amount of use memory in percent.
func MemPercent() (string, error) {
	return info.MemPercent()
}

// MemUsed returns the amount of used memory in bytes.
func MemUsed() (uint64, error) {
	return info.MemUsed()
}

// UsedMem returns the amount of riot used memory in bytes
// after init() func.
func (engine *Engine) UsedMem() (uint64, error) {
	memUsed, err := MemUsed()
	if err != nil {
		return 0, err
	}

	return memUsed - InitMemUsed, err
}

// MemTotal returns the amount of total memory in bytes.
func MemTotal() (uint64, error) {
	return info.MemTotal()
}

// MemFree returns the amount of free memory in bytes.
func MemFree() (uint64, error) {
	return info.MemFree()
}

// ToKB bytes to kb
func ToKB(data uint64) uint64 {
	return data / 1024
}

// ToMB bytes to mb
func ToMB(data uint64) uint64 {
	return data / 1024 / 1024
}

// ToGB bytes to gb
func ToGB(data uint64) uint64 {
	return data / 1024 / 1024 / 1024
}

// Disk init the disk
// func Disk(pt ...bool) ([]*disk.UsageStat, error) {
// 	return info.Disk(pt...)
// }

// DiskPercent returns the amount of use disk in percent.
func DiskPercent() (string, error) {
	return info.DiskPercent()
}

// DiskUsed returns the amount of use disk in bytes.
func DiskUsed() (uint64, error) {
	return info.DiskUsed()
}

// UsedDisk returns the amount of use disk in bytes
// after init() func.
func (engine *Engine) UsedDisk() (uint64, error) {
	diskUsed, err := DiskUsed()
	if err != nil {
		return 0, err
	}

	return diskUsed - InitDiskUsed, err
}

// DiskTotal returns the amount of total disk in bytes.
func DiskTotal() (uint64, error) {
	return info.DiskTotal()
}

// DiskFree returns the amount of free disk in bytes.
func DiskFree() (uint64, error) {
	return info.DiskFree()
}

// CPUInfo returns the cpu info
func CPUInfo(args ...int) (string, error) {
	return info.CPUInfo(args...)
}

// CPUPercent returns the amount of use cpu in percent.
func CPUPercent() ([]float64, error) {
	return info.CPUPercent()
}

// Uptime returns the system uptime in seconds.
func Uptime() (uptime uint64, err error) {
	return info.Uptime()
}

// PlatformInfo fetches system platform information.
func PlatformInfo() (platform, family, osVersion string, err error) {
	return info.PlatformInfo()
}

// Platform returns the platform name and OS Version.
func Platform() (string, error) {
	return info.Platform()
}

// KernelVer returns the kernel version as a string.
func KernelVer() (string, error) {
	return info.KernelVer()
}
<|endoftext|>


<commit_before>package orm

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"reflect"

	"github.com/inconshreveable/log15"
	"github.com/pkg/errors"
	"github.com/rbastic/dyndao/object"
	"github.com/rbastic/dyndao/schema"
)

// NOTE: For foreign key filling, we do not check to see if there are conflicts
// with regards to the uniqueness of primary key names.
func pkQueryValsFromKV(obj *object.Object, sch *schema.Schema, parentTableName string) (map[string]interface{}, error) {
	qv := make(map[string]interface{})

	schemaTable := sch.GetTable(parentTableName)
	if schemaTable == nil {
		return nil, fmt.Errorf("pkQueryValsFromKV: no schema table for table named %s", parentTableName)
	}
	schemaPrimary := schemaTable.Primary

	for fName, field := range schemaTable.Fields {
		if field.IsIdentity || field.IsForeignKey || field.Name == schemaPrimary {
			qv[fName] = obj.Get(fName)
		}
	}
	return qv, nil
}

func (o ORM) recurseAndSave(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	rowsAff, err := o.SaveObject(ctx, tx, obj)
	if err != nil {
		return 0, err
	}

	table := o.s.GetTable(obj.Type)
	pkVal := obj.Get(table.Primary)

	for _, v := range obj.Children {
		for _, childObj := range v {
			// set the primary key in the child object, if it exists in the child object's table
			childTable, ok := o.s.Tables[childObj.Type]
			if !ok {
				return 0, fmt.Errorf("recurseAndSave: Unknown child object type %s for parent type %s", childObj.Type, obj.Type)
			}
			// TODO: support propagation of additional primary keys that are
			// saved from previous recursive saves
			// ... check if the child schema table contains
			// the parent's primary key field as a name
			_, ok = childTable.Fields[table.Primary]
			if ok {
				// set in the child object if the table contains the primary
				childObj.Set(table.Primary, pkVal)
			}

			aff, err := o.recurseAndSave(ctx, tx, childObj)
			if err != nil {
				return rowsAff + aff, err
			}
		}
	}
	return rowsAff, err
}

// SaveAllInsideTx will attempt to save an entire nested object structure inside of a single transaction.
func (o ORM) SaveAllInsideTx(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	// TODO: Review this code for how it uses transactions / rollbacks.
	rowsAff, err := o.recurseAndSave(ctx, tx, obj)
	if err != nil {
		err2 := tx.Rollback()
		if err2 != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, err2.Error())
		}
		return 0, err
	}
	return rowsAff, nil
}

// SaveAll will attempt to save an entire nested object structure inside of a single transaction.
// It begins the transaction, attempts to recursively save the object and all of it's children,
// and any of the children's children, and then will finally rollback/commit as necessary.
func (o ORM) SaveAll(ctx context.Context, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}

	tx, err := o.RawConn.BeginTx(ctx, nil)
	if err != nil {
		return 0, err
	}
	rowsAff, err := o.SaveAllInsideTx(ctx, tx, obj)
	if err != nil {
		rollErr := tx.Rollback()
		if rollErr != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, rollErr.Error())
		}
		return 0, err
	}

	err = tx.Commit()
	if err != nil {
		rollErr := tx.Rollback()
		if rollErr != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, rollErr.Error())
		}
		return 0, err
	}
	return rowsAff, nil
}

// SaveObjectButErrorIfUpdate function will INSERT or UPDATE a record. It does not attempt to
// save any of the children. If given a transaction, it will use that to
// attempt to insert the data.
func (o ORM) SaveObjectButErrorIfUpdate(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObjectButErrorIfUpdate: unknown object table " + obj.Type)
	}
	// skip if object is saved
	if obj.GetSaved() {
		return 0, nil
	}
	// retrieve primary key value
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObjectButErrorIfUpdate: empty primary key for " + obj.Type)
	}
	// skip if primary key has no field configuration in table schema
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObjectButErrorIfUpdate: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return o.Insert(ctx, tx, obj)
	}
	return 0, fmt.Errorf("SaveObjectButErrorIfUpdate: ORM was told to expect an Insert for this obj: %v", obj)
}

// SaveObjectButErrorIfInsert function will UPDATE a record and error if it
// appears that an INSERT should have been performed. This could be necessary in
// situations where an INSERT would compromise the integrity of the data.  If
// given a transaction, it will use that to attempt to insert the data.
func (o ORM) SaveObjectButErrorIfInsert(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObjectButErrorIfInsert: unknown object table " + obj.Type)
	}
	// skip objects that are saved
	if obj.GetSaved() {
		return 0, nil
	}
	// ensure we have a primary key
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObjectButErrorIfInsert: empty primary key for " + obj.Type)
	}
	// ensure the primary key has a field config
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObjectButErrorIfInsert: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return 0, fmt.Errorf("SaveObjectButErrorIfInsert: ORM was told to expect an Update for this obj: %v", obj)
	}
	return o.Update(ctx, tx, obj)
}

// SaveObject function will INSERT or UPDATE a record. It does not attempt to
// save any of the children. If given a transaction, it will use that to
// attempt to insert the data.
func (o ORM) SaveObject(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObject: unknown object table " + obj.Type)
	}
	// skip if object is saved
	if obj.GetSaved() {
		return 0, nil
	}
	// retrieve primary key value
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObject: empty primary key for " + obj.Type)
	}
	// skip if primary key has no field configuration in table schema
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObject: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return o.Insert(ctx, tx, obj)
	}
	return o.Update(ctx, tx, obj)
}

// use transaction if needed, otherwise just execute a non-transactionalized operation
func stmtFromDbOrTx(ctx context.Context, o ORM, tx *sql.Tx, sqlStr string) (*sql.Stmt, error) {
	var stmt *sql.Stmt
	var err error
	if tx != nil {
		stmt, err = tx.PrepareContext(ctx, sqlStr)
	} else {
		stmt, err = o.RawConn.PrepareContext(ctx, sqlStr)
	}
	return stmt, err
}

func maybeDereferenceArgs(arg interface{}) interface{} {
	v := reflect.ValueOf(arg)
	return reflect.Indirect(v).Interface()
}

// Insert function will INSERT a record, given an optional transaction and an object.
// It returns the number of rows affected (int64) and any error that may have occurred.
func (o ORM) Insert(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	objTable := o.s.GetTable(obj.Type)
	if objTable == nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", "thing was unknown")
		}
		return 0, errors.New("Insert: unknown object table " + obj.Type)
	}
	sqlStr, bindArgs, err := o.sqlGen.BindingInsert(o.s, obj.Type, obj.KV)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", err)
		}
		return 0, err
	}
	if os.Getenv("DEBUG_INSERT") != "" {
		fmt.Println("Insert/sqlStr=", sqlStr, "bindArgs=", bindArgs)
	}

	var lastID int64
	if o.sqlGen.FixLastInsertIDbug() {
		bindArgs = append(bindArgs, sql.Named(o.s.GetTable(obj.Type).Primary, sql.Out{
			Dest: &lastID,
		}))
	}

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Info("orm/save error", "error", err)
		}

		return 0, err
	}
	defer func() {
		err := stmt.Close()
		if err != nil {
			fmt.Println(err) // TODO: logging implementation
		}
	}()

	newBindArgs := make([]interface{}, len(bindArgs))
	for i, arg := range bindArgs {
		newBindArgs[i] = maybeDereferenceArgs(arg)
	}

	res, err := stmt.ExecContext(ctx, bindArgs...)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("orm/save error", err)
		}

		return 0, errors.Wrap(err, "Insert/ExecContext")
	}

	{
		newID, err := res.LastInsertId()
		if err != nil && lastID == 0 {
			if os.Getenv("DEBUG_INSERT") != "" {
				fmt.Println("orm/save error", err)
			}
			return 0, err
		}
		if lastID != 0 {
			newID = lastID
		}
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("DEBUG Insert received newID=", newID)
		}
		obj.Set(objTable.Primary, newID) // Set the new primary key in the object
	}

	rowsAff, err := res.RowsAffected()
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("orm/save error", err)
		}
		return 0, err
	}
	obj.SetSaved(true)       // Note that the object has been recently saved
	obj.ResetChangedFields() // Reset the 'changed fields', if any
	return rowsAff, nil
}

// Update function will UPDATE a record ...
func (o ORM) Update(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	sqlStr, bindArgs, bindWhere, err := o.sqlGen.BindingUpdate(o.s, obj)
	if err != nil {
		if os.Getenv("DEBUG_UPDATE") != "" {
			fmt.Println("Update/sqlStr, err=", err)
		}
		return 0, err
	}
	if os.Getenv("DEBUG_UPDATE") != "" {
		fmt.Println("Update/sqlStr=", sqlStr, "bindArgs=", bindArgs, "bindWhere=", bindWhere)
	}

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		return 0, err
	}
	defer func() {
		err := stmt.Close()
		if err != nil {
			fmt.Println(err) // TODO logging implementation
		}
	}()

	allBind := append(bindArgs, bindWhere...)
	newAllBind := make([]interface{}, len(allBind))
	for i, arg := range allBind {
		newAllBind[i] = maybeDereferenceArgs(arg)
	}
	res, err := stmt.ExecContext(ctx, newAllBind...)
	if err != nil {
		return 0, errors.Wrap(err, "Update")
	}

	rowsAff, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}

	obj.SetSaved(true)       // Note that the object has been recently saved
	obj.ResetChangedFields() // Reset the 'changed fields', if any

	return rowsAff, nil
}
<commit_msg>add more log15 to orm Insert<commit_after>package orm

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"reflect"

	"github.com/inconshreveable/log15"
	"github.com/pkg/errors"
	"github.com/rbastic/dyndao/object"
	"github.com/rbastic/dyndao/schema"
)

// NOTE: For foreign key filling, we do not check to see if there are conflicts
// with regards to the uniqueness of primary key names.
func pkQueryValsFromKV(obj *object.Object, sch *schema.Schema, parentTableName string) (map[string]interface{}, error) {
	qv := make(map[string]interface{})

	schemaTable := sch.GetTable(parentTableName)
	if schemaTable == nil {
		return nil, fmt.Errorf("pkQueryValsFromKV: no schema table for table named %s", parentTableName)
	}
	schemaPrimary := schemaTable.Primary

	for fName, field := range schemaTable.Fields {
		if field.IsIdentity || field.IsForeignKey || field.Name == schemaPrimary {
			qv[fName] = obj.Get(fName)
		}
	}
	return qv, nil
}

func (o ORM) recurseAndSave(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	rowsAff, err := o.SaveObject(ctx, tx, obj)
	if err != nil {
		return 0, err
	}

	table := o.s.GetTable(obj.Type)
	pkVal := obj.Get(table.Primary)

	for _, v := range obj.Children {
		for _, childObj := range v {
			// set the primary key in the child object, if it exists in the child object's table
			childTable, ok := o.s.Tables[childObj.Type]
			if !ok {
				return 0, fmt.Errorf("recurseAndSave: Unknown child object type %s for parent type %s", childObj.Type, obj.Type)
			}
			// TODO: support propagation of additional primary keys that are
			// saved from previous recursive saves
			// ... check if the child schema table contains
			// the parent's primary key field as a name
			_, ok = childTable.Fields[table.Primary]
			if ok {
				// set in the child object if the table contains the primary
				childObj.Set(table.Primary, pkVal)
			}

			aff, err := o.recurseAndSave(ctx, tx, childObj)
			if err != nil {
				return rowsAff + aff, err
			}
		}
	}
	return rowsAff, err
}

// SaveAllInsideTx will attempt to save an entire nested object structure inside of a single transaction.
func (o ORM) SaveAllInsideTx(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	// TODO: Review this code for how it uses transactions / rollbacks.
	rowsAff, err := o.recurseAndSave(ctx, tx, obj)
	if err != nil {
		err2 := tx.Rollback()
		if err2 != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, err2.Error())
		}
		return 0, err
	}
	return rowsAff, nil
}

// SaveAll will attempt to save an entire nested object structure inside of a single transaction.
// It begins the transaction, attempts to recursively save the object and all of it's children,
// and any of the children's children, and then will finally rollback/commit as necessary.
func (o ORM) SaveAll(ctx context.Context, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}

	tx, err := o.RawConn.BeginTx(ctx, nil)
	if err != nil {
		return 0, err
	}
	rowsAff, err := o.SaveAllInsideTx(ctx, tx, obj)
	if err != nil {
		rollErr := tx.Rollback()
		if rollErr != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, rollErr.Error())
		}
		return 0, err
	}

	err = tx.Commit()
	if err != nil {
		rollErr := tx.Rollback()
		if rollErr != nil {
			// TODO: Not sure if this wrap is right.
			return 0, errors.Wrap(err, rollErr.Error())
		}
		return 0, err
	}
	return rowsAff, nil
}

// SaveObjectButErrorIfUpdate function will INSERT or UPDATE a record. It does not attempt to
// save any of the children. If given a transaction, it will use that to
// attempt to insert the data.
func (o ORM) SaveObjectButErrorIfUpdate(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObjectButErrorIfUpdate: unknown object table " + obj.Type)
	}
	// skip if object is saved
	if obj.GetSaved() {
		return 0, nil
	}
	// retrieve primary key value
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObjectButErrorIfUpdate: empty primary key for " + obj.Type)
	}
	// skip if primary key has no field configuration in table schema
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObjectButErrorIfUpdate: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return o.Insert(ctx, tx, obj)
	}
	return 0, fmt.Errorf("SaveObjectButErrorIfUpdate: ORM was told to expect an Insert for this obj: %v", obj)
}

// SaveObjectButErrorIfInsert function will UPDATE a record and error if it
// appears that an INSERT should have been performed. This could be necessary in
// situations where an INSERT would compromise the integrity of the data.  If
// given a transaction, it will use that to attempt to insert the data.
func (o ORM) SaveObjectButErrorIfInsert(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObjectButErrorIfInsert: unknown object table " + obj.Type)
	}
	// skip objects that are saved
	if obj.GetSaved() {
		return 0, nil
	}
	// ensure we have a primary key
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObjectButErrorIfInsert: empty primary key for " + obj.Type)
	}
	// ensure the primary key has a field config
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObjectButErrorIfInsert: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return 0, fmt.Errorf("SaveObjectButErrorIfInsert: ORM was told to expect an Update for this obj: %v", obj)
	}
	return o.Update(ctx, tx, obj)
}

// SaveObject function will INSERT or UPDATE a record. It does not attempt to
// save any of the children. If given a transaction, it will use that to
// attempt to insert the data.
func (o ORM) SaveObject(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	objTable := o.s.GetTable(obj.Type)
	// skip if object has invalid type
	if objTable == nil {
		return 0, errors.New("SaveObject: unknown object table " + obj.Type)
	}
	// skip if object is saved
	if obj.GetSaved() {
		return 0, nil
	}
	// retrieve primary key value
	pk := objTable.Primary
	if pk == "" {
		return 0, errors.New("SaveObject: empty primary key for " + obj.Type)
	}
	// skip if primary key has no field configuration in table schema
	fieldMap := objTable.Fields
	f := fieldMap[pk]
	if f == nil {
		return 0, errors.New("SaveObject: empty field " + pk + " for " + obj.Type)
	}
	// Check the primary key to see if we should insert or update
	_, ok := obj.KV[f.Name]
	if !ok {
		return o.Insert(ctx, tx, obj)
	}
	return o.Update(ctx, tx, obj)
}

// use transaction if needed, otherwise just execute a non-transactionalized operation
func stmtFromDbOrTx(ctx context.Context, o ORM, tx *sql.Tx, sqlStr string) (*sql.Stmt, error) {
	var stmt *sql.Stmt
	var err error
	if tx != nil {
		stmt, err = tx.PrepareContext(ctx, sqlStr)
	} else {
		stmt, err = o.RawConn.PrepareContext(ctx, sqlStr)
	}
	return stmt, err
}

func maybeDereferenceArgs(arg interface{}) interface{} {
	v := reflect.ValueOf(arg)
	return reflect.Indirect(v).Interface()
}

// Insert function will INSERT a record, given an optional transaction and an object.
// It returns the number of rows affected (int64) and any error that may have occurred.
func (o ORM) Insert(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	errorString := "orm/save error"

	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}

	objTable := o.s.GetTable(obj.Type)
	if objTable == nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Error(errorString, "GetTable_error", "thing was unknown")
		}
		return 0, errors.New("Insert: unknown object table " + obj.Type)
	}
	sqlStr, bindArgs, err := o.sqlGen.BindingInsert(o.s, obj.Type, obj.KV)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Error(errorString, "BindingInsert_error", err)
		}
		return 0, err
	}
	if os.Getenv("DEBUG_INSERT") != "" {
		fmt.Println("Insert/sqlStr=", sqlStr, "bindArgs=", bindArgs)
	}

	var lastID int64
	// Oracle-specific fix
	if o.sqlGen.FixLastInsertIDbug() {
		bindArgs = append(bindArgs, sql.Named(o.s.GetTable(obj.Type).Primary, sql.Out{
			Dest: &lastID,
		}))
	}

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Error(errorString, "stmtFromDbOrTx_error", err)
		}

		return 0, err
	}
	defer func() {
		err := stmt.Close()
		if err != nil {
			fmt.Println("Insert stmt.Close error=", err) // TODO: logging implementation
		}
	}()

	// TODO: Still necessary?
	newBindArgs := make([]interface{}, len(bindArgs))
	for i, arg := range bindArgs {
		newBindArgs[i] = maybeDereferenceArgs(arg)
	}

	res, err := stmt.ExecContext(ctx, bindArgs...)
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			log15.Error( errorString, "ExecContext_error", err )
			fmt.Println("orm/save error", err)
		}

		return 0, errors.Wrap(err, "Insert/ExecContext")
	}

	{
		newID, err := res.LastInsertId()
		if err != nil && lastID == 0 {
			if os.Getenv("DEBUG_INSERT") != "" {
				fmt.Println("orm/save error", err)
			}
			log15.Error( errorString, "LastInsertID_error", err )
			return 0, err
		}
		if lastID != 0 {
			newID = lastID
		}
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("DEBUG Insert received newID=", newID)
		}

		obj.Set(objTable.Primary, newID) // Set the new primary key in the object
	}

	rowsAff, err := res.RowsAffected()
	if err != nil {
		if os.Getenv("DEBUG_INSERT") != "" {
			fmt.Println("orm/save error", err)
		}
		return 0, err
	}
	obj.SetSaved(true)       // Note that the object has been recently saved
	obj.ResetChangedFields() // Reset the 'changed fields', if any
	return rowsAff, nil
}

// Update function will UPDATE a record ...
func (o ORM) Update(ctx context.Context, tx *sql.Tx, obj *object.Object) (int64, error) {
	select {
	case <-ctx.Done():
		return 0, ctx.Err()
	default:
	}
	sqlStr, bindArgs, bindWhere, err := o.sqlGen.BindingUpdate(o.s, obj)
	if err != nil {
		if os.Getenv("DEBUG_UPDATE") != "" {
			fmt.Println("Update/sqlStr, err=", err)
		}
		return 0, err
	}
	if os.Getenv("DEBUG_UPDATE") != "" {
		fmt.Println("Update/sqlStr=", sqlStr, "bindArgs=", bindArgs, "bindWhere=", bindWhere)
	}

	stmt, err := stmtFromDbOrTx(ctx, o, tx, sqlStr)
	if err != nil {
		return 0, err
	}
	defer func() {
		err := stmt.Close()
		if err != nil {
			fmt.Println(err) // TODO logging implementation
		}
	}()

	allBind := append(bindArgs, bindWhere...)
	newAllBind := make([]interface{}, len(allBind))
	for i, arg := range allBind {
		newAllBind[i] = maybeDereferenceArgs(arg)
	}
	res, err := stmt.ExecContext(ctx, newAllBind...)
	if err != nil {
		return 0, errors.Wrap(err, "Update")
	}

	rowsAff, err := res.RowsAffected()
	if err != nil {
		return 0, err
	}

	obj.SetSaved(true)       // Note that the object has been recently saved
	obj.ResetChangedFields() // Reset the 'changed fields', if any

	return rowsAff, nil
}
<|endoftext|>


<commit_before>package pcap

/*
struct pcap { int dummy; };
#include <stdlib.h>
#include <pcap.h>
*/
import "C";
import (
	"syscall";
	"unsafe";
)

const (
	ERRBUF_SIZE = 256;

	// according to pcap-linktype(7)
	LINKTYPE_NULL = 0;
	LINKTYPE_ETHERNET = 1;
	LINKTYPE_TOKEN_RING = 6;
	LINKTYPE_ARCNET = 7;
	LINKTYPE_SLIP = 8;
	LINKTYPE_PPP = 9;
	LINKTYPE_FDDI = 10;
	LINKTYPE_ATM_RFC1483 = 100;
	LINKTYPE_RAW = 101;
	LINKTYPE_PPP_HDLC = 50;
	LINKTYPE_PPP_ETHER = 51;
	LINKTYPE_C_HDLC = 104;
	LINKTYPE_IEEE802_11 = 105;
	LINKTYPE_FRELAY = 107;
	LINKTYPE_LOOP = 108;
	LINKTYPE_LINUX_SLL = 113;
	LINKTYPE_LTALK = 104;
	LINKTYPE_PFLOG = 117;
	LINKTYPE_PRISM_HEADER = 119;
	LINKTYPE_IP_OVER_FC = 122;
	LINKTYPE_SUNATM = 123;
	LINKTYPE_IEEE802_11_RADIO = 127;
	LINKTYPE_ARCNET_LINUX = 129;
	LINKTYPE_LINUX_IRDA = 144;
	LINKTYPE_LINUX_LAPD = 177;
)

type Pcap struct {
	cptr *C.pcap_t;
}

type Packet struct {
	Time syscall.Timeval;
	Caplen uint32;
	Len uint32;
	Data []byte;
}

type Stat struct {
	PacketsReceived uint32;
	PacketsDropped uint32;
	PacketsIfDropped uint32;
}

type Interface struct {
	Name string;
	Description string;
	// TODO: add more elements
}

func Openlive(device string, snaplen int32, promisc bool, timeout_ms int32) (handle *Pcap, err string) {
	var buf *C.char;
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1));
	h := new(Pcap);
	var pro int32;
	if promisc { pro = 1 } else { pro = 0 }
	h.cptr = C.pcap_open_live(C.CString(device), C.int(snaplen), C.int(pro), C.int(timeout_ms), buf);
	if nil == h.cptr {
		handle = nil;
		err = C.GoString(buf);
	} else {
		handle = h;
	}
	C.free(unsafe.Pointer(buf));
	return;
}

func Openoffline(file string) (handle *Pcap, err string) {
	var buf *C.char;
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1));
	h := new(Pcap);
	h.cptr = C.pcap_open_offline(C.CString(file), buf);
	if nil == h.cptr {
		handle = nil;
		err = C.GoString(buf);
	} else {
		handle = h;
	}
	C.free(unsafe.Pointer(buf));
	return;
}

func(p *Pcap) Next() (pkt *Packet) {
	var pkthdr _Cstruct_pcap_pkthdr;
	var buf unsafe.Pointer;
	buf = unsafe.Pointer(C.pcap_next(p.cptr, &pkthdr));
	if nil == buf {
		return nil;
	}
	pkt = new(Packet);
	pkt.Time.Sec = int64(pkthdr.ts.tv_sec);
	pkt.Time.Usec = int64(pkthdr.ts.tv_usec);
	pkt.Caplen = uint32(pkthdr.caplen);
	pkt.Len = uint32(pkthdr.len);
	pkt.Data = make([]byte, pkthdr.caplen);

	for i := uint32(0) ; i < pkt.Caplen ; i++ {
		pkt.Data[i] = *(*byte)(unsafe.Pointer(uintptr(buf) + uintptr(i)));
	}

	return;
}

func(p *Pcap) Geterror() string {
	return C.GoString(C.pcap_geterr(p.cptr));
}

func(p *Pcap) Getstats() (stat *Stat, err string) {
	var cstats _Cstruct_pcap_stat;
	if -1 == C.pcap_stats(p.cptr, &cstats) {
		return nil, p.Geterror()
	}

	stats := new(Stat);

	stats.PacketsReceived = uint32(cstats.ps_recv);
	stats.PacketsDropped = uint32(cstats.ps_drop);
	stats.PacketsIfDropped = uint32(cstats.ps_ifdrop);

	return stats, "";
}

func(p *Pcap) Setfilter(expr string) (err string) {
	var bpf _Cstruct_bpf_program;

	if -1 == C.pcap_compile(p.cptr, &bpf, C.CString(expr), 1, 0) {
		return p.Geterror()
	}

	if -1 == C.pcap_setfilter(p.cptr, &bpf) {
		C.pcap_freecode(&bpf);
		return p.Geterror()
	}

	C.pcap_freecode(&bpf);
	return ""
}

func Version() string {
	return C.GoString(C.pcap_lib_version())
}

func(p *Pcap) Datalink() int {
	return int(C.pcap_datalink(p.cptr))
}

func(p *Pcap) Setdatalink(dlt int) string {
	if -1 == C.pcap_set_datalink(p.cptr, C.int(dlt)) {
		return p.Geterror()
	}
	return ""
}

func DatalinkValueToName(dlt int) string {
	name := C.pcap_datalink_val_to_name(C.int(dlt));
	if nil != name {
		return C.GoString(name)
	}
	return ""
}

func DatalinkValueToDescription(dlt int) string {
	desc := C.pcap_datalink_val_to_description(C.int(dlt));
	if nil != desc {
		return C.GoString(desc)
	}
	return ""
}

func Findalldevs() (ifs []Interface, err string) {
	var buf *C.char;
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1));
	var alldevsp *_Cstruct_pcap_if;

	if -1 == C.pcap_findalldevs((**C.pcap_if_t)(&alldevsp), buf) {
		ifs = nil;
		err = C.GoString(buf)
	} else {
		dev := alldevsp;
		var i uint32;
		for i = 0; dev != nil ; dev = dev.next {
			i++;
		}
		ifs = make([]Interface, i);
		dev = alldevsp;
		for j := uint32(0) ; dev != nil ; dev = dev.next {
			var iface Interface;
			iface.Name = C.GoString(dev.name);
			iface.Description = C.GoString(dev.description);
			// TODO: add more elements
			ifs[j] = iface;
			j++;
		}
		C.pcap_freealldevs((*C.pcap_if_t)(alldevsp));
	}
	C.free(unsafe.Pointer(buf));
	return
}

func(p *Pcap)  Inject(data []byte) (err string) {
	buf := (*C.char)(C.malloc((C.size_t)(len(data))));

	for i:=0 ; i < len(data) ; i++ {
		*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + uintptr(i))) = data[i];
	}

	if -1 == C.pcap_inject(p.cptr, unsafe.Pointer(buf), (C.size_t)(len(data))) {
		err = p.Geterror()
	}
	C.free(unsafe.Pointer(buf));
	return
}
<commit_msg>make gopcap work on 32 bit systems (patch by spennig)<commit_after>package pcap

/*
struct pcap { int dummy; };
#include <stdlib.h>
#include <pcap.h>
*/
import "C";
import (
	"unsafe";
)

const (
	ERRBUF_SIZE = 256;

	// according to pcap-linktype(7)
	LINKTYPE_NULL = 0;
	LINKTYPE_ETHERNET = 1;
	LINKTYPE_TOKEN_RING = 6;
	LINKTYPE_ARCNET = 7;
	LINKTYPE_SLIP = 8;
	LINKTYPE_PPP = 9;
	LINKTYPE_FDDI = 10;
	LINKTYPE_ATM_RFC1483 = 100;
	LINKTYPE_RAW = 101;
	LINKTYPE_PPP_HDLC = 50;
	LINKTYPE_PPP_ETHER = 51;
	LINKTYPE_C_HDLC = 104;
	LINKTYPE_IEEE802_11 = 105;
	LINKTYPE_FRELAY = 107;
	LINKTYPE_LOOP = 108;
	LINKTYPE_LINUX_SLL = 113;
	LINKTYPE_LTALK = 104;
	LINKTYPE_PFLOG = 117;
	LINKTYPE_PRISM_HEADER = 119;
	LINKTYPE_IP_OVER_FC = 122;
	LINKTYPE_SUNATM = 123;
	LINKTYPE_IEEE802_11_RADIO = 127;
	LINKTYPE_ARCNET_LINUX = 129;
	LINKTYPE_LINUX_IRDA = 144;
	LINKTYPE_LINUX_LAPD = 177;
)

type Pcap struct {
	cptr *C.pcap_t;
}

type Packet struct {
	Time struct { Sec int32; Usec int32 };
	Caplen uint32;
	Len uint32;
	Data []byte;
}

type Stat struct {
	PacketsReceived uint32;
	PacketsDropped uint32;
	PacketsIfDropped uint32;
}

type Interface struct {
	Name string;
	Description string;
	// TODO: add more elements
}

func Openlive(device string, snaplen int32, promisc bool, timeout_ms int32) (handle *Pcap, err string) {
	var buf *C.char;
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1));
	h := new(Pcap);
	var pro int32;
	if promisc { pro = 1 } else { pro = 0 }
	h.cptr = C.pcap_open_live(C.CString(device), C.int(snaplen), C.int(pro), C.int(timeout_ms), buf);
	if nil == h.cptr {
		handle = nil;
		err = C.GoString(buf);
	} else {
		handle = h;
	}
	C.free(unsafe.Pointer(buf));
	return;
}

func Openoffline(file string) (handle *Pcap, err string) {
	var buf *C.char;
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1));
	h := new(Pcap);
	h.cptr = C.pcap_open_offline(C.CString(file), buf);
	if nil == h.cptr {
		handle = nil;
		err = C.GoString(buf);
	} else {
		handle = h;
	}
	C.free(unsafe.Pointer(buf));
	return;
}

func(p *Pcap) Next() (pkt *Packet) {
	var pkthdr _Cstruct_pcap_pkthdr;
	var buf unsafe.Pointer;
	buf = unsafe.Pointer(C.pcap_next(p.cptr, &pkthdr));
	if nil == buf {
		return nil;
	}
	pkt = new(Packet);
	pkt.Time.Sec = int32(pkthdr.ts.tv_sec);
	pkt.Time.Usec = int32(pkthdr.ts.tv_usec);
	pkt.Caplen = uint32(pkthdr.caplen);
	pkt.Len = uint32(pkthdr.len);
	pkt.Data = make([]byte, pkthdr.caplen);

	for i := uint32(0) ; i < pkt.Caplen ; i++ {
		pkt.Data[i] = *(*byte)(unsafe.Pointer(uintptr(buf) + uintptr(i)));
	}

	return;
}

func(p *Pcap) Geterror() string {
	return C.GoString(C.pcap_geterr(p.cptr));
}

func(p *Pcap) Getstats() (stat *Stat, err string) {
	var cstats _Cstruct_pcap_stat;
	if -1 == C.pcap_stats(p.cptr, &cstats) {
		return nil, p.Geterror()
	}

	stats := new(Stat);

	stats.PacketsReceived = uint32(cstats.ps_recv);
	stats.PacketsDropped = uint32(cstats.ps_drop);
	stats.PacketsIfDropped = uint32(cstats.ps_ifdrop);

	return stats, "";
}

func(p *Pcap) Setfilter(expr string) (err string) {
	var bpf _Cstruct_bpf_program;

	if -1 == C.pcap_compile(p.cptr, &bpf, C.CString(expr), 1, 0) {
		return p.Geterror()
	}

	if -1 == C.pcap_setfilter(p.cptr, &bpf) {
		C.pcap_freecode(&bpf);
		return p.Geterror()
	}

	C.pcap_freecode(&bpf);
	return ""
}

func Version() string {
	return C.GoString(C.pcap_lib_version())
}

func(p *Pcap) Datalink() int {
	return int(C.pcap_datalink(p.cptr))
}

func(p *Pcap) Setdatalink(dlt int) string {
	if -1 == C.pcap_set_datalink(p.cptr, C.int(dlt)) {
		return p.Geterror()
	}
	return ""
}

func DatalinkValueToName(dlt int) string {
	name := C.pcap_datalink_val_to_name(C.int(dlt));
	if nil != name {
		return C.GoString(name)
	}
	return ""
}

func DatalinkValueToDescription(dlt int) string {
	desc := C.pcap_datalink_val_to_description(C.int(dlt));
	if nil != desc {
		return C.GoString(desc)
	}
	return ""
}

func Findalldevs() (ifs []Interface, err string) {
	var buf *C.char;
	buf = (*C.char)(C.calloc(ERRBUF_SIZE, 1));
	var alldevsp *_Cstruct_pcap_if;

	if -1 == C.pcap_findalldevs((**C.pcap_if_t)(&alldevsp), buf) {
		ifs = nil;
		err = C.GoString(buf)
	} else {
		dev := alldevsp;
		var i uint32;
		for i = 0; dev != nil ; dev = dev.next {
			i++;
		}
		ifs = make([]Interface, i);
		dev = alldevsp;
		for j := uint32(0) ; dev != nil ; dev = dev.next {
			var iface Interface;
			iface.Name = C.GoString(dev.name);
			iface.Description = C.GoString(dev.description);
			// TODO: add more elements
			ifs[j] = iface;
			j++;
		}
		C.pcap_freealldevs((*C.pcap_if_t)(alldevsp));
	}
	C.free(unsafe.Pointer(buf));
	return
}

func(p *Pcap)  Inject(data []byte) (err string) {
	buf := (*C.char)(C.malloc((C.size_t)(len(data))));

	for i:=0 ; i < len(data) ; i++ {
		*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + uintptr(i))) = data[i];
	}

	if -1 == C.pcap_inject(p.cptr, unsafe.Pointer(buf), (C.size_t)(len(data))) {
		err = p.Geterror()
	}
	C.free(unsafe.Pointer(buf));
	return
}
<|endoftext|>