<commit_before>package goftp

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var REGEX_PWD_PATH *regexp.Regexp = regexp.MustCompile(`\"(.*)\"`)

type FTP struct {
	conn net.Conn

	addr string

	debug     bool
	tlsconfig *tls.Config

	reader *bufio.Reader
	writer *bufio.Writer
}

func (ftp *FTP) Close() {
	ftp.conn.Close()
}

type WalkFunc func(path string, info os.FileMode, err error) error
type RetrFunc func(r io.Reader) error

func parseLine(line string) (perm string, t string, filename string) {
	for _, v := range strings.Split(line, ";") {
		v2 := strings.Split(v, "=")

		switch v2[0] {
		case "perm":
			perm = v2[1]
		case "type":
			t = v2[1]
		default:
			filename = v[1 : len(v)-2]
		}
	}

	return
}

// walks recursively through path and call walkfunc for each file
func (ftp *FTP) Walk(path string, walkFn WalkFunc) (err error) {
	/*
		if err = walkFn(path, os.ModeDir, nil); err != nil {
			if err == filepath.SkipDir {
				return nil
			}
		}
	*/
	if ftp.debug {
		log.Printf("Walking: '%s'\n", path)
	}

	var lines []string

	if lines, err = ftp.List(path); err != nil {
		return
	}

	for _, line := range lines {
		_, t, subpath := parseLine(line)

		switch t {
		case "dir":
			if subpath == "." {
			} else if subpath == ".." {
			} else {
				if err = ftp.Walk(path+subpath+"/", walkFn); err != nil {
					return
				}
			}
		case "file":
			if err = walkFn(path+subpath, os.FileMode(0), nil); err != nil {
				return
			}
		}
	}

	return
}

// send quit to the server and close the connection
func (ftp *FTP) Quit() (err error) {
	if _, err := ftp.cmd("221", "QUIT"); err != nil {
		return err
	}

	ftp.conn.Close()
	ftp.conn = nil

	return nil
}

// will send a NOOP (no operation) to the server
func (ftp *FTP) Noop() (err error) {
	_, err = ftp.cmd("200", "NOOP")
	return
}

// private function to send command and compare return code with expects
func (ftp *FTP) cmd(expects string, command string, args ...interface{}) (line string, err error) {
	if err = ftp.send(command, args...); err != nil {
		return
	}

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, expects) {
		err = errors.New(line)
		return
	}

	return
}

// rename file
func (ftp *FTP) Rename(from string, to string) (err error) {
	if _, err = ftp.cmd("350", "RNFR %s", from); err != nil {
		return
	}

	if _, err = ftp.cmd("250", "RNTO %s", to); err != nil {
		return
	}

	return
}

// make directory
func (ftp *FTP) Mkd(path string) error {
	_, err := ftp.cmd("257", "MKD %s", path)
	return err
}

// get current path
func (ftp *FTP) Pwd() (path string, err error) {
	var line string
	if line, err = ftp.cmd("257", "PWD"); err != nil {
		return
	}

	res := REGEX_PWD_PATH.FindAllStringSubmatch(line[4:], -1)

	path = res[0][1]
	return
}

// change current path
func (ftp *FTP) Cwd(path string) (err error) {
	_, err = ftp.cmd("250", "CWD %s", path)
	return
}

// delete file
func (ftp *FTP) Dele(path string) (err error) {
	if err = ftp.send("DELE %s", path); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "250") {
		return errors.New(line)
	}

	return
}

// secures the ftp connection by using TLS
func (ftp *FTP) AuthTLS(config tls.Config) error {
	if _, err := ftp.cmd("234", "AUTH TLS"); err != nil {
		return err
	}

	// wrap tls on existing connection
	ftp.tlsconfig = &config

	ftp.conn = tls.Client(ftp.conn, &config)
	ftp.writer = bufio.NewWriter(ftp.conn)
	ftp.reader = bufio.NewReader(ftp.conn)

	if _, err := ftp.cmd("200", "PROT P"); err != nil {
		return err
	}

	return nil
}

// change transfer type
func (ftp *FTP) Type(t string) error {
	_, err := ftp.cmd("200", "TYPE %s", t)
	return err
}

func (ftp *FTP) receiveLine() (string, error) {
	line, err := ftp.reader.ReadString('\n')

	if ftp.debug {
		log.Printf("< %s", line)
	}

	return line, err
}

func (ftp *FTP) receive() (string, error) {
	line, err := ftp.receiveLine()

	if err != nil {
		return line, err
	}

	if line[3] == '-' {
		nextLine := ""
		// This is a continuation of output line
		nextLine, err = ftp.receive()
		line = line + nextLine
	}

	return line, err
}

func (ftp *FTP) send(command string, arguments ...interface{}) error {
	if ftp.debug {
		log.Printf("> %s", fmt.Sprintf(command, arguments...))
	}

	command = fmt.Sprintf(command, arguments...)
	command += "\r\n"

	if _, err := ftp.writer.WriteString(command); err != nil {
		return err
	}

	if err := ftp.writer.Flush(); err != nil {
		return err
	}

	return nil
}

// enables passive data connection and returns port number
func (ftp *FTP) Pasv() (port int, err error) {
	var line string
	if line, err = ftp.cmd("227", "PASV"); err != nil {
		return
	}

	re, err := regexp.Compile(`\((.*)\)`)

	res := re.FindAllStringSubmatch(line, -1)

	s := strings.Split(res[0][1], ",")

	l1, _ := strconv.Atoi(s[len(s)-2])
	l2, _ := strconv.Atoi(s[len(s)-1])

	port = l1<<8 + l2

	return
}

// open new data connection
func (ftp *FTP) newConnection(port int) (conn net.Conn, err error) {
	addr := fmt.Sprintf("%s:%d", strings.Split(ftp.addr, ":")[0], port)

	if ftp.debug {
		log.Printf("Connecting to %s\n", addr)
	}

	if conn, err = net.Dial("tcp", addr); err != nil {
		return
	}

	if ftp.tlsconfig != nil {
		conn = tls.Client(conn, ftp.tlsconfig)
	}

	return
}

// upload file
func (ftp *FTP) Stor(path string, r io.Reader) (err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("STOR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if _, err = io.Copy(pconn, r); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return

}

// retrieves file
func (ftp *FTP) Retr(path string, retrFn RetrFunc) (s string, err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("RETR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if err = retrFn(pconn); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

// list the path (or current directory)
func (ftp *FTP) List(path string) (files []string, err error) {
	if err = ftp.Type("A"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	// check if MLSD works
	if err = ftp.send("MLSD %s", path); err != nil {
		if err = ftp.send("LIST %s", path); err != nil {
			return
		}
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	reader := bufio.NewReader(pconn)

	for {
		line, err = reader.ReadString('\n')

		if err == io.EOF {
			break
		} else if err != nil {
			return
		}

		files = append(files, string(line))
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

// login to the server
func (ftp *FTP) Login(username string, password string) (err error) {
	if _, err = ftp.cmd("331", "USER %s", username); err != nil {
		if strings.HasPrefix(err.Error(), "230") {
			// Ok, probably anonymous server
			// but login was fine, so return no error
			err = nil
		} else {
			return
		}
	}

	if _, err = ftp.cmd("230", "PASS %s", password); err != nil {
		return
	}

	return
}

// connect to server
func Connect(addr string, debugp bool) (*FTP, error) {
	var err error
	var conn net.Conn

	if conn, err = net.Dial("tcp", addr); err != nil {
		return nil, err
	}

	writer := bufio.NewWriter(conn)
	reader := bufio.NewReader(conn)

	var line string

	line, err = reader.ReadString('\n')

	var debug bool = debugp

	if debug {
		log.Print(line)
	}

	return &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: debug}, nil
}
<commit_msg>Add length check on line<commit_after>package goftp

import (
	"bufio"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var REGEX_PWD_PATH *regexp.Regexp = regexp.MustCompile(`\"(.*)\"`)

type FTP struct {
	conn net.Conn

	addr string

	debug     bool
	tlsconfig *tls.Config

	reader *bufio.Reader
	writer *bufio.Writer
}

func (ftp *FTP) Close() {
	ftp.conn.Close()
}

type WalkFunc func(path string, info os.FileMode, err error) error
type RetrFunc func(r io.Reader) error

func parseLine(line string) (perm string, t string, filename string) {
	for _, v := range strings.Split(line, ";") {
		v2 := strings.Split(v, "=")

		switch v2[0] {
		case "perm":
			perm = v2[1]
		case "type":
			t = v2[1]
		default:
			filename = v[1 : len(v)-2]
		}
	}

	return
}

// walks recursively through path and call walkfunc for each file
func (ftp *FTP) Walk(path string, walkFn WalkFunc) (err error) {
	/*
		if err = walkFn(path, os.ModeDir, nil); err != nil {
			if err == filepath.SkipDir {
				return nil
			}
		}
	*/
	if ftp.debug {
		log.Printf("Walking: '%s'\n", path)
	}

	var lines []string

	if lines, err = ftp.List(path); err != nil {
		return
	}

	for _, line := range lines {
		_, t, subpath := parseLine(line)

		switch t {
		case "dir":
			if subpath == "." {
			} else if subpath == ".." {
			} else {
				if err = ftp.Walk(path+subpath+"/", walkFn); err != nil {
					return
				}
			}
		case "file":
			if err = walkFn(path+subpath, os.FileMode(0), nil); err != nil {
				return
			}
		}
	}

	return
}

// send quit to the server and close the connection
func (ftp *FTP) Quit() (err error) {
	if _, err := ftp.cmd("221", "QUIT"); err != nil {
		return err
	}

	ftp.conn.Close()
	ftp.conn = nil

	return nil
}

// will send a NOOP (no operation) to the server
func (ftp *FTP) Noop() (err error) {
	_, err = ftp.cmd("200", "NOOP")
	return
}

// private function to send command and compare return code with expects
func (ftp *FTP) cmd(expects string, command string, args ...interface{}) (line string, err error) {
	if err = ftp.send(command, args...); err != nil {
		return
	}

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, expects) {
		err = errors.New(line)
		return
	}

	return
}

// rename file
func (ftp *FTP) Rename(from string, to string) (err error) {
	if _, err = ftp.cmd("350", "RNFR %s", from); err != nil {
		return
	}

	if _, err = ftp.cmd("250", "RNTO %s", to); err != nil {
		return
	}

	return
}

// make directory
func (ftp *FTP) Mkd(path string) error {
	_, err := ftp.cmd("257", "MKD %s", path)
	return err
}

// get current path
func (ftp *FTP) Pwd() (path string, err error) {
	var line string
	if line, err = ftp.cmd("257", "PWD"); err != nil {
		return
	}

	res := REGEX_PWD_PATH.FindAllStringSubmatch(line[4:], -1)

	path = res[0][1]
	return
}

// change current path
func (ftp *FTP) Cwd(path string) (err error) {
	_, err = ftp.cmd("250", "CWD %s", path)
	return
}

// delete file
func (ftp *FTP) Dele(path string) (err error) {
	if err = ftp.send("DELE %s", path); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "250") {
		return errors.New(line)
	}

	return
}

// secures the ftp connection by using TLS
func (ftp *FTP) AuthTLS(config tls.Config) error {
	if _, err := ftp.cmd("234", "AUTH TLS"); err != nil {
		return err
	}

	// wrap tls on existing connection
	ftp.tlsconfig = &config

	ftp.conn = tls.Client(ftp.conn, &config)
	ftp.writer = bufio.NewWriter(ftp.conn)
	ftp.reader = bufio.NewReader(ftp.conn)

	if _, err := ftp.cmd("200", "PROT P"); err != nil {
		return err
	}

	return nil
}

// change transfer type
func (ftp *FTP) Type(t string) error {
	_, err := ftp.cmd("200", "TYPE %s", t)
	return err
}

func (ftp *FTP) receiveLine() (string, error) {
	line, err := ftp.reader.ReadString('\n')

	if ftp.debug {
		log.Printf("< %s", line)
	}

	return line, err
}

func (ftp *FTP) receive() (string, error) {
	line, err := ftp.receiveLine()

	if err != nil {
		return line, err
	}

	if (len(line) >= 4) && (line[3] == '-') {
		nextLine := ""
		// This is a continuation of output line
		nextLine, err = ftp.receive()
		line = line + nextLine
	}

	return line, err
}

func (ftp *FTP) send(command string, arguments ...interface{}) error {
	if ftp.debug {
		log.Printf("> %s", fmt.Sprintf(command, arguments...))
	}

	command = fmt.Sprintf(command, arguments...)
	command += "\r\n"

	if _, err := ftp.writer.WriteString(command); err != nil {
		return err
	}

	if err := ftp.writer.Flush(); err != nil {
		return err
	}

	return nil
}

// enables passive data connection and returns port number
func (ftp *FTP) Pasv() (port int, err error) {
	var line string
	if line, err = ftp.cmd("227", "PASV"); err != nil {
		return
	}

	re, err := regexp.Compile(`\((.*)\)`)

	res := re.FindAllStringSubmatch(line, -1)

	s := strings.Split(res[0][1], ",")

	l1, _ := strconv.Atoi(s[len(s)-2])
	l2, _ := strconv.Atoi(s[len(s)-1])

	port = l1<<8 + l2

	return
}

// open new data connection
func (ftp *FTP) newConnection(port int) (conn net.Conn, err error) {
	addr := fmt.Sprintf("%s:%d", strings.Split(ftp.addr, ":")[0], port)

	if ftp.debug {
		log.Printf("Connecting to %s\n", addr)
	}

	if conn, err = net.Dial("tcp", addr); err != nil {
		return
	}

	if ftp.tlsconfig != nil {
		conn = tls.Client(conn, ftp.tlsconfig)
	}

	return
}

// upload file
func (ftp *FTP) Stor(path string, r io.Reader) (err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("STOR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if _, err = io.Copy(pconn, r); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return

}

// retrieves file
func (ftp *FTP) Retr(path string, retrFn RetrFunc) (s string, err error) {
	if err = ftp.Type("I"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	if err = ftp.send("RETR %s", path); err != nil {
		return
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	if err = retrFn(pconn); err != nil {
		return
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

// list the path (or current directory)
func (ftp *FTP) List(path string) (files []string, err error) {
	if err = ftp.Type("A"); err != nil {
		return
	}

	var port int
	if port, err = ftp.Pasv(); err != nil {
		return
	}

	// check if MLSD works
	if err = ftp.send("MLSD %s", path); err != nil {
		if err = ftp.send("LIST %s", path); err != nil {
			return
		}
	}

	var pconn net.Conn
	if pconn, err = ftp.newConnection(port); err != nil {
		return
	}

	var line string
	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "150") {
		err = errors.New(line)
		return
	}

	reader := bufio.NewReader(pconn)

	for {
		line, err = reader.ReadString('\n')

		if err == io.EOF {
			break
		} else if err != nil {
			return
		}

		files = append(files, string(line))
	}

	pconn.Close()

	if line, err = ftp.receive(); err != nil {
		return
	}

	if !strings.HasPrefix(line, "226") {
		err = errors.New(line)
		return
	}

	return
}

// login to the server
func (ftp *FTP) Login(username string, password string) (err error) {
	if _, err = ftp.cmd("331", "USER %s", username); err != nil {
		if strings.HasPrefix(err.Error(), "230") {
			// Ok, probably anonymous server
			// but login was fine, so return no error
			err = nil
		} else {
			return
		}
	}

	if _, err = ftp.cmd("230", "PASS %s", password); err != nil {
		return
	}

	return
}

// connect to server
func Connect(addr string, debugp bool) (*FTP, error) {
	var err error
	var conn net.Conn

	if conn, err = net.Dial("tcp", addr); err != nil {
		return nil, err
	}

	writer := bufio.NewWriter(conn)
	reader := bufio.NewReader(conn)

	var line string

	line, err = reader.ReadString('\n')

	var debug bool = debugp

	if debug {
		log.Print(line)
	}

	return &FTP{conn: conn, addr: addr, reader: reader, writer: writer, debug: debug}, nil
}
<|endoftext|>


<commit_before>package dbr

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/gocraft/dbr/dialect"
)

// Open instantiates a Connection for a given database/sql connection
// and event receiver
func Open(driver, dsn string, log EventReceiver) (*Connection, error) {
	if log == nil {
		log = nullReceiver
	}
	conn, err := sql.Open(driver, dsn)
	if err != nil {
		return nil, err
	}
	var d Dialect
	switch driver {
	case "mysql":
		d = dialect.MySQL
	case "postgres":
		d = dialect.PostgreSQL
	case "sqlite3":
		d = dialect.SQLite3
	default:
		return nil, ErrNotSupported
	}
	return &Connection{DB: conn, EventReceiver: log, Dialect: d}, nil
}

// FIXME: This will be removed in the future
func NewConnection(db *sql.DB, log EventReceiver) *Connection {
	if log == nil {
		log = nullReceiver
	}
	return &Connection{DB: db, EventReceiver: log, Dialect: dialect.MySQL}
}

const (
	placeholder = "?"
)

// Connection is a connection to the database with an EventReceiver
// to send events, errors, and timings to
type Connection struct {
	*sql.DB
	Dialect Dialect
	EventReceiver
}

// Session represents a business unit of execution for some connection
type Session struct {
	*Connection
	EventReceiver
}

// NewSession instantiates a Session for the Connection
func (conn *Connection) NewSession(log EventReceiver) *Session {
	if log == nil {
		log = conn.EventReceiver // Use parent instrumentation
	}
	return &Session{Connection: conn, EventReceiver: log}
}

// Ensure that tx and session are session runner
var (
	_ SessionRunner = (*Tx)(nil)
	_ SessionRunner = (*Session)(nil)
)

// SessionRunner can do anything that a Session can except start a transaction.
type SessionRunner interface {
	Select(column ...string) *SelectBuilder
	SelectBySql(query string, value ...interface{}) *SelectBuilder

	InsertInto(table string) *InsertBuilder
	InsertBySql(query string, value ...interface{}) *InsertBuilder

	Update(table string) *UpdateBuilder
	UpdateBySql(query string, value ...interface{}) *UpdateBuilder

	DeleteFrom(table string) *DeleteBuilder
	DeleteBySql(query string, value ...interface{}) *DeleteBuilder
}

type runner interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
}

func exec(runner runner, log EventReceiver, builder interface{}, d Dialect) (sql.Result, error) {
	i := interpolator{
		Buffer:       NewBuffer(),
		Dialect:      d,
		IgnoreBinary: true,
	}
	err := i.interpolate(placeholder, []interface{}{builder})
	query, value := i.String(), i.Value()
	if err != nil {
		return nil, log.EventErrKv("dbr.exec.interpolate", err, kvs{
			"sql":  query,
			"args": fmt.Sprint(value),
		})
	}

	startTime := time.Now()
	defer func() {
		log.TimingKv("dbr.exec", time.Since(startTime).Nanoseconds(), kvs{
			"sql": query,
		})
	}()

	result, err := runner.Exec(query, value...)
	if err != nil {
		return result, log.EventErrKv("dbr.exec.exec", err, kvs{
			"sql": query,
		})
	}
	return result, nil
}

func query(runner runner, log EventReceiver, builder interface{}, d Dialect, dest interface{}) (int, error) {
	i := interpolator{
		Buffer:       NewBuffer(),
		Dialect:      d,
		IgnoreBinary: true,
	}
	err := i.interpolate(placeholder, []interface{}{builder})
	query, value := i.String(), i.Value()
	if err != nil {
		return 0, log.EventErrKv("dbr.select.interpolate", err, kvs{
			"sql":  query,
			"args": fmt.Sprint(value),
		})
	}

	startTime := time.Now()
	defer func() {
		log.TimingKv("dbr.select", time.Since(startTime).Nanoseconds(), kvs{
			"sql": query,
		})
	}()

	rows, err := runner.Query(query, value...)
	if err != nil {
		return 0, log.EventErrKv("dbr.select.load.query", err, kvs{
			"sql": query,
		})
	}
	count, err := Load(rows, dest)
	if err != nil {
		return 0, log.EventErrKv("dbr.select.load.scan", err, kvs{
			"sql": query,
		})
	}
	return count, nil
}
<commit_msg>TWEAK: use builder interface in runner<commit_after>package dbr

import (
	"database/sql"
	"fmt"
	"time"

	"github.com/gocraft/dbr/dialect"
)

// Open instantiates a Connection for a given database/sql connection
// and event receiver
func Open(driver, dsn string, log EventReceiver) (*Connection, error) {
	if log == nil {
		log = nullReceiver
	}
	conn, err := sql.Open(driver, dsn)
	if err != nil {
		return nil, err
	}
	var d Dialect
	switch driver {
	case "mysql":
		d = dialect.MySQL
	case "postgres":
		d = dialect.PostgreSQL
	case "sqlite3":
		d = dialect.SQLite3
	default:
		return nil, ErrNotSupported
	}
	return &Connection{DB: conn, EventReceiver: log, Dialect: d}, nil
}

// FIXME: This will be removed in the future
func NewConnection(db *sql.DB, log EventReceiver) *Connection {
	if log == nil {
		log = nullReceiver
	}
	return &Connection{DB: db, EventReceiver: log, Dialect: dialect.MySQL}
}

const (
	placeholder = "?"
)

// Connection is a connection to the database with an EventReceiver
// to send events, errors, and timings to
type Connection struct {
	*sql.DB
	Dialect Dialect
	EventReceiver
}

// Session represents a business unit of execution for some connection
type Session struct {
	*Connection
	EventReceiver
}

// NewSession instantiates a Session for the Connection
func (conn *Connection) NewSession(log EventReceiver) *Session {
	if log == nil {
		log = conn.EventReceiver // Use parent instrumentation
	}
	return &Session{Connection: conn, EventReceiver: log}
}

// Ensure that tx and session are session runner
var (
	_ SessionRunner = (*Tx)(nil)
	_ SessionRunner = (*Session)(nil)
)

// SessionRunner can do anything that a Session can except start a transaction.
type SessionRunner interface {
	Select(column ...string) *SelectBuilder
	SelectBySql(query string, value ...interface{}) *SelectBuilder

	InsertInto(table string) *InsertBuilder
	InsertBySql(query string, value ...interface{}) *InsertBuilder

	Update(table string) *UpdateBuilder
	UpdateBySql(query string, value ...interface{}) *UpdateBuilder

	DeleteFrom(table string) *DeleteBuilder
	DeleteBySql(query string, value ...interface{}) *DeleteBuilder
}

type runner interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
}

func exec(runner runner, log EventReceiver, builder Builder, d Dialect) (sql.Result, error) {
	i := interpolator{
		Buffer:       NewBuffer(),
		Dialect:      d,
		IgnoreBinary: true,
	}
	err := i.interpolate(placeholder, []interface{}{builder})
	query, value := i.String(), i.Value()
	if err != nil {
		return nil, log.EventErrKv("dbr.exec.interpolate", err, kvs{
			"sql":  query,
			"args": fmt.Sprint(value),
		})
	}

	startTime := time.Now()
	defer func() {
		log.TimingKv("dbr.exec", time.Since(startTime).Nanoseconds(), kvs{
			"sql": query,
		})
	}()

	result, err := runner.Exec(query, value...)
	if err != nil {
		return result, log.EventErrKv("dbr.exec.exec", err, kvs{
			"sql": query,
		})
	}
	return result, nil
}

func query(runner runner, log EventReceiver, builder Builder, d Dialect, dest interface{}) (int, error) {
	i := interpolator{
		Buffer:       NewBuffer(),
		Dialect:      d,
		IgnoreBinary: true,
	}
	err := i.interpolate(placeholder, []interface{}{builder})
	query, value := i.String(), i.Value()
	if err != nil {
		return 0, log.EventErrKv("dbr.select.interpolate", err, kvs{
			"sql":  query,
			"args": fmt.Sprint(value),
		})
	}

	startTime := time.Now()
	defer func() {
		log.TimingKv("dbr.select", time.Since(startTime).Nanoseconds(), kvs{
			"sql": query,
		})
	}()

	rows, err := runner.Query(query, value...)
	if err != nil {
		return 0, log.EventErrKv("dbr.select.load.query", err, kvs{
			"sql": query,
		})
	}
	count, err := Load(rows, dest)
	if err != nil {
		return 0, log.EventErrKv("dbr.select.load.scan", err, kvs{
			"sql": query,
		})
	}
	return count, nil
}
<|endoftext|>


<commit_before>/**
 * (C) Copyright 2014, Deft Labs
 *
 * 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 dlshared

import(
	"fmt"
	"time"
	"sync"
	"strings"
	"encoding/json"
)

// The google cloud messaging service. This component dispatches JSON messages to
// Google via HTTP and implements exponential backoff to ensure that if there is a
// failure, everything slows down as to not overload the consuming service. This
// component wraps a consumer component. The component is configured by passing in
// the configuration path.
// 		"gcm": {
//			"postUrl": "https://android.googleapis.com/gcm/send",
//			"authKey": "WHATEVER_IT_IS_FOR_YOUR_API_ACCESS",
//			"acceptableGoogleCloudMsgFailurePercent": 10,
//			"initialGoogleCloudMsgBackoffInMs": 100,
//			"maxGoogleCloudMsgBackoffInMs": 10000,
//			"consumer": {
//				"maxGoroutines": 1000,
//				"maxWaitOnStopInMs": 30000
//          }
//		}
//
// In the example above, the configuration path would be "gcm" (passed in New function). This assumes
// that "gcm" is located as child of the root of the document.
//
type GoogleCloudMessagingSvc struct {
	Logger
	consumer *Consumer
	configPath string

	consumerChannel chan interface{}
	requestChannel chan interface{}
	responseChannel chan interface{}

	authKey string
	postUrl string

	updateStatsTicker *time.Ticker

	httpClient HttpRequestClient

	stats *GoogleCloudMsgSendStats
	sync.WaitGroup

}

type GoogleCloudMessagingMsgResponseHandler func(*GoogleCloudMsgResponse)

const(
	gcmAuthHeader = "Authorization"
	gcmAuthHeaderKeyPrefix = "key="
)

// The user must set the required fields in the message.
type GoogleCloudMsg struct {
	RegistrationIds []string `json:"registration_ids"`
	NotificationKey string `json:"notification_key,omitempty"`
	CollapseKey string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool `json:"delay_while_idle,omitempty"`
	TimeToLive int `json:"time_to_live,omitempty"`
	RestrictedPackageName string `json:"restricted_package_name,omitempty"`
	DryRun bool `json:"dry_run,omitempty"`
	Data map[string]interface{} `json:"data,omitempty"`

	// This can contain anything - usually, it is source info/data that can be used
	// when the response is processed. These items should be in the same order as
	// the registration id.
	AdditionalInfo []interface{} `json:"-"`
}

// The response object returned by google.
type GoogleCloudMsgResponse struct {
	MulticastId float64 `json:"multicast_id"`
	SuccessCount float64 `json:"success"`
	FailureCount float64 `json:"failure"`
	CanonicalIdsCount float64 `json:"canonical_ids"`
	Results []*GoogleCloudMsgResponseResult `json:"results"`
	OrigMsg *GoogleCloudMsg `json:"-"`
	Err error `json:"-"` // If there is an error caused by io or a problem with gcm.
	HttpStatusCode int `json:"-"`
}

type GoogleCloudMsgResponseResult struct {
	MessageId string `json:"message_id"`
	RegistrationId string `json:"registration_id"`
	Error string `json:"error"`
}

func NewGoogleCloudMessagingSvc(configPath string, httpClient HttpRequestClient, requestChannel, responseChannel chan interface{}) *GoogleCloudMessagingSvc {
	return &GoogleCloudMessagingSvc{	configPath: configPath,
										requestChannel: requestChannel,
										responseChannel: responseChannel,
										consumerChannel: make(chan interface{}),
										updateStatsTicker: time.NewTicker(1 * time.Second),
										httpClient: httpClient,
										stats: &GoogleCloudMsgSendStats{},
	}
}

func (self *GoogleCloudMessagingSvc) sendResponse(response *GoogleCloudMsgResponse, msgCount int, err error) {
	self.Add(1)
	defer self.Done()

	if err != nil {
		self.stats.updateFailureCount(msgCount)
		response.Err = err
	} else {
		self.stats.updateSuccessCount(msgCount)
	}

	self.responseChannel <- response
}

// This is the internal method called by the consumer to actually send the message.
func (self *GoogleCloudMessagingSvc) processMsg(msg interface{}) {

	gcmMsg := msg.(*GoogleCloudMsg)

	response := GoogleCloudMsgResponse{ OrigMsg: gcmMsg }

	msgCount := len(gcmMsg.RegistrationIds)

	var result []byte
	var err error

	fmt.Println(fmt.Sprintf("-------- auth key: %s", fmt.Sprintf(gcmAuthHeaderKeyPrefix, self.authKey)))

	response.HttpStatusCode,
	result,
	err = self.httpClient.PostJson(self.postUrl, gcmMsg, map[string]string { gcmAuthHeader:  fmt.Sprintf(gcmAuthHeaderKeyPrefix, self.authKey) })

	if err != nil {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm post - err: %v", err))

	} else if response.HttpStatusCode >= 500 {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm servers - http status error code: %d", response.HttpStatusCode))

	} else if response.HttpStatusCode == 400 {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm post - http status: 400 - problem with JSON sent"))

	} else if response.HttpStatusCode == 401 {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm post - http status: 401 - unable to authenticate - check your auth key"))

	} else if response.HttpStatusCode != 200 {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm post - http status: %d", response.HttpStatusCode))

	} else {

		if err = json.Unmarshal(result, &response); err != nil {

			self.sendResponse(&response, msgCount, NewStackError("Unable to parse gcm response - err: %v", err))

		} else {

			self.sendResponse(&response, msgCount, nil)

		}
	}
}

func (self *GoogleCloudMessagingSvc) listenForRequests() {
	self.Add(1)
	defer self.Done()
	for msg := range self.requestChannel {
		backoff := self.stats.backoffTime()
		if backoff > 0 { time.Sleep(time.Duration(backoff) * time.Millisecond) }
		self.consumerChannel <- msg
	}
}

func (self *GoogleCloudMessagingSvc) updateStats() { for now := range self.updateStatsTicker.C { self.stats.update(&now) } }

func (self *GoogleCloudMessagingSvc) Start(kernel *Kernel) error {

	self.authKey = strings.TrimSpace(kernel.Configuration.StringWithPath(self.configPath, "authKey", ""))
	self.postUrl = strings.TrimSpace(kernel.Configuration.StringWithPath(self.configPath, "postUrl", "https://android.googleapis.com/gcm/send"))

	if len(self.authKey) == 0 { return NewStackError("Unable to create GoogleCloudMessagingSvc - no \"authKey\" field in config file - path: %s", self.configPath) }

	self.stats.acceptableGcmFailurePercent = kernel.Configuration.IntWithPath(self.configPath, "acceptableGoogleCloudMsgFailurePercent", 10)
	self.stats.initialGcmBackoffInMs = kernel.Configuration.IntWithPath(self.configPath, "initialGoogleCloudMsgBackoffInMs", 100)
	self.stats.maxGcmBackoffInMs = kernel.Configuration.IntWithPath(self.configPath, "maxGoogleCloudMsgBackoffInMs", 10000)

	self.consumer = NewConsumer("GoogleCloudMessagingSvcConsumer",
								self.consumerChannel,
								self.processMsg,
								kernel.Configuration.IntWithPath(self.configPath, "consumer.maxGoroutines", 1000),
								kernel.Configuration.IntWithPath(self.configPath, "consumer.maxWaitOnStopInMs", 30000),
								kernel.Logger)

	if err := self.consumer.Start(); err != nil { return err }

	go self.updateStats()

	go self.listenForRequests()

	return nil
}

func (self *GoogleCloudMessagingSvc) Stop(kernel *Kernel) error {

	close(self.consumerChannel)

	if self.consumer != nil { if err := self.consumer.Stop(); err != nil { return err } }

	self.Wait()

	return nil
}

// The cloud message stats.
type GoogleCloudMsgSendStats struct {
	BackoffTimeInMs int
	CurrentSuccessCount int
	CurrentFailureCount int
	PreviousSuccessCount int
	PreviousFailureCount int

	acceptableGcmFailurePercent int
	initialGcmBackoffInMs int
	maxGcmBackoffInMs int
	sync.Mutex
}

func (self *GoogleCloudMsgSendStats) update(now *time.Time) {
	self.Lock()
	defer self.Unlock()

	if self.currentFailurePercent() <= self.acceptableGcmFailurePercent { self.reduceBackoff() } else { self.increaseBackoff() }

	// Update the current and previous values.
	self.updateCurrent()
}

func (self *GoogleCloudMsgSendStats) updateCurrent() {
	self.PreviousSuccessCount = self.CurrentSuccessCount
	self.PreviousFailureCount = self.CurrentFailureCount
	self.CurrentSuccessCount = 0
	self.CurrentFailureCount = 0
}

func (self *GoogleCloudMsgSendStats) reduceBackoff() {
	if self.BackoffTimeInMs == 0 { return }
	self.BackoffTimeInMs /= 2
	if self.BackoffTimeInMs <= self.initialGcmBackoffInMs { self.BackoffTimeInMs = 0 }
}

func (self *GoogleCloudMsgSendStats) increaseBackoff() {
	if self.BackoffTimeInMs == 0 { self.BackoffTimeInMs = self.initialGcmBackoffInMs
	} else { self.BackoffTimeInMs *= 2 }
	if self.BackoffTimeInMs > self.maxGcmBackoffInMs { self.BackoffTimeInMs = self.maxGcmBackoffInMs  }
}

func (self *GoogleCloudMsgSendStats) currentFailurePercent() int {
	totalMsgs := self.CurrentSuccessCount + self.CurrentFailureCount
	if totalMsgs == 0 { return 0 } else { return (self.CurrentFailureCount / totalMsgs) * 100 }
}

func (self *GoogleCloudMsgSendStats) backoffTime() int {
	self.Lock()
	defer self.Unlock()
	return self.BackoffTimeInMs
}

func (self *GoogleCloudMsgSendStats) updateFailureCount(count int) {
	self.Lock()
	defer self.Unlock()
	self.CurrentFailureCount += count
}

func (self *GoogleCloudMsgSendStats) updateSuccessCount(count int) {
	self.Lock()
	defer self.Unlock()
	self.CurrentSuccessCount += count
}

<commit_msg>fixed auth<commit_after>/**
 * (C) Copyright 2014, Deft Labs
 *
 * 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 dlshared

import(
	"fmt"
	"time"
	"sync"
	"strings"
	"encoding/json"
)

// The google cloud messaging service. This component dispatches JSON messages to
// Google via HTTP and implements exponential backoff to ensure that if there is a
// failure, everything slows down as to not overload the consuming service. This
// component wraps a consumer component. The component is configured by passing in
// the configuration path.
// 		"gcm": {
//			"postUrl": "https://android.googleapis.com/gcm/send",
//			"authKey": "WHATEVER_IT_IS_FOR_YOUR_API_ACCESS",
//			"acceptableGoogleCloudMsgFailurePercent": 10,
//			"initialGoogleCloudMsgBackoffInMs": 100,
//			"maxGoogleCloudMsgBackoffInMs": 10000,
//			"consumer": {
//				"maxGoroutines": 1000,
//				"maxWaitOnStopInMs": 30000
//          }
//		}
//
// In the example above, the configuration path would be "gcm" (passed in New function). This assumes
// that "gcm" is located as child of the root of the document.
//
type GoogleCloudMessagingSvc struct {
	Logger
	consumer *Consumer
	configPath string

	consumerChannel chan interface{}
	requestChannel chan interface{}
	responseChannel chan interface{}

	authKey string
	postUrl string

	updateStatsTicker *time.Ticker

	httpClient HttpRequestClient

	stats *GoogleCloudMsgSendStats
	sync.WaitGroup

}

type GoogleCloudMessagingMsgResponseHandler func(*GoogleCloudMsgResponse)

const(
	gcmAuthHeader = "Authorization"
	gcmAuthHeaderKeyPattern= "key=%s"
)

// The user must set the required fields in the message.
type GoogleCloudMsg struct {
	RegistrationIds []string `json:"registration_ids"`
	NotificationKey string `json:"notification_key,omitempty"`
	CollapseKey string `json:"collapse_key,omitempty"`
	DelayWhileIdle bool `json:"delay_while_idle,omitempty"`
	TimeToLive int `json:"time_to_live,omitempty"`
	RestrictedPackageName string `json:"restricted_package_name,omitempty"`
	DryRun bool `json:"dry_run,omitempty"`
	Data map[string]interface{} `json:"data,omitempty"`

	// This can contain anything - usually, it is source info/data that can be used
	// when the response is processed. These items should be in the same order as
	// the registration id.
	AdditionalInfo []interface{} `json:"-"`
}

// The response object returned by google.
type GoogleCloudMsgResponse struct {
	MulticastId float64 `json:"multicast_id"`
	SuccessCount float64 `json:"success"`
	FailureCount float64 `json:"failure"`
	CanonicalIdsCount float64 `json:"canonical_ids"`
	Results []*GoogleCloudMsgResponseResult `json:"results"`
	OrigMsg *GoogleCloudMsg `json:"-"`
	Err error `json:"-"` // If there is an error caused by io or a problem with gcm.
	HttpStatusCode int `json:"-"`
}

type GoogleCloudMsgResponseResult struct {
	MessageId string `json:"message_id"`
	RegistrationId string `json:"registration_id"`
	Error string `json:"error"`
}

func NewGoogleCloudMessagingSvc(configPath string, httpClient HttpRequestClient, requestChannel, responseChannel chan interface{}) *GoogleCloudMessagingSvc {
	return &GoogleCloudMessagingSvc{	configPath: configPath,
										requestChannel: requestChannel,
										responseChannel: responseChannel,
										consumerChannel: make(chan interface{}),
										updateStatsTicker: time.NewTicker(1 * time.Second),
										httpClient: httpClient,
										stats: &GoogleCloudMsgSendStats{},
	}
}

func (self *GoogleCloudMessagingSvc) sendResponse(response *GoogleCloudMsgResponse, msgCount int, err error) {
	self.Add(1)
	defer self.Done()

	if err != nil {
		self.stats.updateFailureCount(msgCount)
		response.Err = err
	} else {
		self.stats.updateSuccessCount(msgCount)
	}

	self.responseChannel <- response
}

// This is the internal method called by the consumer to actually send the message.
func (self *GoogleCloudMessagingSvc) processMsg(msg interface{}) {

	gcmMsg := msg.(*GoogleCloudMsg)

	response := GoogleCloudMsgResponse{ OrigMsg: gcmMsg }

	msgCount := len(gcmMsg.RegistrationIds)

	var result []byte
	var err error

	response.HttpStatusCode,
	result,
	err = self.httpClient.PostJson(self.postUrl, gcmMsg, map[string]string { gcmAuthHeader:  fmt.Sprintf(gcmAuthHeaderKeyPattern, self.authKey) })

	if err != nil {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm post - err: %v", err))

	} else if response.HttpStatusCode >= 500 {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm servers - http status error code: %d", response.HttpStatusCode))

	} else if response.HttpStatusCode == 400 {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm post - http status: 400 - problem with JSON sent"))

	} else if response.HttpStatusCode == 401 {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm post - http status: 401 - unable to authenticate - check your auth key"))

	} else if response.HttpStatusCode != 200 {

		self.sendResponse(&response, msgCount, NewStackError("Problem with gcm post - http status: %d", response.HttpStatusCode))

	} else {

		if err = json.Unmarshal(result, &response); err != nil {

			self.sendResponse(&response, msgCount, NewStackError("Unable to parse gcm response - err: %v", err))

		} else {

			self.sendResponse(&response, msgCount, nil)

		}
	}
}

func (self *GoogleCloudMessagingSvc) listenForRequests() {
	self.Add(1)
	defer self.Done()
	for msg := range self.requestChannel {
		backoff := self.stats.backoffTime()
		if backoff > 0 { time.Sleep(time.Duration(backoff) * time.Millisecond) }
		self.consumerChannel <- msg
	}
}

func (self *GoogleCloudMessagingSvc) updateStats() { for now := range self.updateStatsTicker.C { self.stats.update(&now) } }

func (self *GoogleCloudMessagingSvc) Start(kernel *Kernel) error {

	self.authKey = strings.TrimSpace(kernel.Configuration.StringWithPath(self.configPath, "authKey", ""))
	self.postUrl = strings.TrimSpace(kernel.Configuration.StringWithPath(self.configPath, "postUrl", "https://android.googleapis.com/gcm/send"))

	if len(self.authKey) == 0 { return NewStackError("Unable to create GoogleCloudMessagingSvc - no \"authKey\" field in config file - path: %s", self.configPath) }

	self.stats.acceptableGcmFailurePercent = kernel.Configuration.IntWithPath(self.configPath, "acceptableGoogleCloudMsgFailurePercent", 10)
	self.stats.initialGcmBackoffInMs = kernel.Configuration.IntWithPath(self.configPath, "initialGoogleCloudMsgBackoffInMs", 100)
	self.stats.maxGcmBackoffInMs = kernel.Configuration.IntWithPath(self.configPath, "maxGoogleCloudMsgBackoffInMs", 10000)

	self.consumer = NewConsumer("GoogleCloudMessagingSvcConsumer",
								self.consumerChannel,
								self.processMsg,
								kernel.Configuration.IntWithPath(self.configPath, "consumer.maxGoroutines", 1000),
								kernel.Configuration.IntWithPath(self.configPath, "consumer.maxWaitOnStopInMs", 30000),
								kernel.Logger)

	if err := self.consumer.Start(); err != nil { return err }

	go self.updateStats()

	go self.listenForRequests()

	return nil
}

func (self *GoogleCloudMessagingSvc) Stop(kernel *Kernel) error {

	close(self.consumerChannel)

	if self.consumer != nil { if err := self.consumer.Stop(); err != nil { return err } }

	self.Wait()

	return nil
}

// The cloud message stats.
type GoogleCloudMsgSendStats struct {
	BackoffTimeInMs int
	CurrentSuccessCount int
	CurrentFailureCount int
	PreviousSuccessCount int
	PreviousFailureCount int

	acceptableGcmFailurePercent int
	initialGcmBackoffInMs int
	maxGcmBackoffInMs int
	sync.Mutex
}

func (self *GoogleCloudMsgSendStats) update(now *time.Time) {
	self.Lock()
	defer self.Unlock()

	if self.currentFailurePercent() <= self.acceptableGcmFailurePercent { self.reduceBackoff() } else { self.increaseBackoff() }

	// Update the current and previous values.
	self.updateCurrent()
}

func (self *GoogleCloudMsgSendStats) updateCurrent() {
	self.PreviousSuccessCount = self.CurrentSuccessCount
	self.PreviousFailureCount = self.CurrentFailureCount
	self.CurrentSuccessCount = 0
	self.CurrentFailureCount = 0
}

func (self *GoogleCloudMsgSendStats) reduceBackoff() {
	if self.BackoffTimeInMs == 0 { return }
	self.BackoffTimeInMs /= 2
	if self.BackoffTimeInMs <= self.initialGcmBackoffInMs { self.BackoffTimeInMs = 0 }
}

func (self *GoogleCloudMsgSendStats) increaseBackoff() {
	if self.BackoffTimeInMs == 0 { self.BackoffTimeInMs = self.initialGcmBackoffInMs
	} else { self.BackoffTimeInMs *= 2 }
	if self.BackoffTimeInMs > self.maxGcmBackoffInMs { self.BackoffTimeInMs = self.maxGcmBackoffInMs  }
}

func (self *GoogleCloudMsgSendStats) currentFailurePercent() int {
	totalMsgs := self.CurrentSuccessCount + self.CurrentFailureCount
	if totalMsgs == 0 { return 0 } else { return (self.CurrentFailureCount / totalMsgs) * 100 }
}

func (self *GoogleCloudMsgSendStats) backoffTime() int {
	self.Lock()
	defer self.Unlock()
	return self.BackoffTimeInMs
}

func (self *GoogleCloudMsgSendStats) updateFailureCount(count int) {
	self.Lock()
	defer self.Unlock()
	self.CurrentFailureCount += count
}

func (self *GoogleCloudMsgSendStats) updateSuccessCount(count int) {
	self.Lock()
	defer self.Unlock()
	self.CurrentSuccessCount += count
}

<|endoftext|>


<commit_before>package egoscale

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

// DNSDomain represents a domain
type DNSDomain struct {
	ID             int64  `json:"id"`
	AccountID      int64  `json:"account_id,omitempty"`
	UserID         int64  `json:"user_id,omitempty"`
	RegistrantID   int64  `json:"registrant_id,omitempty"`
	Name           string `json:"name"`
	UnicodeName    string `json:"unicode_name"`
	Token          string `json:"token"`
	State          string `json:"state"`
	Language       string `json:"language,omitempty"`
	Lockable       bool   `json:"lockable"`
	AutoRenew      bool   `json:"auto_renew"`
	WhoisProtected bool   `json:"whois_protected"`
	RecordCount    int64  `json:"record_count"`
	ServiceCount   int64  `json:"service_count"`
	ExpiresOn      string `json:"expires_on,omitempty"`
	CreatedAt      string `json:"created_at"`
	UpdatedAt      string `json:"updated_at"`
}

// DNSDomainResponse represents a domain creation response
type DNSDomainResponse struct {
	Domain *DNSDomain `json:"domain"`
}

// DNSRecord represents a DNS record
type DNSRecord struct {
	ID         int64  `json:"id,omitempty"`
	DomainID   int64  `json:"domain_id,omitempty"`
	Name       string `json:"name"`
	TTL        int    `json:"ttl,omitempty"`
	CreatedAt  string `json:"created_at,omitempty"`
	UpdatedAt  string `json:"updated_at,omitempty"`
	Content    string `json:"content"`
	RecordType string `json:"record_type"`
	Prio       int    `json:"prio,omitempty"`
}

// DNSRecordResponse represents the creation of a DNS record
type DNSRecordResponse struct {
	Record DNSRecord `json:"record"`
}

// UpdateDNSRecord represents a DNS record
type UpdateDNSRecord struct {
	ID         int64  `json:"id,omitempty"`
	DomainID   int64  `json:"domain_id,omitempty"`
	Name       string `json:"name,omitempty"`
	TTL        int    `json:"ttl,omitempty"`
	CreatedAt  string `json:"created_at,omitempty"`
	UpdatedAt  string `json:"updated_at,omitempty"`
	Content    string `json:"content,omitempty"`
	RecordType string `json:"record_type,omitempty"`
	Prio       int    `json:"prio,omitempty"`
}

// UpdateDNSRecordResponse represents the creation of a DNS record
type UpdateDNSRecordResponse struct {
	Record UpdateDNSRecord `json:"record"`
}

// DNSErrorResponse represents an error in the API
type DNSErrorResponse struct {
	Message string              `json:"message,omitempty"`
	Errors  map[string][]string `json:"errors"`
}

// Record represent record type
type Record int

//go:generate stringer -type=Record
const (
	// A record type
	A Record = iota
	// AAAA record type
	AAAA
	// ALIAS record type
	ALIAS
	// CNAME record type
	CNAME
	// HINFO record type
	HINFO
	// MX record type
	MX
	// NAPTR record type
	NAPTR
	// NS record type
	NS
	// POOL record type
	POOL
	// SPF record type
	SPF
	// SRV record type
	SRV
	// SSHFP record type
	SSHFP
	// TXT record type
	TXT
	// URL record type
	URL
)

// Error formats the DNSerror into a string
func (req *DNSErrorResponse) Error() string {
	if len(req.Errors) > 0 {
		errs := []string{}
		for name, ss := range req.Errors {
			if len(ss) > 0 {
				errs = append(errs, fmt.Sprintf("%s: %s", name, strings.Join(ss, ", ")))
			}
		}
		return fmt.Sprintf("dns error: %s (%s)", req.Message, strings.Join(errs, "; "))
	}
	return fmt.Sprintf("dns error: %s", req.Message)
}

// CreateDomain creates a DNS domain
func (client *Client) CreateDomain(ctx context.Context, name string) (*DNSDomain, error) {
	m, err := json.Marshal(DNSDomainResponse{
		Domain: &DNSDomain{
			Name: name,
		},
	})
	if err != nil {
		return nil, err
	}

	resp, err := client.dnsRequest(ctx, "/v1/domains", nil, string(m), "POST")
	if err != nil {
		return nil, err
	}

	var d *DNSDomainResponse
	if err := json.Unmarshal(resp, &d); err != nil {
		return nil, err
	}

	return d.Domain, nil
}

// GetDomain gets a DNS domain
func (client *Client) GetDomain(ctx context.Context, name string) (*DNSDomain, error) {
	resp, err := client.dnsRequest(ctx, "/v1/domains/"+name, nil, "", "GET")
	if err != nil {
		return nil, err
	}

	var d *DNSDomainResponse
	if err := json.Unmarshal(resp, &d); err != nil {
		return nil, err
	}

	return d.Domain, nil
}

// GetDomains gets DNS domains
func (client *Client) GetDomains(ctx context.Context) ([]DNSDomain, error) {
	resp, err := client.dnsRequest(ctx, "/v1/domains", nil, "", "GET")
	if err != nil {
		return nil, err
	}

	var d []DNSDomainResponse
	if err := json.Unmarshal(resp, &d); err != nil {
		return nil, err
	}

	domains := make([]DNSDomain, len(d))
	for i := range d {
		domains[i] = *d[i].Domain
	}
	return domains, nil
}

// DeleteDomain delets a DNS domain
func (client *Client) DeleteDomain(ctx context.Context, name string) error {
	_, err := client.dnsRequest(ctx, "/v1/domains/"+name, nil, "", "DELETE")
	return err
}

// GetRecord returns a DNS record
func (client *Client) GetRecord(ctx context.Context, domain string, recordID int64) (*DNSRecord, error) {
	id := strconv.FormatInt(recordID, 10)
	resp, err := client.dnsRequest(ctx, "/v1/domains/"+domain+"/records/"+id, nil, "", "GET")
	if err != nil {
		return nil, err
	}

	var r DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	return &(r.Record), nil
}

// GetRecords returns the DNS records
func (client *Client) GetRecords(ctx context.Context, domain string) ([]DNSRecord, error) {
	resp, err := client.dnsRequest(ctx, "/v1/domains/"+domain+"/records", nil, "", "GET")
	if err != nil {
		return nil, err
	}

	var r []DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	records := make([]DNSRecord, 0, len(r))
	for _, rec := range r {
		records = append(records, rec.Record)
	}

	return records, nil
}

// GetRecordsWithFilters returns the DNS records (filters can be empty)
func (client *Client) GetRecordsWithFilters(ctx context.Context, domain, name, recordType string) ([]DNSRecord, error) {

	filters := url.Values{}
	if name != "" {
		filters.Add("name", name)
	}
	if recordType != "" {
		filters.Add("record_type", recordType)
	}

	resp, err := client.dnsRequest(ctx, "/v1/domains/"+domain+"/records", filters, "", "GET")
	if err != nil {
		return nil, err
	}

	var r []DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	records := make([]DNSRecord, 0, len(r))
	for _, rec := range r {
		records = append(records, rec.Record)
	}

	return records, nil
}

// CreateRecord creates a DNS record
func (client *Client) CreateRecord(ctx context.Context, name string, rec DNSRecord) (*DNSRecord, error) {
	body, err := json.Marshal(DNSRecordResponse{
		Record: rec,
	})
	if err != nil {
		return nil, err
	}

	resp, err := client.dnsRequest(ctx, "/v1/domains/"+name+"/records", nil, string(body), "POST")
	if err != nil {
		return nil, err
	}

	var r DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	return &(r.Record), nil
}

// UpdateRecord updates a DNS record
func (client *Client) UpdateRecord(ctx context.Context, name string, rec UpdateDNSRecord) (*DNSRecord, error) {
	body, err := json.Marshal(UpdateDNSRecordResponse{
		Record: rec,
	})
	if err != nil {
		return nil, err
	}

	id := strconv.FormatInt(rec.ID, 10)
	resp, err := client.dnsRequest(ctx, "/v1/domains/"+name+"/records/"+id, nil, string(body), "PUT")
	if err != nil {
		return nil, err
	}

	var r DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	return &(r.Record), nil
}

// DeleteRecord deletes a record
func (client *Client) DeleteRecord(ctx context.Context, name string, recordID int64) error {
	id := strconv.FormatInt(recordID, 10)
	_, err := client.dnsRequest(ctx, "/v1/domains/"+name+"/records/"+id, nil, "", "DELETE")

	return err
}

func (client *Client) dnsRequest(ctx context.Context, uri string, urlValues url.Values, params, method string) (json.RawMessage, error) {
	rawURL := client.Endpoint + uri
	url, err := url.Parse(rawURL)
	if err != nil {
		return nil, err
	}

	q := url.Query()
	for k, vs := range urlValues {
		for _, v := range vs {
			q.Add(k, v)
		}
	}
	url.RawQuery = q.Encode()

	req, err := http.NewRequest(method, url.String(), strings.NewReader(params))
	if err != nil {
		return nil, err
	}

	var hdr = make(http.Header)
	hdr.Add("X-DNS-TOKEN", client.APIKey+":"+client.apiSecret)
	hdr.Add("User-Agent", fmt.Sprintf("exoscale/egoscale (%v)", Version))
	hdr.Add("Accept", "application/json")
	if params != "" {
		hdr.Add("Content-Type", "application/json")
	}
	req.Header = hdr

	resp, err := client.HTTPClient.Do(req.WithContext(ctx))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close() // nolint: errcheck

	contentType := resp.Header.Get("content-type")
	if !strings.Contains(contentType, "application/json") {
		return nil, fmt.Errorf(`response content-type expected to be "application/json", got %q`, contentType)
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 400 {
		e := new(DNSErrorResponse)
		if err := json.Unmarshal(b, e); err != nil {
			return nil, err
		}
		return nil, e
	}

	return b, nil
}
<commit_msg>dns: user_id is not sent back (#394)<commit_after>package egoscale

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

// DNSDomain represents a domain
type DNSDomain struct {
	ID             int64  `json:"id"`
	Name           string `json:"name"`
	UnicodeName    string `json:"unicode_name"`
	Token          string `json:"token"`
	State          string `json:"state"`
	Language       string `json:"language,omitempty"`
	Lockable       bool   `json:"lockable"`
	AutoRenew      bool   `json:"auto_renew"`
	WhoisProtected bool   `json:"whois_protected"`
	RecordCount    int64  `json:"record_count"`
	ServiceCount   int64  `json:"service_count"`
	ExpiresOn      string `json:"expires_on,omitempty"`
	CreatedAt      string `json:"created_at"`
	UpdatedAt      string `json:"updated_at"`
}

// DNSDomainResponse represents a domain creation response
type DNSDomainResponse struct {
	Domain *DNSDomain `json:"domain"`
}

// DNSRecord represents a DNS record
type DNSRecord struct {
	ID         int64  `json:"id,omitempty"`
	DomainID   int64  `json:"domain_id,omitempty"`
	Name       string `json:"name"`
	TTL        int    `json:"ttl,omitempty"`
	CreatedAt  string `json:"created_at,omitempty"`
	UpdatedAt  string `json:"updated_at,omitempty"`
	Content    string `json:"content"`
	RecordType string `json:"record_type"`
	Prio       int    `json:"prio,omitempty"`
}

// DNSRecordResponse represents the creation of a DNS record
type DNSRecordResponse struct {
	Record DNSRecord `json:"record"`
}

// UpdateDNSRecord represents a DNS record
type UpdateDNSRecord struct {
	ID         int64  `json:"id,omitempty"`
	DomainID   int64  `json:"domain_id,omitempty"`
	Name       string `json:"name,omitempty"`
	TTL        int    `json:"ttl,omitempty"`
	CreatedAt  string `json:"created_at,omitempty"`
	UpdatedAt  string `json:"updated_at,omitempty"`
	Content    string `json:"content,omitempty"`
	RecordType string `json:"record_type,omitempty"`
	Prio       int    `json:"prio,omitempty"`
}

// UpdateDNSRecordResponse represents the creation of a DNS record
type UpdateDNSRecordResponse struct {
	Record UpdateDNSRecord `json:"record"`
}

// DNSErrorResponse represents an error in the API
type DNSErrorResponse struct {
	Message string              `json:"message,omitempty"`
	Errors  map[string][]string `json:"errors"`
}

// Record represent record type
type Record int

//go:generate stringer -type=Record
const (
	// A record type
	A Record = iota
	// AAAA record type
	AAAA
	// ALIAS record type
	ALIAS
	// CNAME record type
	CNAME
	// HINFO record type
	HINFO
	// MX record type
	MX
	// NAPTR record type
	NAPTR
	// NS record type
	NS
	// POOL record type
	POOL
	// SPF record type
	SPF
	// SRV record type
	SRV
	// SSHFP record type
	SSHFP
	// TXT record type
	TXT
	// URL record type
	URL
)

// Error formats the DNSerror into a string
func (req *DNSErrorResponse) Error() string {
	if len(req.Errors) > 0 {
		errs := []string{}
		for name, ss := range req.Errors {
			if len(ss) > 0 {
				errs = append(errs, fmt.Sprintf("%s: %s", name, strings.Join(ss, ", ")))
			}
		}
		return fmt.Sprintf("dns error: %s (%s)", req.Message, strings.Join(errs, "; "))
	}
	return fmt.Sprintf("dns error: %s", req.Message)
}

// CreateDomain creates a DNS domain
func (client *Client) CreateDomain(ctx context.Context, name string) (*DNSDomain, error) {
	m, err := json.Marshal(DNSDomainResponse{
		Domain: &DNSDomain{
			Name: name,
		},
	})
	if err != nil {
		return nil, err
	}

	resp, err := client.dnsRequest(ctx, "/v1/domains", nil, string(m), "POST")
	if err != nil {
		return nil, err
	}

	var d *DNSDomainResponse
	if err := json.Unmarshal(resp, &d); err != nil {
		return nil, err
	}

	return d.Domain, nil
}

// GetDomain gets a DNS domain
func (client *Client) GetDomain(ctx context.Context, name string) (*DNSDomain, error) {
	resp, err := client.dnsRequest(ctx, "/v1/domains/"+name, nil, "", "GET")
	if err != nil {
		return nil, err
	}

	var d *DNSDomainResponse
	if err := json.Unmarshal(resp, &d); err != nil {
		return nil, err
	}

	return d.Domain, nil
}

// GetDomains gets DNS domains
func (client *Client) GetDomains(ctx context.Context) ([]DNSDomain, error) {
	resp, err := client.dnsRequest(ctx, "/v1/domains", nil, "", "GET")
	if err != nil {
		return nil, err
	}

	var d []DNSDomainResponse
	if err := json.Unmarshal(resp, &d); err != nil {
		return nil, err
	}

	domains := make([]DNSDomain, len(d))
	for i := range d {
		domains[i] = *d[i].Domain
	}
	return domains, nil
}

// DeleteDomain delets a DNS domain
func (client *Client) DeleteDomain(ctx context.Context, name string) error {
	_, err := client.dnsRequest(ctx, "/v1/domains/"+name, nil, "", "DELETE")
	return err
}

// GetRecord returns a DNS record
func (client *Client) GetRecord(ctx context.Context, domain string, recordID int64) (*DNSRecord, error) {
	id := strconv.FormatInt(recordID, 10)
	resp, err := client.dnsRequest(ctx, "/v1/domains/"+domain+"/records/"+id, nil, "", "GET")
	if err != nil {
		return nil, err
	}

	var r DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	return &(r.Record), nil
}

// GetRecords returns the DNS records
func (client *Client) GetRecords(ctx context.Context, domain string) ([]DNSRecord, error) {
	resp, err := client.dnsRequest(ctx, "/v1/domains/"+domain+"/records", nil, "", "GET")
	if err != nil {
		return nil, err
	}

	var r []DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	records := make([]DNSRecord, 0, len(r))
	for _, rec := range r {
		records = append(records, rec.Record)
	}

	return records, nil
}

// GetRecordsWithFilters returns the DNS records (filters can be empty)
func (client *Client) GetRecordsWithFilters(ctx context.Context, domain, name, recordType string) ([]DNSRecord, error) {

	filters := url.Values{}
	if name != "" {
		filters.Add("name", name)
	}
	if recordType != "" {
		filters.Add("record_type", recordType)
	}

	resp, err := client.dnsRequest(ctx, "/v1/domains/"+domain+"/records", filters, "", "GET")
	if err != nil {
		return nil, err
	}

	var r []DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	records := make([]DNSRecord, 0, len(r))
	for _, rec := range r {
		records = append(records, rec.Record)
	}

	return records, nil
}

// CreateRecord creates a DNS record
func (client *Client) CreateRecord(ctx context.Context, name string, rec DNSRecord) (*DNSRecord, error) {
	body, err := json.Marshal(DNSRecordResponse{
		Record: rec,
	})
	if err != nil {
		return nil, err
	}

	resp, err := client.dnsRequest(ctx, "/v1/domains/"+name+"/records", nil, string(body), "POST")
	if err != nil {
		return nil, err
	}

	var r DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	return &(r.Record), nil
}

// UpdateRecord updates a DNS record
func (client *Client) UpdateRecord(ctx context.Context, name string, rec UpdateDNSRecord) (*DNSRecord, error) {
	body, err := json.Marshal(UpdateDNSRecordResponse{
		Record: rec,
	})
	if err != nil {
		return nil, err
	}

	id := strconv.FormatInt(rec.ID, 10)
	resp, err := client.dnsRequest(ctx, "/v1/domains/"+name+"/records/"+id, nil, string(body), "PUT")
	if err != nil {
		return nil, err
	}

	var r DNSRecordResponse
	if err = json.Unmarshal(resp, &r); err != nil {
		return nil, err
	}

	return &(r.Record), nil
}

// DeleteRecord deletes a record
func (client *Client) DeleteRecord(ctx context.Context, name string, recordID int64) error {
	id := strconv.FormatInt(recordID, 10)
	_, err := client.dnsRequest(ctx, "/v1/domains/"+name+"/records/"+id, nil, "", "DELETE")

	return err
}

func (client *Client) dnsRequest(ctx context.Context, uri string, urlValues url.Values, params, method string) (json.RawMessage, error) {
	rawURL := client.Endpoint + uri
	url, err := url.Parse(rawURL)
	if err != nil {
		return nil, err
	}

	q := url.Query()
	for k, vs := range urlValues {
		for _, v := range vs {
			q.Add(k, v)
		}
	}
	url.RawQuery = q.Encode()

	req, err := http.NewRequest(method, url.String(), strings.NewReader(params))
	if err != nil {
		return nil, err
	}

	var hdr = make(http.Header)
	hdr.Add("X-DNS-TOKEN", client.APIKey+":"+client.apiSecret)
	hdr.Add("User-Agent", fmt.Sprintf("exoscale/egoscale (%v)", Version))
	hdr.Add("Accept", "application/json")
	if params != "" {
		hdr.Add("Content-Type", "application/json")
	}
	req.Header = hdr

	resp, err := client.HTTPClient.Do(req.WithContext(ctx))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close() // nolint: errcheck

	contentType := resp.Header.Get("content-type")
	if !strings.Contains(contentType, "application/json") {
		return nil, fmt.Errorf(`response content-type expected to be "application/json", got %q`, contentType)
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 400 {
		e := new(DNSErrorResponse)
		if err := json.Unmarshal(b, e); err != nil {
			return nil, err
		}
		return nil, e
	}

	return b, nil
}
<|endoftext|>


<commit_before>/*
Package junos provides automation for Junos (Juniper Networks) devices, as
well as interaction with Junos Space.

Establishing A Session

To connect to a Junos device, the process is fairly straightforward.

    jnpr := junos.NewSession(host, user, password)
    defer jnpr.Close()

Viewing The Configuration

To View the entire configuration, use the keyword "full" for the second
argument. If anything else outside of "full" is specified, it will return
the configuration of that section only. So "security" would return everything
under the "security" stanza.

    // Output format can be "text" or "xml"
    config, err := jnpr.GetConfig("full", "text")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(config)

    // Viewing only a certain part of the configuration
    routing, err := jnpr.GetConfig("routing-instances", "text")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(routing)

Compare Rollback Configurations

If you want to view the difference between the current configuration and a rollback
one, then you can use the ConfigDiff() function.

    diff, err := jnpr.ConfigDiff(3)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(diff)

This will output exactly how it does on the CLI when you "| compare."

Rolling Back to a Previous State

You can also rollback to a previous state, or the "rescue" configuration by using
the RollbackConfig() function:

    err := jnpr.RollbackConfig(3)
    if err != nil {
        fmt.Println(err)
    }

    // Create a rescue config from the active configuration.
    jnpr.Rescue("save")

    // You can also delete a rescue config.
    jnpr.Rescue("delete")

    // Rollback to the "rescue" configuration.
    err := jnpr.RollbackConfig("rescue")
    if err != nil {
        fmt.Println(err)
    }

Device Configuration

When configuring a device, it is good practice to lock the configuration database,
load the config, commit the configuration, and then unlock the configuration database.

You can do this with the following functions:

    Lock(), Commit(), Unlock()

There are multiple ways to commit a configuration as well:

    // Commit the configuration as normal
    Commit()

    // Check the configuration for any syntax errors (NOTE: you must still issue a Commit())
    CommitCheck()

    // Commit at a later time, i.e. 4:30 PM
    CommitAt("16:30:00")

    // Rollback configuration if a Commit() is not issued within the given <minutes>.
    CommitConfirm(15)

You can configure the Junos device by uploading a local file, or pulling from an
FTP/HTTP server. The LoadConfig() function takes three arguments:

    filename or URL, format, and commit-on-load

If you specify a URL, it must be in the following format:

    ftp://<username>:<password>@hostname/pathname/file-name
    http://<username>:<password>@hostname/pathname/file-name

    Note: The default value for the FTP path variable is the user’s home directory. Thus,
    by default the file path to the configuration file is relative to the user directory.
    To specify an absolute path when using FTP, start the path with the characters %2F;
    for example: ftp://username:password@hostname/%2Fpath/filename.

The format of the commands within the file must be one of the following types:

    set
    // system name-server 1.1.1.1

    text
    // system {
    //     name-server 1.1.1.1;
    // }

    xml
    // <system>
    //     <name-server>
    //         <name>1.1.1.1</name>
    //     </name-server>
    // </system>

If the third option is "true" then after the configuration is loaded, a commit
will be issued. If set to "false," you will have to commit the configuration
using the Commit() function.

    jnpr.Lock()
    err := jnpr.LoadConfig("path-to-file.txt", "set", true)
    if err != nil {
        fmt.Println(err)
    }
    jnpr.Unlock()

You don't have to use Lock() and Unlock() if you wish, but if by chance someone
else tries to edit the device configuration at the same time, there can be conflics
and most likely an error will be returned.

Running Commands

You can run operational mode commands such as "show" and "request" by using the
Command() function. Output formats can be "text" or "xml."

    // Results returned in text format
    output, err := jnpr.Command("show chassis hardware", "text")

    // Results returned in XML format
    output, err := jnpr.Command("show chassis hardware", "xml")

Viewing Platform and Software Information

When you call the Facts() function, it prints out the platform and software information:

    jnpr.Facts()

    // Returns output similar to the following
    node0
    --------------------------------------------------------------------------
    Hostname: firewall-1
    Model: SRX240H2
    Version: 12.1X47-D10.4

    node1
    --------------------------------------------------------------------------
    Hostname: firewall-1
    Model: SRX240H2
    Version: 12.1X47-D10.4

Connecting and Viewing Device Information from Junos Space

Here's an example of how to connect to a Junos Space server, and get information about
the devices it manages.

    // Establish a connection to a Junos Space server.
    space, err := junos.NewServer("space.company.com", "admin", "juniper123")
    if err != nil {
        fmt.Println(err)
    }

    // Get the list of devices.
    d, err := space.Devices()
    if err != nil {
        fmt.Println(err)
    }

    // Iterate over our device list and display some information about them.
    for _, device := range d.Devices {
        fmt.Printf("Name: %s, IP Address: %s, Platform: %s\n", device.Name, device.IP, device.Platform)
    }

How to add and remove devices:

    // Add a device to Junos Space.
    err = space.AddDevice("sdubs-fw", "admin", "juniper123")
    if err != nil {
        fmt.Println(err)
    }

    // Remove a device from Junos Space...given it's device ID.
    err = space.RemoveDevice(11138405)
    if err != nil {
        fmt.Println(err)
    }

    // Here's a good way to loop through all devices, and find the one you want to delete:
    d, err := space.Devices()
    if err != nil {
        fmt.Println(err)
    }

    for _, device := range d.Devices {
        if device.Name == "sdubs-fw" {
            err = space.RemoveDevice(device.ID)
            if err != nil {
                fmt.Println(err)
            }

            fmt.Printf("Deleted device: %s\n", device.Name)
        }
    }
*/
package junos
<commit_msg>Updated examples<commit_after>/*
Package junos provides automation for Junos (Juniper Networks) devices, as
well as interaction with Junos Space.

Establishing A Session

To connect to a Junos device, the process is fairly straightforward.

    jnpr := junos.NewSession(host, user, password)
    defer jnpr.Close()

Viewing The Configuration

To View the entire configuration, use the keyword "full" for the second
argument. If anything else outside of "full" is specified, it will return
the configuration of that section only. So "security" would return everything
under the "security" stanza.

    // Output format can be "text" or "xml"
    config, err := jnpr.GetConfig("full", "text")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(config)

    // Viewing only a certain part of the configuration
    routing, err := jnpr.GetConfig("routing-instances", "text")
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(routing)

Compare Rollback Configurations

If you want to view the difference between the current configuration and a rollback
one, then you can use the ConfigDiff() function.

    diff, err := jnpr.ConfigDiff(3)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(diff)

This will output exactly how it does on the CLI when you "| compare."

Rolling Back to a Previous State

You can also rollback to a previous state, or the "rescue" configuration by using
the RollbackConfig() function:

    err := jnpr.RollbackConfig(3)
    if err != nil {
        fmt.Println(err)
    }

    // Create a rescue config from the active configuration.
    jnpr.Rescue("save")

    // You can also delete a rescue config.
    jnpr.Rescue("delete")

    // Rollback to the "rescue" configuration.
    err := jnpr.RollbackConfig("rescue")
    if err != nil {
        fmt.Println(err)
    }

Device Configuration

When configuring a device, it is good practice to lock the configuration database,
load the config, commit the configuration, and then unlock the configuration database.

You can do this with the following functions:

    Lock(), Commit(), Unlock()

There are multiple ways to commit a configuration as well:

    // Commit the configuration as normal
    Commit()

    // Check the configuration for any syntax errors (NOTE: you must still issue a Commit())
    CommitCheck()

    // Commit at a later time, i.e. 4:30 PM
    CommitAt("16:30:00")

    // Rollback configuration if a Commit() is not issued within the given <minutes>.
    CommitConfirm(15)

You can configure the Junos device by uploading a local file, or pulling from an
FTP/HTTP server. The LoadConfig() function takes three arguments:

    filename or URL, format, and commit-on-load

If you specify a URL, it must be in the following format:

    ftp://<username>:<password>@hostname/pathname/file-name
    http://<username>:<password>@hostname/pathname/file-name

    Note: The default value for the FTP path variable is the user’s home directory. Thus,
    by default the file path to the configuration file is relative to the user directory.
    To specify an absolute path when using FTP, start the path with the characters %2F;
    for example: ftp://username:password@hostname/%2Fpath/filename.

The format of the commands within the file must be one of the following types:

    set
    // system name-server 1.1.1.1

    text
    // system {
    //     name-server 1.1.1.1;
    // }

    xml
    // <system>
    //     <name-server>
    //         <name>1.1.1.1</name>
    //     </name-server>
    // </system>

If the third option is "true" then after the configuration is loaded, a commit
will be issued. If set to "false," you will have to commit the configuration
using the Commit() function.

    jnpr.Lock()
    err := jnpr.LoadConfig("path-to-file.txt", "set", true)
    if err != nil {
        fmt.Println(err)
    }
    jnpr.Unlock()

You don't have to use Lock() and Unlock() if you wish, but if by chance someone
else tries to edit the device configuration at the same time, there can be conflics
and most likely an error will be returned.

Running Commands

You can run operational mode commands such as "show" and "request" by using the
Command() function. Output formats can be "text" or "xml."

    // Results returned in text format
    output, err := jnpr.Command("show chassis hardware", "text")

    // Results returned in XML format
    output, err := jnpr.Command("show chassis hardware", "xml")

Viewing Platform and Software Information

When you call the Facts() function, it prints out the platform and software information:

    jnpr.Facts()

    // Returns output similar to the following
    node0
    --------------------------------------------------------------------------
    Hostname: firewall-1
    Model: SRX240H2
    Version: 12.1X47-D10.4

    node1
    --------------------------------------------------------------------------
    Hostname: firewall-1
    Model: SRX240H2
    Version: 12.1X47-D10.4

Connecting and Viewing Device Information from Junos Space

Here's an example of how to connect to a Junos Space server, and get information about
the devices it manages.

    // Establish a connection to a Junos Space server.
    space, err := junos.NewServer("space.company.com", "admin", "juniper123")
    if err != nil {
        fmt.Println(err)
    }

    // Get the list of devices.
    d, err := space.Devices()
    if err != nil {
        fmt.Println(err)
    }

    // Iterate over our device list and display some information about them.
    for _, device := range d.Devices {
        fmt.Printf("Name: %s, IP Address: %s, Platform: %s\n", device.Name, device.IP, device.Platform)
    }

How to add and remove devices:

    // Add a device to Junos Space.
    jobID, err = space.AddDevice("sdubs-fw", "admin", "juniper123")
    if err != nil {
        fmt.Println(err)
    }

    // Remove a device from Junos Space.
    err = space.RemoveDevice("sdubs-fw")
    if err != nil {
        fmt.Println(err)
    }

    // Here's a good way to loop through all devices, and find the one you want to delete:
    d, err := space.Devices()
    if err != nil {
        fmt.Println(err)
    }

    for _, device := range d.Devices {
        if device.Name == "sdubs-fw" {
            err = space.RemoveDevice(device.Name)
            if err != nil {
                fmt.Println(err)
            }

            fmt.Printf("Deleted device: %s\n", device.Name)
        }
    }

Stage a software image on a device. This basically just downloads it to the device, and does
not upgrade it.

    // The third parameter is whether or not to remove any existing images from the device - true or false.
    jobID, err := space.StageSoftware("sdubs-fw", "junos-srxsme-12.1X46-D30.2-domestic.tgz", false)
    if err != nil {
        fmt.Println(err)
    }

If you want to issue a software upgrade to the device, here's how:

    // Deploy (upgrade) the software image to a device.
    options := &junos.SoftwareDeployOptions{
        UseDownloaded: true,
        Validate: false,
        Reboot: false,
        RebootAfter: 0,
        Cleanup: false,
        RemoveAfter: false,
    }

    jobID, err := space.DeploySoftware("sdubs-fw", "junos-srxsme-12.1X46-D30.2-domestic.tgz", options)
    if err != nil {
        fmt.Println(err)
    }

Remove a staged image from a device.

    // Remove a staged image from the device.
    jobID, err := space.RemoveStagedSoftware("sdubs-fw", "junos-srxsme-12.1X46-D30.2-domestic.tgz")
    if err != nil {
        fmt.Println(err)
    }
*/
package junos
<|endoftext|>


<commit_before>// tweetlib - A fully oauth-authenticated Go Twitter library
//
// Copyright 2011 The Tweetlib Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
A fully OAuth-authenticated implementation of Twitter's REST API v1.1

See https:dev.twitter.com/docs/api/

Usage example:

  import "robteix.com/v2/tweetlib"

  config := &tweetlib.Config{
  	ConsumerKey: "your-consumer-key",
  	ConsumerSecret: "your-consumer-secret",
	Callback: "http:www.my-app.com/my-callback",
  }
  token := &tweetlib.Token{
        OAuthSecret: "your-oauth-secret",
        OAuthToken: "your-oauth-token",
  }
  tr := &tweetlib.Transport{
	Config: config,
        Token: token,
  }

  client, _ := tweetlib.New(tr.Client())
  client.Tweets.Update("Hello, world")

Authentication

Twitter uses OAUTH 1.0a for authentication and tweetlib supports the
3-legged authorization method.

See https://dev.twitter.com/docs/auth/3-legged-authorization

1. Setup a Config structure with the Consuler Key and Secret found on your
application's page. You can create new applications by visiting
http://dev.twitter.com/apps/new. Twitter also requires a callback URL
that will receive Twitter's token

    config := &tweetlib.Config{
        ConsumerKey: "your-consumer-key",
	ConsumerSecret: "your-consumer-secret",
	Callback: "http://www.my-app.com/my-callback",
    }

2. Populate a tweetlibTransport structure with the Config from the previous
step and, for now, an empty Token

    tr := &tweetlib.Transport{
	    Config: config,
            Token: &tweetlib.Token{}
    }

2a. (Optional) tweetlib.Transport uses the http package to talk to Twitter
by default. This may not be possible or desirable. For example, if the
Client is to be used in a Google Appengine app, it becomes necessary to
change the underlying transport to be used. E.g.:

	tr.Transport = &urlfetch.Transport{Context: c}

3. Not it's possible to request the temporary token. This will start the
little Oauth dance with Twitter

	tt, err := tr.TempToken()

4. With the tweetlib.TempToken ready, now it's time to request the user to
authorize your application. This is done by redirecting the user to the URL
returned by tweetlib.TempToken.AuthURL(). (Note that you must save the
temporary token as it will be necessary later to request the permanent token)

	authorizationURL := tt.AUthURL()
	// save tt so it is accessible from the callback later
	// redirect the user to authorizationURL...

5. The user will be promted by Twitter to authorize your application. If they
authorize it, Twitter will call your callback as set in step 1. Twitter will
issue a GET request to your callback with two parameters:

    oauth_token      same as your TempToken.Token
    oauth_verifier   A code that will be used to verify that your call back
                     was valid and received information.

6. Finally, you'll request the permanent token from Twitter

    tok, err := tr.AccessToken(tt, oauth_verifier)

Note that you do not need to update your tweetlib.Transport.Token with the new
token, as this is done automatically, meaning you can immediatly start making
API calls with the same transport.

That said, you must save the token for future use so you don't have to go
through all this dance again each time. Next time you need to make calls
on behalf of a user you already have a token for, you simply set the
Transport with the saved token.

    tr := &tweetlib.Transport{
	    Config: config,
	    Token: savedUserToken,
    }

Application Only Authentication

Using the Twitter API we can obtain an authentication token for only our application

   a := &tweetlib.ApplicationOnly{
      Client: &http.Client{},  // Or whatever client you wish to use
      Config: config,
   }
   token, err := a.GetToken()

Yes it's that easy. Now you have a token that you can use to make API calls

Invalidating An Application Authentication Token

We can invalidate a previously obtained application only token by passing it to the
InvalidateToken function

   token := "AAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAA"
   a := &tweetlib.ApplicationOnly{
      Client: &http.Client{},
      Config: config,
   }
   a.InvalidateToken(token)

Making API calls

For making calls based off a users account, making an API call is trivial once
authentication is set up. It all starts with getting an API Client object:

   oauthClient := (&tweetlib.Transport{config, token, nil}).Client()
   client, err := tweetlib.New(oauthClient)
   if err != nil {
	   panic(err)
   }

For making calls for applications only, we can use our previously obtained
authentication token and pass it to the application-only client constructor.
Unlike utilizing user authenticated APIs, we do not need to use any custom
instances of http.Client

   token, err := a.GetToken()
   client, err := tweetlib.NewApplicationClient(&http.Client{}, token)

Once you have the client, you can make API calls easily. For example,
to post a tweet as the authenticating user

   tweet, err := client.Tweets.Update("Hello, world", nil)

The vast majority of API calls to the Twitter REST API takes one or two
required parameters along with any number of optional ones. For example,
when updating the status, it is possible to attached geographical
coordinates to it via the 'lat' and 'long' optional parameters.

To provide optional parameters, use tweetlib.Optionals

    opts := tweetlib.NewOptionals()
    opts.Add("lat", 37.7821120598956)
    opts.Add("long", -122.400612831116)
    tweet, err := client.Tweets.Update("Hello, world", opts)

There's also two ways of making arbitrary API calls. This is useful
when you need to call a new API that is not directly supported
by tweetlib's utility functions or maybe you want better control of
the response objects.

The first way is using Client.Call like this:

    var user User
    opts := NewOptionals()
    opts.Add("screen_name", "sometwitteruser")
    err := client.Call("GET", "users/show", opts, &user)

Client.Call will try to unmarshal the response returned from Twitter. If
however you wish to do it yourself or maybe not use the types defined
by tweetlib (User, Tweet, etc), you can use CallJSON instead:

    rawJSON, err := client.CallJSON("GET", "users/show", opts)
    // rawJSON now has the JSON response from Twitter

These two functions are usually internally by the many helper functions
defined in tweetlib and also add flexibility to

*/
package tweetlib
<commit_msg>Fix docs<commit_after>// tweetlib - A fully oauth-authenticated Go Twitter library
//
// Copyright 2011 The Tweetlib Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
A fully OAuth-authenticated implementation of Twitter's REST API v1.1

See https:dev.twitter.com/docs/api/

Usage example:

  import "gopkg.in/tweetlib.v2"

  config := &tweetlib.Config{
  	ConsumerKey: "your-consumer-key",
  	ConsumerSecret: "your-consumer-secret",
	Callback: "http:www.my-app.com/my-callback",
  }
  token := &tweetlib.Token{
        OAuthSecret: "your-oauth-secret",
        OAuthToken: "your-oauth-token",
  }
  tr := &tweetlib.Transport{
	Config: config,
        Token: token,
  }

  client, _ := tweetlib.New(tr.Client())
  client.Tweets.Update("Hello, world")

Authentication

Twitter uses OAUTH 1.0a for authentication and tweetlib supports the
3-legged authorization method.

See https://dev.twitter.com/docs/auth/3-legged-authorization

1. Setup a Config structure with the Consuler Key and Secret found on your
application's page. You can create new applications by visiting
http://dev.twitter.com/apps/new. Twitter also requires a callback URL
that will receive Twitter's token

    config := &tweetlib.Config{
        ConsumerKey: "your-consumer-key",
	ConsumerSecret: "your-consumer-secret",
	Callback: "http://www.my-app.com/my-callback",
    }

2. Populate a tweetlibTransport structure with the Config from the previous
step and, for now, an empty Token

    tr := &tweetlib.Transport{
	    Config: config,
            Token: &tweetlib.Token{}
    }

2a. (Optional) tweetlib.Transport uses the http package to talk to Twitter
by default. This may not be possible or desirable. For example, if the
Client is to be used in a Google Appengine app, it becomes necessary to
change the underlying transport to be used. E.g.:

	tr.Transport = &urlfetch.Transport{Context: c}

3. Not it's possible to request the temporary token. This will start the
little Oauth dance with Twitter

	tt, err := tr.TempToken()

4. With the tweetlib.TempToken ready, now it's time to request the user to
authorize your application. This is done by redirecting the user to the URL
returned by tweetlib.TempToken.AuthURL(). (Note that you must save the
temporary token as it will be necessary later to request the permanent token)

	authorizationURL := tt.AUthURL()
	// save tt so it is accessible from the callback later
	// redirect the user to authorizationURL...

5. The user will be promted by Twitter to authorize your application. If they
authorize it, Twitter will call your callback as set in step 1. Twitter will
issue a GET request to your callback with two parameters:

    oauth_token      same as your TempToken.Token
    oauth_verifier   A code that will be used to verify that your call back
                     was valid and received information.

6. Finally, you'll request the permanent token from Twitter

    tok, err := tr.AccessToken(tt, oauth_verifier)

Note that you do not need to update your tweetlib.Transport.Token with the new
token, as this is done automatically, meaning you can immediatly start making
API calls with the same transport.

That said, you must save the token for future use so you don't have to go
through all this dance again each time. Next time you need to make calls
on behalf of a user you already have a token for, you simply set the
Transport with the saved token.

    tr := &tweetlib.Transport{
	    Config: config,
	    Token: savedUserToken,
    }

Application Only Authentication

Using the Twitter API we can obtain an authentication token for only our application

   a := &tweetlib.ApplicationOnly{
      Client: &http.Client{},  // Or whatever client you wish to use
      Config: config,
   }
   token, err := a.GetToken()

Yes it's that easy. Now you have a token that you can use to make API calls

Invalidating An Application Authentication Token

We can invalidate a previously obtained application only token by passing it to the
InvalidateToken function

   token := "AAAAAAAAAAAAAAAAAAAAAAA%2FAAAAAAAAAAAA"
   a := &tweetlib.ApplicationOnly{
      Client: &http.Client{},
      Config: config,
   }
   a.InvalidateToken(token)

Making API calls

For making calls based off a users account, making an API call is trivial once
authentication is set up. It all starts with getting an API Client object:

   oauthClient := (&tweetlib.Transport{config, token, nil}).Client()
   client, err := tweetlib.New(oauthClient)
   if err != nil {
	   panic(err)
   }

For making calls for applications only, we can use our previously obtained
authentication token and pass it to the application-only client constructor.
Unlike utilizing user authenticated APIs, we do not need to use any custom
instances of http.Client

   token, err := a.GetToken()
   client, err := tweetlib.NewApplicationClient(&http.Client{}, token)

Once you have the client, you can make API calls easily. For example,
to post a tweet as the authenticating user

   tweet, err := client.Tweets.Update("Hello, world", nil)

The vast majority of API calls to the Twitter REST API takes one or two
required parameters along with any number of optional ones. For example,
when updating the status, it is possible to attached geographical
coordinates to it via the 'lat' and 'long' optional parameters.

To provide optional parameters, use tweetlib.Optionals

    opts := tweetlib.NewOptionals()
    opts.Add("lat", 37.7821120598956)
    opts.Add("long", -122.400612831116)
    tweet, err := client.Tweets.Update("Hello, world", opts)

There's also two ways of making arbitrary API calls. This is useful
when you need to call a new API that is not directly supported
by tweetlib's utility functions or maybe you want better control of
the response objects.

The first way is using Client.Call like this:

    var user User
    opts := NewOptionals()
    opts.Add("screen_name", "sometwitteruser")
    err := client.Call("GET", "users/show", opts, &user)

Client.Call will try to unmarshal the response returned from Twitter. If
however you wish to do it yourself or maybe not use the types defined
by tweetlib (User, Tweet, etc), you can use CallJSON instead:

    rawJSON, err := client.CallJSON("GET", "users/show", opts)
    // rawJSON now has the JSON response from Twitter

These two functions are usually internally by the many helper functions
defined in tweetlib and also add flexibility to

*/
package tweetlib
<|endoftext|>