<commit_before>package mqs

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/iron-io/functions/api/models"
	mq_config "github.com/iron-io/iron_go3/config"
	ironmq "github.com/iron-io/iron_go3/mq"
)

type assoc struct {
	msgId         string
	reservationId string
}

type IronMQ struct {
	queues []ironmq.Queue
	// Protects the map
	sync.Mutex
	// job id to {msgid, reservationid}
	msgAssoc map[string]*assoc
}

type IronMQConfig struct {
	Token       string `mapstructure:"token"`
	ProjectId   string `mapstructure:"project_id"`
	Host        string `mapstructure:"host"`
	Scheme      string `mapstructure:"scheme"`
	Port        uint16 `mapstructure:"port"`
	QueuePrefix string `mapstructure:"queue_prefix"`
}

func NewIronMQ(url *url.URL) *IronMQ {

	if url.User == nil || url.User.Username() == "" {
		logrus.Fatal("IronMQ requires PROJECT_ID and TOKEN")
	}
	p, ok := url.User.Password()
	if !ok {
		logrus.Fatal("IronMQ requires PROJECT_ID and TOKEN")
	}
	settings := &mq_config.Settings{
		Token:     p,
		ProjectId: url.User.Username(),
		Host:      url.Host,
		Scheme:    "https",
	}

	if url.Scheme == "ironmq+http" {
		settings.Scheme = "http"
	}

	parts := strings.Split(url.Host, ":")
	if len(parts) > 1 {
		settings.Host = parts[0]
		p, err := strconv.Atoi(parts[1])
		if err != nil {
			logrus.WithFields(logrus.Fields{"host_port": url.Host}).Fatal("Invalid host+port combination")
		}
		settings.Port = uint16(p)
	}

	var queueName string
	if url.Path != "" {
		queueName = url.Path
	} else {
		queueName = "titan"
	}
	mq := &IronMQ{
		queues:   make([]ironmq.Queue, 3),
		msgAssoc: make(map[string]*assoc),
	}

	// Check we can connect by trying to create one of the queues. Create is
	// idempotent, so this is fine.
	_, err := ironmq.ConfigCreateQueue(ironmq.QueueInfo{Name: fmt.Sprintf("%s_%d", queueName, 0)}, settings)
	if err != nil {
		logrus.WithError(err).Fatal("Could not connect to IronMQ")
	}

	for i := 0; i < 3; i++ {
		mq.queues[i] = ironmq.ConfigNew(fmt.Sprintf("%s_%d", queueName, i), settings)
	}

	logrus.WithFields(logrus.Fields{"base_queue": queueName}).Info("IronMQ initialized")
	return mq
}

func (mq *IronMQ) Push(ctx context.Context, job *models.Task) (*models.Task, error) {
	if job.Priority == nil || *job.Priority < 0 || *job.Priority > 2 {
		return nil, fmt.Errorf("IronMQ Push job %s: Bad priority", job.ID)
	}

	// Push the work onto the queue.
	buf, err := json.Marshal(job)
	if err != nil {
		return nil, err
	}
	_, err = mq.queues[*job.Priority].PushMessage(ironmq.Message{Body: string(buf), Delay: int64(job.Delay)})
	return job, err
}

func (mq *IronMQ) Reserve(ctx context.Context) (*models.Task, error) {
	var job models.Task

	var messages []ironmq.Message
	var err error
	for i := 2; i >= 0; i-- {
		messages, err = mq.queues[i].LongPoll(1, int(time.Minute), 0 /* wait */, false /* delete */)
		if err != nil {
			// It is OK if the queue does not exist, it will be created when a message is queued.
			if !strings.Contains(err.Error(), "404 Not Found") {
				return nil, err
			}
		}

		if len(messages) == 0 {
			// Try next priority.
			if i == 0 {
				return nil, nil
			}
			continue
		}

		// Found a message!
		break
	}

	message := messages[0]
	if message.Body == "" {
		return nil, nil
	}

	err = json.Unmarshal([]byte(message.Body), &job)
	if err != nil {
		return nil, err
	}
	mq.Lock()
	mq.msgAssoc[job.ID] = &assoc{message.Id, message.ReservationId}
	mq.Unlock()
	return &job, nil
}

func (mq *IronMQ) Delete(ctx context.Context, job *models.Task) error {
	if job.Priority == nil || *job.Priority < 0 || *job.Priority > 2 {
		return fmt.Errorf("IronMQ Delete job %s: Bad priority", job.ID)
	}
	mq.Lock()
	assoc, exists := mq.msgAssoc[job.ID]
	delete(mq.msgAssoc, job.ID)
	mq.Unlock()

	if exists {
		return mq.queues[*job.Priority].DeleteMessage(assoc.msgId, assoc.reservationId)
	}
	return nil
}
<commit_msg>arm overflow s/ns fix (#590)<commit_after>package mqs

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/iron-io/functions/api/models"
	mq_config "github.com/iron-io/iron_go3/config"
	ironmq "github.com/iron-io/iron_go3/mq"
)

type assoc struct {
	msgId         string
	reservationId string
}

type IronMQ struct {
	queues []ironmq.Queue
	// Protects the map
	sync.Mutex
	// job id to {msgid, reservationid}
	msgAssoc map[string]*assoc
}

type IronMQConfig struct {
	Token       string `mapstructure:"token"`
	ProjectId   string `mapstructure:"project_id"`
	Host        string `mapstructure:"host"`
	Scheme      string `mapstructure:"scheme"`
	Port        uint16 `mapstructure:"port"`
	QueuePrefix string `mapstructure:"queue_prefix"`
}

func NewIronMQ(url *url.URL) *IronMQ {

	if url.User == nil || url.User.Username() == "" {
		logrus.Fatal("IronMQ requires PROJECT_ID and TOKEN")
	}
	p, ok := url.User.Password()
	if !ok {
		logrus.Fatal("IronMQ requires PROJECT_ID and TOKEN")
	}
	settings := &mq_config.Settings{
		Token:     p,
		ProjectId: url.User.Username(),
		Host:      url.Host,
		Scheme:    "https",
	}

	if url.Scheme == "ironmq+http" {
		settings.Scheme = "http"
	}

	parts := strings.Split(url.Host, ":")
	if len(parts) > 1 {
		settings.Host = parts[0]
		p, err := strconv.Atoi(parts[1])
		if err != nil {
			logrus.WithFields(logrus.Fields{"host_port": url.Host}).Fatal("Invalid host+port combination")
		}
		settings.Port = uint16(p)
	}

	var queueName string
	if url.Path != "" {
		queueName = url.Path
	} else {
		queueName = "titan"
	}
	mq := &IronMQ{
		queues:   make([]ironmq.Queue, 3),
		msgAssoc: make(map[string]*assoc),
	}

	// Check we can connect by trying to create one of the queues. Create is
	// idempotent, so this is fine.
	_, err := ironmq.ConfigCreateQueue(ironmq.QueueInfo{Name: fmt.Sprintf("%s_%d", queueName, 0)}, settings)
	if err != nil {
		logrus.WithError(err).Fatal("Could not connect to IronMQ")
	}

	for i := 0; i < 3; i++ {
		mq.queues[i] = ironmq.ConfigNew(fmt.Sprintf("%s_%d", queueName, i), settings)
	}

	logrus.WithFields(logrus.Fields{"base_queue": queueName}).Info("IronMQ initialized")
	return mq
}

func (mq *IronMQ) Push(ctx context.Context, job *models.Task) (*models.Task, error) {
	if job.Priority == nil || *job.Priority < 0 || *job.Priority > 2 {
		return nil, fmt.Errorf("IronMQ Push job %s: Bad priority", job.ID)
	}

	// Push the work onto the queue.
	buf, err := json.Marshal(job)
	if err != nil {
		return nil, err
	}
	_, err = mq.queues[*job.Priority].PushMessage(ironmq.Message{Body: string(buf), Delay: int64(job.Delay)})
	return job, err
}

func (mq *IronMQ) Reserve(ctx context.Context) (*models.Task, error) {
	var job models.Task

	var messages []ironmq.Message
	var err error
	for i := 2; i >= 0; i-- {
		messages, err = mq.queues[i].LongPoll(1, 60, 0 /* wait */, false /* delete */)
		if err != nil {
			// It is OK if the queue does not exist, it will be created when a message is queued.
			if !strings.Contains(err.Error(), "404 Not Found") {
				return nil, err
			}
		}

		if len(messages) == 0 {
			// Try next priority.
			if i == 0 {
				return nil, nil
			}
			continue
		}

		// Found a message!
		break
	}

	message := messages[0]
	if message.Body == "" {
		return nil, nil
	}

	err = json.Unmarshal([]byte(message.Body), &job)
	if err != nil {
		return nil, err
	}
	mq.Lock()
	mq.msgAssoc[job.ID] = &assoc{message.Id, message.ReservationId}
	mq.Unlock()
	return &job, nil
}

func (mq *IronMQ) Delete(ctx context.Context, job *models.Task) error {
	if job.Priority == nil || *job.Priority < 0 || *job.Priority > 2 {
		return fmt.Errorf("IronMQ Delete job %s: Bad priority", job.ID)
	}
	mq.Lock()
	assoc, exists := mq.msgAssoc[job.ID]
	delete(mq.msgAssoc, job.ID)
	mq.Unlock()

	if exists {
		return mq.queues[*job.Priority].DeleteMessage(assoc.msgId, assoc.reservationId)
	}
	return nil
}
<|endoftext|>


<commit_before>package start

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	flag "github.com/ogier/pflag"
)

// Add adds a command to either the global Commands map, or, if the command has a parent value, to its parent command as a subcommand.
func Add(cmd *Command) error {
	return Commands.Add(cmd)
}

// Add for CommandMap adds a command to a list of commands.
func (c *CommandMap) Add(cmd *Command) error {
	if cmd == nil {
		return errors.New("Add: Parameter cmd must not be nil.")
	}
	cmd.init()
	if cmd.Parent == "" {
		// Add a top-level command.
		if _, alreadyExists := (*c)[cmd.Name]; alreadyExists {
			return errors.New("Add: command " + cmd.Name + " already exists.")
		}
		(*c)[cmd.Name] = cmd
		return nil
	}
	// Add a child command.
	if _, ok := Commands[cmd.Parent]; ok == false {
		return errors.New("Add: Parent command not found for subcommand " +
			cmd.Name + ".")
	}
	return Commands[cmd.Parent].Add(cmd)
}

// Add for Command adds a subcommand to a command.
func (cmd *Command) Add(subcmd *Command) error {
	cmd.init()
	subcmd.init()
	if _, alreadyExists := (*cmd).children[subcmd.Name]; alreadyExists {
		return errors.New("Add: subcommand " + subcmd.Name +
			" already exists for command " + cmd.Name + ".")
	}
	(*cmd).children[subcmd.Name] = subcmd
	return nil
}

// Usage prints a description of the application and the short help string
// of every command, when called with a nil argument.
// When called with a command as parameter, Usage prints this command's
// long help string as well as the short help strings of the available
// subcommands.
// Parse() or Up() must be called before invoking Usage().
func Usage(cmd *Command) error {
	if cmd == nil {
		applicationUsage()
	} else {
		err := commandUsage(cmd)
		if err != nil {
			fmt.Println(err)
		}
	}
	fmt.Println()
	return nil
}

func applicationUsage() {
	fmt.Println()
	fmt.Println(filepath.Base(os.Args[0]))
	fmt.Println()
	if len(Description) > 0 {
		fmt.Println(Description)
		fmt.Println()
	}
	if len(Commands) > 0 {
		width := maxCmdNameLen()
		fmt.Println("Available commands:")
		fmt.Println()
		for _, c := range Commands {
			fmt.Printf("%-*s  %s\n", width, c.Name, c.Short)
		}
	}
	globalFlags := checkFlags(nil)
	if len(globalFlags) > 0 {
		fmt.Println("Available global flags:")
		flagUsage(nil)
	}
}

func commandUsage(cmd *Command) error {
	if cmd.Parent != "" {
		fmt.Printf("%v ", cmd.Parent)
	}
	fmt.Printf("%v\n\n%v\n", cmd.Name, cmd.Long)
	if len(cmd.Flags) > 0 {
		if err := Parse(); err != nil {
			return err
		}
		fmt.Println()
		fmt.Println("Command-specific flags:")
		fmt.Println()
		flagUsage(cmd.Flags)
	}
	return nil
}

func flagUsage(flagNames []string) {
	flagUsageList := [][]string{}
	var flagNamesAndDefault string
	var width int
	for _, flagName := range flagNames {
		flg := flag.Lookup(flagName)
		if flg == nil {
			panic("Flag '" + flagName + "' does not exist.")
		}
		flagNamesAndDefault = fmt.Sprintf("-%s, --%s=%s", flg.Shorthand, flagName, flg.Value) // TODO -> pflag specific "Shorthand"
		if width < len(flagNamesAndDefault) {
			width = len(flagNamesAndDefault)
		}
		flagUsageList = append(flagUsageList, []string{flagNamesAndDefault, flg.Usage})
	}
	for _, flg := range flagUsageList {
		fmt.Printf("%-*s  %s\n", width, flg[0], flg[1])

	}
}

// maxCmdNameLen returns the length of the longest command name.
func maxCmdNameLen() int {
	maxLength := 0
	for _, cmd := range Commands {
		length := len(cmd.Name)
		if length > maxLength {
			maxLength = length
		}
	}
	return maxLength
}

// init initializes the children map.
// Calling init more than once for the same cmd should be safe.
func (cmd *Command) init() *Command {
	if len(cmd.children) == 0 {
		cmd.children = make(CommandMap)
	}
	cmd.registerPrivateFlags()
	return cmd
}

// registerPrivateFlags adds the command's flags to the global PrivateFlags map.
func (cmd *Command) registerPrivateFlags() {
	for _, f := range cmd.Flags {
		privateFlags[f] = true
	}
}

// If c is nil, then checkFlags returns all *global* flags.
// If c exists, then checkFlags returns a list of *private* flags that
// c has rejected as not being its own flags.
func checkFlags(c *Command) map[string]bool {
	notMyFlags := make(map[string]bool)
	// visit all flags that were passed in via command line:
	flag.Visit(func(f *flag.Flag) {
		isNotMyFlag := true
		if c != nil {
			for _, myFlag := range c.Flags {
				if f.Name == myFlag {
					isNotMyFlag = false // yes, f is among my flags
				}
			}
		}
		if isNotMyFlag {
			for pf := range privateFlags {
				if f.Name == pf {
					notMyFlags[pf] = true
				}
			}
		}
	})
	return notMyFlags
}

// readCommand extracts the command (and any subcommand, if applicable) from the
// list of arguments.
// Parameter args is the list of arguments *after* being parsed by flag.Parse().
// The first item of args is expected to be a command name. If that command has
// subcommands defined, the second item must contain the name of a subcommand.
// If any error occurs, readCommand returns an error and the pre-defined Usage
// command.
func readCommand(args []string) (*Command, error) {
	var cmd, subcmd *Command
	var ok bool
	if len(args) == 0 {
		Usage(nil)
		os.Exit(0)
	}
	var name = args[0]
	if cmd, ok = Commands[name]; ok {
		// command found. Remove it from the argument list.
		args = args[1:]
		if len(cmd.children) > 0 {
			var subname = args[0]
			subcmd, ok = cmd.children[subname]
			if ok {
				// subcommand found.
				args = args[1:]
				cmd = subcmd
			} else {
				// no subcommand passed in, so cmd should have a Cmd to execute
				if cmd.Cmd == nil {
					errmsg := "Command " + cmd.Name + " requires one of these subcommands: "
					for _, n := range cmd.children {
						errmsg += n.Name + ", "
					}
					return &Command{
						Cmd: Usage,
					}, errors.New(errmsg)
				}
			}
		} else {
			cmd = Commands[name]
		}
		cmd.Args = args
		notMyFlags := checkFlags(cmd)
		if len(notMyFlags) > 0 {
			errmsg := fmt.Sprintf("Unknown flags: %v", notMyFlags)
			return &Command{
				Cmd: Usage,
			}, errors.New(errmsg)
		}
		return cmd, nil
	}
	return &Command{
		Cmd: Usage,
	}, nil
}
<commit_msg>Enhanced "Unknown flag(s)" message.<commit_after>package start

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"

	flag "github.com/ogier/pflag"
)

// Add adds a command to either the global Commands map, or, if the command has a parent value, to its parent command as a subcommand.
func Add(cmd *Command) error {
	return Commands.Add(cmd)
}

// Add for CommandMap adds a command to a list of commands.
func (c *CommandMap) Add(cmd *Command) error {
	if cmd == nil {
		return errors.New("Add: Parameter cmd must not be nil.")
	}
	cmd.init()
	if cmd.Parent == "" {
		// Add a top-level command.
		if _, alreadyExists := (*c)[cmd.Name]; alreadyExists {
			return errors.New("Add: command " + cmd.Name + " already exists.")
		}
		(*c)[cmd.Name] = cmd
		return nil
	}
	// Add a child command.
	if _, ok := Commands[cmd.Parent]; ok == false {
		return errors.New("Add: Parent command not found for subcommand " +
			cmd.Name + ".")
	}
	return Commands[cmd.Parent].Add(cmd)
}

// Add for Command adds a subcommand to a command.
func (cmd *Command) Add(subcmd *Command) error {
	cmd.init()
	subcmd.init()
	if _, alreadyExists := (*cmd).children[subcmd.Name]; alreadyExists {
		return errors.New("Add: subcommand " + subcmd.Name +
			" already exists for command " + cmd.Name + ".")
	}
	(*cmd).children[subcmd.Name] = subcmd
	return nil
}

// Usage prints a description of the application and the short help string
// of every command, when called with a nil argument.
// When called with a command as parameter, Usage prints this command's
// long help string as well as the short help strings of the available
// subcommands.
// Parse() or Up() must be called before invoking Usage().
func Usage(cmd *Command) error {
	if cmd == nil {
		applicationUsage()
	} else {
		err := commandUsage(cmd)
		if err != nil {
			fmt.Println(err)
		}
	}
	fmt.Println()
	return nil
}

func applicationUsage() {
	fmt.Println()
	fmt.Println(filepath.Base(os.Args[0]))
	fmt.Println()
	if len(Description) > 0 {
		fmt.Println(Description)
		fmt.Println()
	}
	if len(Commands) > 0 {
		width := maxCmdNameLen()
		fmt.Println("Available commands:")
		fmt.Println()
		for _, c := range Commands {
			fmt.Printf("%-*s  %s\n", width, c.Name, c.Short)
		}
	}
	globalFlags := checkFlags(nil)
	if len(globalFlags) > 0 {
		fmt.Println("Available global flags:")
		flagUsage(nil)
	}
}

func commandUsage(cmd *Command) error {
	if cmd.Parent != "" {
		fmt.Printf("%v ", cmd.Parent)
	}
	fmt.Printf("%v\n\n%v\n", cmd.Name, cmd.Long)
	if len(cmd.Flags) > 0 {
		if err := Parse(); err != nil {
			return err
		}
		fmt.Println()
		fmt.Println("Command-specific flags:")
		fmt.Println()
		flagUsage(cmd.Flags)
	}
	return nil
}

func flagUsage(flagNames []string) {
	flagUsageList := [][]string{}
	var flagNamesAndDefault string
	var width int
	for _, flagName := range flagNames {
		flg := flag.Lookup(flagName)
		if flg == nil {
			panic("Flag '" + flagName + "' does not exist.")
		}
		flagNamesAndDefault = fmt.Sprintf("-%s, --%s=%s", flg.Shorthand, flagName, flg.Value) // TODO -> pflag specific "Shorthand"
		if width < len(flagNamesAndDefault) {
			width = len(flagNamesAndDefault)
		}
		flagUsageList = append(flagUsageList, []string{flagNamesAndDefault, flg.Usage})
	}
	for _, flg := range flagUsageList {
		fmt.Printf("%-*s  %s\n", width, flg[0], flg[1])

	}
}

// maxCmdNameLen returns the length of the longest command name.
func maxCmdNameLen() int {
	maxLength := 0
	for _, cmd := range Commands {
		length := len(cmd.Name)
		if length > maxLength {
			maxLength = length
		}
	}
	return maxLength
}

// init initializes the children map.
// Calling init more than once for the same cmd should be safe.
func (cmd *Command) init() *Command {
	if len(cmd.children) == 0 {
		cmd.children = make(CommandMap)
	}
	cmd.registerPrivateFlags()
	return cmd
}

// registerPrivateFlags adds the command's flags to the global PrivateFlags map.
func (cmd *Command) registerPrivateFlags() {
	for _, f := range cmd.Flags {
		privateFlags[f] = true
	}
}

// If c is nil, then checkFlags returns all *global* flags.
// If c exists, then checkFlags returns a list of *private* flags that
// c has rejected as not being its own flags.
func checkFlags(c *Command) map[string]bool {
	notMyFlags := make(map[string]bool)
	// visit all flags that were passed in via command line:
	flag.Visit(func(f *flag.Flag) {
		isNotMyFlag := true
		if c != nil {
			for _, myFlag := range c.Flags {
				if f.Name == myFlag {
					isNotMyFlag = false // yes, f is among my flags
				}
			}
		}
		if isNotMyFlag {
			for pf := range privateFlags {
				if f.Name == pf {
					notMyFlags[pf] = true
				}
			}
		}
	})
	return notMyFlags
}

// readCommand extracts the command (and any subcommand, if applicable) from the
// list of arguments.
// Parameter args is the list of arguments *after* being parsed by flag.Parse().
// The first item of args is expected to be a command name. If that command has
// subcommands defined, the second item must contain the name of a subcommand.
// If any error occurs, readCommand returns an error and the pre-defined Usage
// command.
func readCommand(args []string) (*Command, error) {
	var cmd, subcmd *Command
	var ok bool
	if len(args) == 0 {
		Usage(nil)
		os.Exit(0)
	}
	var name = args[0]
	if cmd, ok = Commands[name]; ok {
		// command found. Remove it from the argument list.
		args = args[1:]
		if len(cmd.children) > 0 {
			var subname = args[0]
			subcmd, ok = cmd.children[subname]
			if ok {
				// subcommand found.
				args = args[1:]
				cmd = subcmd
			} else {
				// no subcommand passed in, so cmd should have a Cmd to execute
				if cmd.Cmd == nil {
					errmsg := "Command " + cmd.Name + " requires one of these subcommands: "
					for _, n := range cmd.children {
						errmsg += n.Name + ", "
					}
					return &Command{
						Cmd: Usage,
					}, errors.New(errmsg)
				}
			}
		} else {
			cmd = Commands[name]
		}
		cmd.Args = args
		notMyFlags := checkFlags(cmd)
		s := ""
		if len(notMyFlags) > 0 {
			if len(notMyFlags) > 1 {
				s = "s"
			}
			errmsg := fmt.Sprintf("Unknown flag%s: %v", s, notMyFlags)
			return &Command{
				Cmd: Usage,
			}, errors.New(errmsg)
		}
		return cmd, nil
	}
	return &Command{
		Cmd: Usage,
	}, nil
}
<|endoftext|>


<commit_before>package medtronic

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/ecc1/medtronic/packet"
)

const (
	pumpEnvVar      = "MEDTRONIC_PUMP_ID"
	carelinkDevice  = 0xA7
	maxPacketSize   = 70   // excluding CRC byte
	historyPageSize = 1024 // including CRC16
)

var (
	carelinkPrefix []byte
)

func initCarelinkPrefix() {
	if len(carelinkPrefix) != 0 {
		return
	}
	id := os.Getenv(pumpEnvVar)
	if len(id) == 0 {
		log.Fatalf("%s environment variable is not set", pumpEnvVar)
	}
	if len(id) != 6 {
		log.Fatalf("%s environment variable must be 6 digits", pumpEnvVar)
	}
	carelinkPrefix = append([]byte{carelinkDevice}, marshalDeviceID(id)...)
}

func marshalDeviceID(id string) []byte {
	if len(id) != 6 {
		panic("device ID must be 6 digits")
	}
	return []byte{
		(id[0]-'0')<<4 | (id[1] - '0'),
		(id[2]-'0')<<4 | (id[3] - '0'),
		(id[4]-'0')<<4 | (id[5] - '0'),
	}
}

// Command represents a pump command.
type Command byte

//go:generate stringer -type Command

const (
	ack Command = 0x06
	nak Command = 0x15
)

// NoResponseError indicates that no response to a command was received.
type NoResponseError Command

func (e NoResponseError) Error() string {
	return fmt.Sprintf("no response to %v", Command(e))
}

// NoResponse checks whether the pump has a NoResponseError.
func (pump *Pump) NoResponse() bool {
	_, ok := pump.Error().(NoResponseError)
	return ok
}

// InvalidCommandError indicates that the pump rejected a command as invalid.
type InvalidCommandError struct {
	Command   Command
	PumpError PumpError
}

// PumpError represents an error response from the pump.
type PumpError byte

//go:generate stringer -type PumpError

// Pump error codes.
const (
	CommandRefused           PumpError = 0x08
	MaxSettingExceeded       PumpError = 0x09
	BolusInProgress          PumpError = 0x0C
	InvalidHistoryPageNumber PumpError = 0x0D
)

func (e InvalidCommandError) Error() string {
	return fmt.Sprintf("%v error: %v", e.Command, e.PumpError)
}

// BadResponseError indicates an unexpected response to a command.
type BadResponseError struct {
	Command Command
	Data    []byte
}

func (e BadResponseError) Error() string {
	return fmt.Sprintf("unexpected response to %v: % X", e.Command, e.Data)
}

// BadResponse sets the pump's error state to a BadResponseError.
func (pump *Pump) BadResponse(cmd Command, data []byte) {
	pump.SetError(BadResponseError{Command: cmd, Data: data})
}

// carelinkPacket constructs a packet
// with the specified command code and parameters.
// A command packet with no parameters is 7 bytes long:
//   device type (0xA7)
//   3 bytes of pump ID
//   command code
//   length of parameters (0)
//   CRC-8
// A command packet with parameters is 71 bytes long:
//   device type (0xA7)
//   3 bytes of pump ID
//   command code
//   length of parameters
//   64 bytes of parameters plus padding
//   CRC-8
func carelinkPacket(cmd Command, params []byte) []byte {
	initCarelinkPrefix()
	var data []byte
	if len(params) == 0 {
		data = make([]byte, 6)
	} else {
		data = make([]byte, maxPacketSize)
	}
	copy(data, carelinkPrefix)
	data[4] = byte(cmd)
	data[5] = byte(len(params))
	if len(params) != 0 {
		copy(data[6:], params)
	}
	return packet.Encode(data)
}

// Execute sends a command and parameters to the pump and returns its response.
// Commands with parameters require an initial exchange with no parameters,
// followed by an exchange with the actual arguments.
func (pump *Pump) Execute(cmd Command, params ...byte) []byte {
	if len(params) != 0 {
		pump.perform(cmd, ack, nil)
		return pump.perform(cmd, ack, params)
	}
	return pump.perform(cmd, cmd, nil)
}

// History pages are returned as a series of 65-byte fragments:
//   sequence number (1 to 16)
//   64 bytes of payload
// The caller must send an ACK to receive the next fragment
// or a NAK to have the current one retransmitted.
// The 0x80 bit is set in the sequence number of the final fragment.
// The page consists of the concatenated payloads.
// The final 2 bytes are the CRC-16 of the preceding data.

const (
	numFragments    = 16
	fragmentLength  = 65
	doneBit         = 1 << 7
	maxNAKs         = 10
	downloadTimeout = 150 * time.Millisecond
)

// Download requests the given history page from the pump.
func (pump *Pump) Download(cmd Command, page int) []byte {
	timeout := pump.Timeout()
	pump.SetTimeout(downloadTimeout)
	defer pump.SetTimeout(timeout)
	results := make([]byte, 0, historyPageSize)
	data := pump.Execute(cmd, byte(page))
	if pump.Error() != nil {
		return nil
	}
	retries := pump.Retries()
	pump.SetRetries(1)
	defer pump.SetRetries(retries)
	seq := 1
	for {
		payload, n := pump.checkFragment(page, data, seq)
		if pump.Error() != nil {
			return nil
		}
		if n == seq {
			results = append(results, payload...)
			seq++
		}
		if n == numFragments {
			return pump.checkPageCRC(page, results)
		}
		// Acknowledge the current fragment and receive the next.
		next := pump.perform(ack, cmd, nil)
		if pump.Error() != nil {
			if !pump.NoResponse() {
				return nil
			}
			next = pump.handleNoResponse(cmd, page, seq)
		}
		data = next
	}
}

// checkFragment verifies that a fragment has the expected sequence number
// and returns the payload.
func (pump *Pump) checkFragment(page int, data []byte, expected int) ([]byte, int) {
	if len(data) != fragmentLength {
		pump.SetError(fmt.Errorf("history page %d: unexpected fragment length (%d)", page, len(data)))
		return nil, 0
	}
	seqNum := int(data[0] &^ doneBit)
	if seqNum > expected {
		// Missed fragment.
		pump.SetError(fmt.Errorf("history page %d: received fragment %d instead of %d", page, seqNum, expected))
		return nil, 0
	}
	if seqNum < expected {
		// Skip duplicate responses.
		return nil, seqNum
	}
	// This is the next fragment.
	done := data[0]&doneBit != 0
	if (done && seqNum != numFragments) || (!done && seqNum == numFragments) {
		pump.SetError(fmt.Errorf("history page %d: unexpected final sequence number (%d)", page, seqNum))
		return nil, seqNum
	}
	return data[1:], seqNum
}

// handleNoResponse sends NAKs to request retransmission of the expected fragment.
func (pump *Pump) handleNoResponse(cmd Command, page int, expected int) []byte {
	for count := 0; count < maxNAKs; count++ {
		pump.SetError(nil)
		data := pump.perform(nak, cmd, nil)
		if pump.Error() == nil {
			seqNum := int(data[0] &^ doneBit)
			format := "history page %d: received fragment %d after %d NAK"
			if count != 0 {
				format += "s"
			}
			log.Printf(format, page, seqNum, count+1)
			return data
		}
		if !pump.NoResponse() {
			return nil
		}
	}
	pump.SetError(fmt.Errorf("history page %d: lost fragment %d", page, expected))
	return nil
}

// checkPageCRC verifies the history page CRC and returns the page data with the CRC removed.
func (pump *Pump) checkPageCRC(page int, data []byte) []byte {
	if len(data) != historyPageSize {
		pump.SetError(fmt.Errorf("history page %d: unexpected size (%d)", page, len(data)))
		return nil
	}
	dataCRC := twoByteUint(data[historyPageSize-2:])
	data = data[:historyPageSize-2]
	calcCRC := packet.CRC16(data)
	if calcCRC != dataCRC {
		pump.SetError(fmt.Errorf("history page %d: computed CRC %02X but received %02X", page, calcCRC, dataCRC))
		return nil
	}
	return data
}

func (pump *Pump) perform(cmd Command, resp Command, params []byte) []byte {
	if pump.Error() != nil {
		return nil
	}
	p := carelinkPacket(cmd, params)
	for tries := 0; tries < pump.retries || pump.retries == 0; tries++ {
		pump.Radio.Send(p)
		response, rssi := pump.Radio.Receive(pump.Timeout())
		if len(response) == 0 {
			pump.SetError(nil)
			continue
		}
		data, err := packet.Decode(response)
		if err != nil {
			continue
		}
		if pump.unexpected(cmd, resp, data) {
			return nil
		}
		pump.rssi = rssi
		return data[5:]
	}
	pump.SetError(NoResponseError(cmd))
	return nil
}

func (pump *Pump) unexpected(cmd Command, resp Command, data []byte) bool {
	if len(data) < 6 {
		pump.BadResponse(cmd, data)
		return true
	}
	n := len(carelinkPrefix)
	if !bytes.Equal(data[:n], carelinkPrefix) {
		pump.BadResponse(cmd, data)
		return true
	}
	switch Command(data[n]) {
	case cmd:
		return false
	case resp:
		return false
	case ack:
		if cmd == wakeup {
			return false
		}
		pump.BadResponse(cmd, data)
		return true
	case nak:
		pump.SetError(InvalidCommandError{
			Command:   cmd,
			PumpError: PumpError(data[n+1]),
		})
		return true
	default:
		pump.BadResponse(cmd, data)
		return true
	}
}
<commit_msg>Log commands that take more than one try<commit_after>package medtronic

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/ecc1/medtronic/packet"
)

const (
	pumpEnvVar      = "MEDTRONIC_PUMP_ID"
	carelinkDevice  = 0xA7
	maxPacketSize   = 70   // excluding CRC byte
	historyPageSize = 1024 // including CRC16
)

var (
	carelinkPrefix []byte
)

func initCarelinkPrefix() {
	if len(carelinkPrefix) != 0 {
		return
	}
	id := os.Getenv(pumpEnvVar)
	if len(id) == 0 {
		log.Fatalf("%s environment variable is not set", pumpEnvVar)
	}
	if len(id) != 6 {
		log.Fatalf("%s environment variable must be 6 digits", pumpEnvVar)
	}
	carelinkPrefix = append([]byte{carelinkDevice}, marshalDeviceID(id)...)
}

func marshalDeviceID(id string) []byte {
	if len(id) != 6 {
		panic("device ID must be 6 digits")
	}
	return []byte{
		(id[0]-'0')<<4 | (id[1] - '0'),
		(id[2]-'0')<<4 | (id[3] - '0'),
		(id[4]-'0')<<4 | (id[5] - '0'),
	}
}

// Command represents a pump command.
type Command byte

//go:generate stringer -type Command

const (
	ack Command = 0x06
	nak Command = 0x15
)

// NoResponseError indicates that no response to a command was received.
type NoResponseError Command

func (e NoResponseError) Error() string {
	return fmt.Sprintf("no response to %v", Command(e))
}

// NoResponse checks whether the pump has a NoResponseError.
func (pump *Pump) NoResponse() bool {
	_, ok := pump.Error().(NoResponseError)
	return ok
}

// InvalidCommandError indicates that the pump rejected a command as invalid.
type InvalidCommandError struct {
	Command   Command
	PumpError PumpError
}

// PumpError represents an error response from the pump.
type PumpError byte

//go:generate stringer -type PumpError

// Pump error codes.
const (
	CommandRefused           PumpError = 0x08
	MaxSettingExceeded       PumpError = 0x09
	BolusInProgress          PumpError = 0x0C
	InvalidHistoryPageNumber PumpError = 0x0D
)

func (e InvalidCommandError) Error() string {
	return fmt.Sprintf("%v error: %v", e.Command, e.PumpError)
}

// BadResponseError indicates an unexpected response to a command.
type BadResponseError struct {
	Command Command
	Data    []byte
}

func (e BadResponseError) Error() string {
	return fmt.Sprintf("unexpected response to %v: % X", e.Command, e.Data)
}

// BadResponse sets the pump's error state to a BadResponseError.
func (pump *Pump) BadResponse(cmd Command, data []byte) {
	pump.SetError(BadResponseError{Command: cmd, Data: data})
}

// carelinkPacket constructs a packet
// with the specified command code and parameters.
// A command packet with no parameters is 7 bytes long:
//   device type (0xA7)
//   3 bytes of pump ID
//   command code
//   length of parameters (0)
//   CRC-8
// A command packet with parameters is 71 bytes long:
//   device type (0xA7)
//   3 bytes of pump ID
//   command code
//   length of parameters
//   64 bytes of parameters plus padding
//   CRC-8
func carelinkPacket(cmd Command, params []byte) []byte {
	initCarelinkPrefix()
	var data []byte
	if len(params) == 0 {
		data = make([]byte, 6)
	} else {
		data = make([]byte, maxPacketSize)
	}
	copy(data, carelinkPrefix)
	data[4] = byte(cmd)
	data[5] = byte(len(params))
	if len(params) != 0 {
		copy(data[6:], params)
	}
	return packet.Encode(data)
}

// Execute sends a command and parameters to the pump and returns its response.
// Commands with parameters require an initial exchange with no parameters,
// followed by an exchange with the actual arguments.
func (pump *Pump) Execute(cmd Command, params ...byte) []byte {
	if len(params) != 0 {
		pump.perform(cmd, ack, nil)
		return pump.perform(cmd, ack, params)
	}
	return pump.perform(cmd, cmd, nil)
}

// History pages are returned as a series of 65-byte fragments:
//   sequence number (1 to 16)
//   64 bytes of payload
// The caller must send an ACK to receive the next fragment
// or a NAK to have the current one retransmitted.
// The 0x80 bit is set in the sequence number of the final fragment.
// The page consists of the concatenated payloads.
// The final 2 bytes are the CRC-16 of the preceding data.

const (
	numFragments    = 16
	fragmentLength  = 65
	doneBit         = 1 << 7
	maxNAKs         = 10
	downloadTimeout = 150 * time.Millisecond
)

// Download requests the given history page from the pump.
func (pump *Pump) Download(cmd Command, page int) []byte {
	timeout := pump.Timeout()
	pump.SetTimeout(downloadTimeout)
	defer pump.SetTimeout(timeout)
	results := make([]byte, 0, historyPageSize)
	data := pump.Execute(cmd, byte(page))
	if pump.Error() != nil {
		return nil
	}
	retries := pump.Retries()
	pump.SetRetries(1)
	defer pump.SetRetries(retries)
	seq := 1
	for {
		payload, n := pump.checkFragment(page, data, seq)
		if pump.Error() != nil {
			return nil
		}
		if n == seq {
			results = append(results, payload...)
			seq++
		}
		if n == numFragments {
			return pump.checkPageCRC(page, results)
		}
		// Acknowledge the current fragment and receive the next.
		next := pump.perform(ack, cmd, nil)
		if pump.Error() != nil {
			if !pump.NoResponse() {
				return nil
			}
			next = pump.handleNoResponse(cmd, page, seq)
		}
		data = next
	}
}

// checkFragment verifies that a fragment has the expected sequence number
// and returns the payload.
func (pump *Pump) checkFragment(page int, data []byte, expected int) ([]byte, int) {
	if len(data) != fragmentLength {
		pump.SetError(fmt.Errorf("history page %d: unexpected fragment length (%d)", page, len(data)))
		return nil, 0
	}
	seqNum := int(data[0] &^ doneBit)
	if seqNum > expected {
		// Missed fragment.
		pump.SetError(fmt.Errorf("history page %d: received fragment %d instead of %d", page, seqNum, expected))
		return nil, 0
	}
	if seqNum < expected {
		// Skip duplicate responses.
		return nil, seqNum
	}
	// This is the next fragment.
	done := data[0]&doneBit != 0
	if (done && seqNum != numFragments) || (!done && seqNum == numFragments) {
		pump.SetError(fmt.Errorf("history page %d: unexpected final sequence number (%d)", page, seqNum))
		return nil, seqNum
	}
	return data[1:], seqNum
}

// handleNoResponse sends NAKs to request retransmission of the expected fragment.
func (pump *Pump) handleNoResponse(cmd Command, page int, expected int) []byte {
	for count := 0; count < maxNAKs; count++ {
		pump.SetError(nil)
		data := pump.perform(nak, cmd, nil)
		if pump.Error() == nil {
			seqNum := int(data[0] &^ doneBit)
			format := "history page %d: received fragment %d after %d NAK"
			if count != 0 {
				format += "s"
			}
			log.Printf(format, page, seqNum, count+1)
			return data
		}
		if !pump.NoResponse() {
			return nil
		}
	}
	pump.SetError(fmt.Errorf("history page %d: lost fragment %d", page, expected))
	return nil
}

// checkPageCRC verifies the history page CRC and returns the page data with the CRC removed.
func (pump *Pump) checkPageCRC(page int, data []byte) []byte {
	if len(data) != historyPageSize {
		pump.SetError(fmt.Errorf("history page %d: unexpected size (%d)", page, len(data)))
		return nil
	}
	dataCRC := twoByteUint(data[historyPageSize-2:])
	data = data[:historyPageSize-2]
	calcCRC := packet.CRC16(data)
	if calcCRC != dataCRC {
		pump.SetError(fmt.Errorf("history page %d: computed CRC %02X but received %02X", page, calcCRC, dataCRC))
		return nil
	}
	return data
}

func (pump *Pump) perform(cmd Command, resp Command, params []byte) []byte {
	if pump.Error() != nil {
		return nil
	}
	p := carelinkPacket(cmd, params)
	for tries := 0; tries < pump.retries || pump.retries == 0; tries++ {
		pump.Radio.Send(p)
		response, rssi := pump.Radio.Receive(pump.Timeout())
		if len(response) == 0 {
			pump.SetError(nil)
			continue
		}
		data, err := packet.Decode(response)
		if err != nil {
			continue
		}
		if pump.unexpected(cmd, resp, data) {
			return nil
		}
		if tries != 0 {
			var r string
			switch tries {
			case 1:
				r = "retry"
			default:
				r = "retries"
			}
			log.Printf("%v command required %d %s", cmd, tries, r)
		}
		pump.rssi = rssi
		return data[5:]
	}
	pump.SetError(NoResponseError(cmd))
	return nil
}

func (pump *Pump) unexpected(cmd Command, resp Command, data []byte) bool {
	if len(data) < 6 {
		pump.BadResponse(cmd, data)
		return true
	}
	n := len(carelinkPrefix)
	if !bytes.Equal(data[:n], carelinkPrefix) {
		pump.BadResponse(cmd, data)
		return true
	}
	switch Command(data[n]) {
	case cmd:
		return false
	case resp:
		return false
	case ack:
		if cmd == wakeup {
			return false
		}
		pump.BadResponse(cmd, data)
		return true
	case nak:
		pump.SetError(InvalidCommandError{
			Command:   cmd,
			PumpError: PumpError(data[n+1]),
		})
		return true
	default:
		pump.BadResponse(cmd, data)
		return true
	}
}
<|endoftext|>


<commit_before>package complete

import "github.com/posener/complete/match"

// Command represents a command line
// It holds the data that enables auto completion of command line
// Command can also be a sub command.
type Command struct {
	// Sub is map of sub commands of the current command
	// The key refer to the sub command name, and the value is it's
	// Command descriptive struct.
	Sub Commands

	// Flags is a map of flags that the command accepts.
	// The key is the flag name, and the value is it's predictions.
	Flags Flags

	// Args are extra arguments that the command accepts, those who are
	// given without any flag before.
	Args Predictor
}

// Commands is the type of Sub member, it maps a command name to a command struct
type Commands map[string]Command

// Flags is the type Flags of the Flags member, it maps a flag name to the flag predictions.
type Flags map[string]Predictor

// Predict returns all possible predictions for args according to the command struct
func (c *Command) Predict(a Args) (predictions []string) {
	predictions, _ = c.predict(a)
	return
}

func (c *Command) predict(a Args) (options []string, only bool) {

	// if wordCompleted has something that needs to follow it,
	// it is the most relevant completion
	if predictor, ok := c.Flags[a.LastCompleted]; ok && predictor != nil {
		Log("Predicting according to flag %s", a.Last)
		return predictor.Predict(a), true
	}

	sub, options, only := c.searchSub(a)
	if only {
		return
	}

	// if no sub command was found, return a list of the sub commands
	if sub == "" {
		options = append(options, c.subCommands(a.Last)...)
	}

	// add global available complete Predict
	for flag := range c.Flags {
		if match.Prefix(flag, a.Last) {
			options = append(options, flag)
		}
	}

	// add additional expected argument of the command
	if c.Args != nil {
		options = append(options, c.Args.Predict(a)...)
	}

	return
}

// searchSub searches recursively within sub commands if the sub command appear
// in the on of the arguments.
func (c *Command) searchSub(a Args) (sub string, all []string, only bool) {
	for i, arg := range a.Completed {
		if cmd, ok := c.Sub[arg]; ok {
			sub = arg
			all, only = cmd.predict(a.from(i))
			return
		}
	}
	return
}

// subCommands returns a list of matching sub commands
func (c *Command) subCommands(last string) (prediction []string) {
	for sub := range c.Sub {
		if match.Prefix(sub, last) {
			prediction = append(prediction, sub)
		}
	}
	return
}
<commit_msg>Fix comment<commit_after>package complete

import "github.com/posener/complete/match"

// Command represents a command line
// It holds the data that enables auto completion of command line
// Command can also be a sub command.
type Command struct {
	// Sub is map of sub commands of the current command
	// The key refer to the sub command name, and the value is it's
	// Command descriptive struct.
	Sub Commands

	// Flags is a map of flags that the command accepts.
	// The key is the flag name, and the value is it's predictions.
	Flags Flags

	// Args are extra arguments that the command accepts, those who are
	// given without any flag before.
	Args Predictor
}

// Commands is the type of Sub member, it maps a command name to a command struct
type Commands map[string]Command

// Flags is the type Flags of the Flags member, it maps a flag name to the flag predictions.
type Flags map[string]Predictor

// Predict returns all possible predictions for args according to the command struct
func (c *Command) Predict(a Args) (predictions []string) {
	predictions, _ = c.predict(a)
	return
}

func (c *Command) predict(a Args) (options []string, only bool) {

	// if wordCompleted has something that needs to follow it,
	// it is the most relevant completion
	if predictor, ok := c.Flags[a.LastCompleted]; ok && predictor != nil {
		Log("Predicting according to flag %s", a.Last)
		return predictor.Predict(a), true
	}

	sub, options, only := c.searchSub(a)
	if only {
		return
	}

	// if no sub command was found, return a list of the sub commands
	if sub == "" {
		options = append(options, c.subCommands(a.Last)...)
	}

	// add global available complete options
	for flag := range c.Flags {
		if match.Prefix(flag, a.Last) {
			options = append(options, flag)
		}
	}

	// add additional expected argument of the command
	if c.Args != nil {
		options = append(options, c.Args.Predict(a)...)
	}

	return
}

// searchSub searches recursively within sub commands if the sub command appear
// in the on of the arguments.
func (c *Command) searchSub(a Args) (sub string, all []string, only bool) {
	for i, arg := range a.Completed {
		if cmd, ok := c.Sub[arg]; ok {
			sub = arg
			all, only = cmd.predict(a.from(i))
			return
		}
	}
	return
}

// subCommands returns a list of matching sub commands
func (c *Command) subCommands(last string) (prediction []string) {
	for sub := range c.Sub {
		if match.Prefix(sub, last) {
			prediction = append(prediction, sub)
		}
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
)

var cmdStart = &Command{
	Exec:        runStart,
	UsageLine:   "start [OPTIONS] SERVER [SERVER...]",
	Description: "Start a stopped server",
	Help:        "Start a stopped server.",
}

func init() {
	// FIXME: -h
	cmdStart.Flag.BoolVar(&startW, []string{"w", "-wait"}, false, "Synchronous start. Wait for SSH to be ready")
}

// Flags
var startW bool // -w flag

func startOnce(cmd *Command, needle string, successChan chan bool, errChan chan error) {
	server := cmd.GetServer(needle)

	err := cmd.API.PostServerAction(server, "poweron")
	if err != nil {
		if err.Error() != "server should be stopped" {
			errChan <- errors.New(fmt.Sprintf("failed to stop server %s: %v", server, err))
			return
		}
	} else {
		if startW {
			_, err = WaitForServerReady(cmd.API, server)
			if err != nil {
				errChan <- errors.New(fmt.Sprintf("Failed to wait for server %s to be ready, %v", needle, err))
				return
			}
		}
		fmt.Println(needle)
	}
	successChan <- true
}

func runStart(cmd *Command, args []string) {
	if len(args) == 0 {
		log.Fatalf("usage: scw %s", cmd.UsageLine)
	}

	hasError := false
	errChan := make(chan error)
	successChan := make(chan bool)
	remainingItems := len(args)

	for _, needle := range args {
		go startOnce(cmd, needle, successChan, errChan)
	}

	for {
		select {
		case _ = <-successChan:
			remainingItems--
		case err := <-errChan:
			log.Errorf(fmt.Sprintf("%s", err))
			remainingItems--
			hasError = true
		}

		if remainingItems == 0 {
			break
		}
	}

	if hasError {
		os.Exit(1)
	}
}
<commit_msg>Added 'start --timeout' option<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
)

var cmdStart = &Command{
	Exec:        runStart,
	UsageLine:   "start [OPTIONS] SERVER [SERVER...]",
	Description: "Start a stopped server",
	Help:        "Start a stopped server.",
}

func init() {
	// FIXME: -h
	cmdStart.Flag.BoolVar(&startW, []string{"w", "-wait"}, false, "Synchronous start. Wait for SSH to be ready")
	cmdStart.Flag.Float64Var(&startTimeout, []string{"T", "-timeout"}, 0, "Set timeout values to seconds")
}

// Flags
var startW bool          // -w flag
var startTimeout float64 // -T flag

func startOnce(cmd *Command, needle string, successChan chan bool, errChan chan error) {
	server := cmd.GetServer(needle)

	err := cmd.API.PostServerAction(server, "poweron")
	if err != nil {
		if err.Error() != "server should be stopped" {
			errChan <- errors.New(fmt.Sprintf("failed to stop server %s: %v", server, err))
			return
		}
	} else {
		if startW {
			_, err = WaitForServerReady(cmd.API, server)
			if err != nil {
				errChan <- errors.New(fmt.Sprintf("Failed to wait for server %s to be ready, %v", needle, err))
				return
			}
		}
		fmt.Println(needle)
	}
	successChan <- true
}

func runStart(cmd *Command, args []string) {
	if len(args) == 0 {
		log.Fatalf("usage: scw %s", cmd.UsageLine)
	}

	hasError := false
	errChan := make(chan error)
	successChan := make(chan bool)
	remainingItems := len(args)

	for _, needle := range args {
		go startOnce(cmd, needle, successChan, errChan)
	}

	if startTimeout > 0 {
		go func() {
			time.Sleep(time.Duration(startTimeout*1000) * time.Millisecond)
			log.Fatalf("Operation timed out")
		}()
	}

	for {
		select {
		case _ = <-successChan:
			remainingItems--
		case err := <-errChan:
			log.Errorf(fmt.Sprintf("%s", err))
			remainingItems--
			hasError = true
		}

		if remainingItems == 0 {
			break
		}
	}

	if hasError {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package param

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"html/template"
	"strconv"
	"strings"
	"time"
)

const (
	EmptyHTML      = template.HTML(``)
	EmptyJS        = template.JS(``)
	EmptyCSS       = template.CSS(``)
	EmptyHTMLAttr  = template.HTMLAttr(``)
	DateTimeLayout = `2006-01-02 15:04:05`
	DateTimeShort  = `2006-01-02 15:04`
	DateLayout     = `2006-01-02`
	TimeLayout     = `15:04:05`
	DateMd         = `01-02`
	DateShort      = `06-01-02`
	TimeShort      = `15:04`
)

func AsType(typ string, val interface{}) interface{} {
	switch typ {
	case `string`:
		return AsString(val)
	case `bytes`:
		return AsBytes(val)
	case `bool`:
		return AsBool(val)
	case `float64`:
		return AsFloat64(val)
	case `float32`:
		return AsFloat32(val)
	case `int8`:
		return AsInt8(val)
	case `int16`:
		return AsInt16(val)
	case `int`:
		return AsInt(val)
	case `int32`:
		return AsInt32(val)
	case `int64`:
		return AsInt64(val)
	case `uint8`:
		return AsUint8(val)
	case `uint16`:
		return AsUint16(val)
	case `uint`:
		return AsUint(val)
	case `uint32`:
		return AsUint32(val)
	case `uint64`:
		return AsUint64(val)
	default:
		return val
	}
}

func AsString(val interface{}) string {
	switch v := val.(type) {
	case string:
		return v
	case nil:
		return ``
	default:
		return fmt.Sprint(val)
	}
}

func AsBytes(val interface{}) []byte {
	switch v := val.(type) {
	case []byte:
		return v
	case nil:
		return nil
	case string:
		return []byte(v)
	default:
		var buf bytes.Buffer
		enc := gob.NewEncoder(&buf)
		err := enc.Encode(val)
		if err != nil {
			return nil
		}
		return buf.Bytes()
	}
}

func Split(val interface{}, sep string, limit ...int) StringSlice {
	str := AsString(val)
	if len(str) == 0 {
		return StringSlice{}
	}
	if len(limit) > 0 {
		return strings.SplitN(str, sep, limit[0])
	}
	return strings.Split(str, sep)
}

func Trim(val interface{}) String {
	return String(strings.TrimSpace(AsString(val)))
}

func AsHTML(val interface{}) template.HTML {
	switch v := val.(type) {
	case template.HTML:
		return v
	case string:
		return template.HTML(v)
	case nil:
		return EmptyHTML
	default:
		return template.HTML(fmt.Sprint(v))
	}
}

func AsHTMLAttr(val interface{}) template.HTMLAttr {
	switch v := val.(type) {
	case template.HTMLAttr:
		return v
	case string:
		return template.HTMLAttr(v)
	case nil:
		return EmptyHTMLAttr
	default:
		return template.HTMLAttr(fmt.Sprint(v))
	}
}

func AsJS(val interface{}) template.JS {
	switch v := val.(type) {
	case template.JS:
		return v
	case string:
		return template.JS(v)
	case nil:
		return EmptyJS
	default:
		return template.JS(fmt.Sprint(v))
	}
}

func AsCSS(val interface{}) template.CSS {
	switch v := val.(type) {
	case template.CSS:
		return v
	case string:
		return template.CSS(v)
	case nil:
		return EmptyCSS
	default:
		return template.CSS(fmt.Sprint(v))
	}
}

func AsBool(val interface{}) bool {
	switch v := val.(type) {
	case bool:
		return v
	case string:
		if len(v) > 0 {
			r, _ := strconv.ParseBool(v)
			return r
		}
		return false
	case nil:
		return false
	default:
		p := fmt.Sprint(v)
		if len(p) > 0 {
			r, _ := strconv.ParseBool(p)
			return r
		}
	}
	return false
}

func AsFloat64(val interface{}) float64 {
	switch v := val.(type) {
	case float64:
		return v
	case int64:
		return float64(v)
	case uint64:
		return float64(v)
	case float32:
		return float64(v)
	case int32:
		return float64(v)
	case uint32:
		return float64(v)
	case int:
		return float64(v)
	case uint:
		return float64(v)
	case string:
		i, _ := strconv.ParseFloat(v, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseFloat(s, 64)
		return i
	}
}

func AsFloat32(val interface{}) float32 {
	switch v := val.(type) {
	case float32:
		return v
	case int32:
		return float32(v)
	case uint32:
		return float32(v)
	case string:
		f, _ := strconv.ParseFloat(v, 32)
		return float32(f)
	case nil:
		return 0
	default:
		s := fmt.Sprint(val)
		f, _ := strconv.ParseFloat(s, 32)
		return float32(f)
	}
}

func AsInt8(val interface{}) int8 {
	switch v := val.(type) {
	case int8:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 8)
		return int8(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(val)
		i, _ := strconv.ParseInt(s, 10, 8)
		return int8(i)
	}
}

func AsInt16(val interface{}) int16 {
	switch v := val.(type) {
	case int16:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 16)
		return int16(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 16)
		return int16(i)
	}
}

func AsInt(val interface{}) int {
	switch v := val.(type) {
	case int:
		return v
	case string:
		i, _ := strconv.Atoi(v)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.Atoi(s)
		return i
	}
}

func AsInt32(val interface{}) int32 {
	switch v := val.(type) {
	case int32:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 32)
		return int32(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 32)
		return int32(i)
	}
}

func AsInt64(val interface{}) int64 {
	switch v := val.(type) {
	case int64:
		return v
	case int32:
		return int64(v)
	case uint32:
		return int64(v)
	case int:
		return int64(v)
	case uint:
		return int64(v)
	case string:
		i, _ := strconv.ParseInt(v, 10, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 64)
		return i
	}
}

func Decr(val interface{}, n int64) int64 {
	v, _ := val.(int64)
	v -= n
	return v
}

func Incr(val interface{}, n int64) int64 {
	v, _ := val.(int64)
	v += n
	return v
}

func AsUint8(val interface{}) uint8 {
	switch v := val.(type) {
	case uint8:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 8)
		return uint8(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 8)
		return uint8(i)
	}
}

func AsUint16(val interface{}) uint16 {
	switch v := val.(type) {
	case uint16:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 16)
		return uint16(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 16)
		return uint16(i)
	}
}

func AsUint(val interface{}) uint {
	switch v := val.(type) {
	case uint:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 32)
		return uint(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 32)
		return uint(i)
	}
}

func AsUint32(val interface{}) uint32 {
	switch v := val.(type) {
	case uint32:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 32)
		return uint32(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 32)
		return uint32(i)
	}
}

func AsUint64(val interface{}) uint64 {
	switch v := val.(type) {
	case uint64:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 64)
		return i
	}
}

func AsTimestamp(val interface{}) time.Time {
	p := AsString(val)
	if len(p) > 0 {
		s := strings.SplitN(p, `.`, 2)
		var sec int64
		var nsec int64
		switch len(s) {
		case 2:
			nsec = String(s[1]).Int64()
			fallthrough
		case 1:
			sec = String(s[0]).Int64()
		}
		return time.Unix(sec, nsec)
	}
	return emptyTime
}

func AsDateTime(val interface{}, layouts ...string) time.Time {
	p := AsString(val)
	if len(p) > 0 {
		layout := DateTimeLayout
		if len(layouts) > 0 {
			layout = layouts[0]
		}
		t, _ := time.Parse(layout, p)
		return t
	}
	return emptyTime
}
<commit_msg>update<commit_after>package param

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"html/template"
	"strconv"
	"strings"
	"time"
)

const (
	EmptyHTML      = template.HTML(``)
	EmptyJS        = template.JS(``)
	EmptyCSS       = template.CSS(``)
	EmptyHTMLAttr  = template.HTMLAttr(``)
	DateTimeLayout = `2006-01-02 15:04:05`
	DateTimeShort  = `2006-01-02 15:04`
	DateLayout     = `2006-01-02`
	TimeLayout     = `15:04:05`
	DateMd         = `01-02`
	DateShort      = `06-01-02`
	TimeShort      = `15:04`
)

func AsType(typ string, val interface{}) interface{} {
	switch typ {
	case `string`:
		return AsString(val)
	case `bytes`, `[]byte`:
		return AsBytes(val)
	case `bool`:
		return AsBool(val)
	case `float64`:
		return AsFloat64(val)
	case `float32`:
		return AsFloat32(val)
	case `int8`:
		return AsInt8(val)
	case `int16`:
		return AsInt16(val)
	case `int`:
		return AsInt(val)
	case `int32`:
		return AsInt32(val)
	case `int64`:
		return AsInt64(val)
	case `uint8`:
		return AsUint8(val)
	case `uint16`:
		return AsUint16(val)
	case `uint`:
		return AsUint(val)
	case `uint32`:
		return AsUint32(val)
	case `uint64`:
		return AsUint64(val)
	default:
		return val
	}
}

func AsString(val interface{}) string {
	switch v := val.(type) {
	case string:
		return v
	case nil:
		return ``
	default:
		return fmt.Sprint(val)
	}
}

func AsBytes(val interface{}) []byte {
	switch v := val.(type) {
	case []byte:
		return v
	case nil:
		return nil
	case string:
		return []byte(v)
	default:
		var buf bytes.Buffer
		enc := gob.NewEncoder(&buf)
		err := enc.Encode(val)
		if err != nil {
			return nil
		}
		return buf.Bytes()
	}
}

func Split(val interface{}, sep string, limit ...int) StringSlice {
	str := AsString(val)
	if len(str) == 0 {
		return StringSlice{}
	}
	if len(limit) > 0 {
		return strings.SplitN(str, sep, limit[0])
	}
	return strings.Split(str, sep)
}

func Trim(val interface{}) String {
	return String(strings.TrimSpace(AsString(val)))
}

func AsHTML(val interface{}) template.HTML {
	switch v := val.(type) {
	case template.HTML:
		return v
	case string:
		return template.HTML(v)
	case nil:
		return EmptyHTML
	default:
		return template.HTML(fmt.Sprint(v))
	}
}

func AsHTMLAttr(val interface{}) template.HTMLAttr {
	switch v := val.(type) {
	case template.HTMLAttr:
		return v
	case string:
		return template.HTMLAttr(v)
	case nil:
		return EmptyHTMLAttr
	default:
		return template.HTMLAttr(fmt.Sprint(v))
	}
}

func AsJS(val interface{}) template.JS {
	switch v := val.(type) {
	case template.JS:
		return v
	case string:
		return template.JS(v)
	case nil:
		return EmptyJS
	default:
		return template.JS(fmt.Sprint(v))
	}
}

func AsCSS(val interface{}) template.CSS {
	switch v := val.(type) {
	case template.CSS:
		return v
	case string:
		return template.CSS(v)
	case nil:
		return EmptyCSS
	default:
		return template.CSS(fmt.Sprint(v))
	}
}

func AsBool(val interface{}) bool {
	switch v := val.(type) {
	case bool:
		return v
	case string:
		if len(v) > 0 {
			r, _ := strconv.ParseBool(v)
			return r
		}
		return false
	case nil:
		return false
	default:
		p := fmt.Sprint(v)
		if len(p) > 0 {
			r, _ := strconv.ParseBool(p)
			return r
		}
	}
	return false
}

func AsFloat64(val interface{}) float64 {
	switch v := val.(type) {
	case float64:
		return v
	case int64:
		return float64(v)
	case uint64:
		return float64(v)
	case float32:
		return float64(v)
	case int32:
		return float64(v)
	case uint32:
		return float64(v)
	case int:
		return float64(v)
	case uint:
		return float64(v)
	case string:
		i, _ := strconv.ParseFloat(v, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseFloat(s, 64)
		return i
	}
}

func AsFloat32(val interface{}) float32 {
	switch v := val.(type) {
	case float32:
		return v
	case int32:
		return float32(v)
	case uint32:
		return float32(v)
	case string:
		f, _ := strconv.ParseFloat(v, 32)
		return float32(f)
	case nil:
		return 0
	default:
		s := fmt.Sprint(val)
		f, _ := strconv.ParseFloat(s, 32)
		return float32(f)
	}
}

func AsInt8(val interface{}) int8 {
	switch v := val.(type) {
	case int8:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 8)
		return int8(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(val)
		i, _ := strconv.ParseInt(s, 10, 8)
		return int8(i)
	}
}

func AsInt16(val interface{}) int16 {
	switch v := val.(type) {
	case int16:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 16)
		return int16(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 16)
		return int16(i)
	}
}

func AsInt(val interface{}) int {
	switch v := val.(type) {
	case int:
		return v
	case string:
		i, _ := strconv.Atoi(v)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.Atoi(s)
		return i
	}
}

func AsInt32(val interface{}) int32 {
	switch v := val.(type) {
	case int32:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 32)
		return int32(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 32)
		return int32(i)
	}
}

func AsInt64(val interface{}) int64 {
	switch v := val.(type) {
	case int64:
		return v
	case int32:
		return int64(v)
	case uint32:
		return int64(v)
	case int:
		return int64(v)
	case uint:
		return int64(v)
	case string:
		i, _ := strconv.ParseInt(v, 10, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 64)
		return i
	}
}

func Decr(val interface{}, n int64) int64 {
	v, _ := val.(int64)
	v -= n
	return v
}

func Incr(val interface{}, n int64) int64 {
	v, _ := val.(int64)
	v += n
	return v
}

func AsUint8(val interface{}) uint8 {
	switch v := val.(type) {
	case uint8:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 8)
		return uint8(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 8)
		return uint8(i)
	}
}

func AsUint16(val interface{}) uint16 {
	switch v := val.(type) {
	case uint16:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 16)
		return uint16(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 16)
		return uint16(i)
	}
}

func AsUint(val interface{}) uint {
	switch v := val.(type) {
	case uint:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 32)
		return uint(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 32)
		return uint(i)
	}
}

func AsUint32(val interface{}) uint32 {
	switch v := val.(type) {
	case uint32:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 32)
		return uint32(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 32)
		return uint32(i)
	}
}

func AsUint64(val interface{}) uint64 {
	switch v := val.(type) {
	case uint64:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 64)
		return i
	}
}

func AsTimestamp(val interface{}) time.Time {
	p := AsString(val)
	if len(p) > 0 {
		s := strings.SplitN(p, `.`, 2)
		var sec int64
		var nsec int64
		switch len(s) {
		case 2:
			nsec = String(s[1]).Int64()
			fallthrough
		case 1:
			sec = String(s[0]).Int64()
		}
		return time.Unix(sec, nsec)
	}
	return emptyTime
}

func AsDateTime(val interface{}, layouts ...string) time.Time {
	p := AsString(val)
	if len(p) > 0 {
		layout := DateTimeLayout
		if len(layouts) > 0 {
			layout = layouts[0]
		}
		t, _ := time.Parse(layout, p)
		return t
	}
	return emptyTime
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package storage

import (
	"database/sql"
	"fmt"
	"log"
	"strings"

	"github.com/adampresley/golangdb"
	"github.com/mailslurper/libmailslurper/model/attachment"
	"github.com/mailslurper/libmailslurper/model/mailitem"
	"github.com/mailslurper/libmailslurper/sanitization"
)

/*
Creates a global connection handle in a map named "lib".
*/
func ConnectToStorage(connectionInfo *golangdb.DatabaseConnection) error {
	var err error

	log.Println("libmailslurper: IFNO - Connecting to database", connectionInfo.Database)

	err = connectionInfo.Connect("lib")
	if err != nil {
		return err
	}

	switch connectionInfo.Engine {
	case golangdb.SQLITE:
		CreateSqlliteDatabase()
	}

	return nil
}

/*
DeleteMails deletes a set of email and their attachments. If the optional start/end
dates are not empty strings then the delete is date-filtered.
*/
func DeleteMails(startDate, endDate string) error {
	where := ""
	var err error

	if len(startDate) > 0 && len(endDate) > 0 {
		where = where + " AND dateSent >= ? "
		where = where + " AND dateSend <= ? "
	}

	sql := "DELETE FROM mailitem WHERE 1=1" + where

	if len(where) > 0 {
		_, err = golangdb.Db["lib"].Exec(sql)
	} else {
		_, err = golangdb.Db["lib"].Exec(sql, startDate, endDate)
	}

	return err
}

/*
Disconnects from the database storage
*/
func DisconnectFromStorage() {
	golangdb.Db["lib"].Close()
}

/*
Returns an attachment by ID
*/
func GetAttachment(mailId, attachmentId string) (attachment.Attachment, error) {
	result := attachment.Attachment{}

	rows, err := golangdb.Db["lib"].Query(`
		SELECT
			  fileName TEXT
			, contentType TEXT
			, content TEXT
		FROM attachment
		WHERE
			id=?
			AND mailItemId=?
	`, attachmentId, mailId)

	if err != nil {
		return result, fmt.Errorf("Error running query to get attachment")
	}

	defer rows.Close()
	rows.Next()

	var fileName string
	var contentType string
	var content string

	rows.Scan(&fileName, &contentType, &content)

	result.Headers = &attachment.AttachmentHeader{
		FileName:    fileName,
		ContentType: contentType,
	}

	result.Contents = content
	return result, nil
}

func getMailQuery(whereClause string) string {
	sql := `
		SELECT
			  mailitem.id AS mailItemId
			, mailitem.dateSent
			, mailitem.fromAddress
			, mailitem.toAddressList
			, mailitem.subject
			, mailitem.xmailer
			, mailitem.body
			, mailitem.contentType
			, mailitem.boundary

		FROM mailitem

		WHERE 1=1 `

	sql = sql + whereClause
	sql = sql + ` ORDER BY mailitem.dateSent DESC `

	return sql
}

/*
Returns a single mail item by ID.
*/
func GetMail(id string) (mailitem.MailItem, error) {
	result := mailitem.MailItem{}
	xssService := sanitization.NewXSSService()

	sql := getMailQuery(" AND mailitem.id=? ")
	rows, err := golangdb.Db["lib"].Query(sql, id)

	if err != nil {
		return result, fmt.Errorf("Error running query to get mail items: %s", err)
	}

	rows.Next()

	var mailItemId string
	var dateSent string
	var fromAddress string
	var toAddressList string
	var subject string
	var xmailer string
	var body string
	var contentType string
	var boundary string

	rows.Scan(&mailItemId, &dateSent, &fromAddress, &toAddressList, &subject, &xmailer, &body, &contentType, &boundary)

	result = mailitem.MailItem{
		Id:          mailItemId,
		DateSent:    dateSent,
		FromAddress: fromAddress,
		ToAddresses: strings.Split(toAddressList, "; "),
		Subject:     xssService.SanitizeString(subject),
		XMailer:     xssService.SanitizeString(xmailer),
		Body:        xssService.SanitizeString(body),
		ContentType: contentType,
		Boundary:    boundary,
	}

	/*
	 * Get attachments
	 */
	sql = `
		SELECT
			  attachment.id AS attachmentId
			, attachment.fileName
			, attachment.contentType

		FROM attachment
		WHERE
			attachment.mailItemId=?`

	attachmentRows, err := golangdb.Db["lib"].Query(sql, mailItemId)
	if err != nil {
		return result, err
	}

	attachments := make([]*attachment.Attachment, 0)

	for attachmentRows.Next() {
		var attachmentId string
		var fileName string
		var contentType string

		attachmentRows.Scan(&attachmentId, &fileName, &contentType)

		newAttachment := &attachment.Attachment{
			Id:     attachmentId,
			MailId: mailItemId,
			Headers: &attachment.AttachmentHeader{
				FileName:    xssService.SanitizeString(fileName),
				ContentType: contentType,
			},
		}

		attachments = append(attachments, newAttachment)
	}

	attachmentRows.Close()

	result.Attachments = attachments

	rows.Close()
	return result, nil
}

/*
Retrieves all stored mail items as an array of MailItem items. Only
returns rows starting at offset and gets up to length records. NOTE:
This code stinks. It gets ALL rows, then returns a slice and the total number
of mail items. Ick!
*/
func GetMailCollection(offset, length int) ([]mailitem.MailItem, int, error) {
	result := make([]mailitem.MailItem, 0)
	xssService := sanitization.NewXSSService()

	sql := getMailQuery("")
	rows, err := golangdb.Db["lib"].Query(sql)

	if err != nil {
		return result, 0, fmt.Errorf("Error running query to get mail items: %s", err)
	}

	/*
	 * Loop over our records and grab attachments on the way.
	 */
	for rows.Next() {
		var mailItemId string
		var dateSent string
		var fromAddress string
		var toAddressList string
		var subject string
		var xmailer string
		var body string
		var contentType string
		var boundary string

		rows.Scan(&mailItemId, &dateSent, &fromAddress, &toAddressList, &subject, &xmailer, &body, &contentType, &boundary)

		newItem := mailitem.MailItem{
			Id:          mailItemId,
			DateSent:    dateSent,
			FromAddress: fromAddress,
			ToAddresses: strings.Split(toAddressList, "; "),
			Subject:     xssService.SanitizeString(subject),
			XMailer:     xssService.SanitizeString(xmailer),
			Body:        xssService.SanitizeString(body),
			ContentType: contentType,
			Boundary:    boundary,
		}

		/*
		 * Get attachments
		 */
		sql = `
			SELECT
				  attachment.id AS attachmentId
				, attachment.fileName
			FROM attachment
			WHERE
				attachment.mailItemId=?`

		attachmentRows, err := golangdb.Db["lib"].Query(sql, mailItemId)
		if err != nil {
			return result, 0, err
		}

		attachments := make([]*attachment.Attachment, 0)

		for attachmentRows.Next() {
			var attachmentId string
			var fileName string

			attachmentRows.Scan(&attachmentId, &fileName)

			newAttachment := &attachment.Attachment{
				Id:      attachmentId,
				MailId:  mailItemId,
				Headers: &attachment.AttachmentHeader{FileName: xssService.SanitizeString(fileName)},
			}

			attachments = append(attachments, newAttachment)
		}

		attachmentRows.Close()

		newItem.Attachments = attachments
		result = append(result, newItem)
	}

	rows.Close()

	totalRecords := len(result)
	return result, totalRecords, nil
}

/*
GetMailCount returns a count of mail items in the storage system.
*/
func GetMailCount() (int, error) {
	var mailItemCount int
	var err error

	err = golangdb.Db["lib"].QueryRow(`SELECT COUNT(id) AS mailItemCount FROM mailitem`).Scan(&mailItemCount)
	if err != nil {
		return 0, fmt.Errorf("Error running query to get mail item count: %s", err)
	}

	return mailItemCount, nil
}

func storeAttachments(mailItemId string, transaction *sql.Tx, attachments []*attachment.Attachment) error {
	for _, a := range attachments {
		attachmentId, err := mailitem.GenerateId()
		if err != nil {
			return fmt.Errorf("Error generating ID for attachment: %s", err)
		}

		statement, err := transaction.Prepare(`
			INSERT INTO attachment (
				  id
				, mailItemId
				, fileName
				, contentType
				, content
			) VALUES (
				  ?
				, ?
				, ?
				, ?
				, ?
			)
		`)

		if err != nil {
			return fmt.Errorf("Error preparing insert attachment statement: %s", err)
		}

		_, err = statement.Exec(
			attachmentId,
			mailItemId,
			a.Headers.FileName,
			a.Headers.ContentType,
			a.Contents,
		)

		if err != nil {
			return fmt.Errorf("Error executing insert attachment in StoreMail: %s", err)
		}

		statement.Close()
		a.Id = attachmentId
	}

	return nil
}

func StoreMail(mailItem *mailitem.MailItem) (string, error) {
	/*
	 * Create a transaction and insert the new mail item
	 */
	transaction, err := golangdb.Db["lib"].Begin()
	if err != nil {
		return "", fmt.Errorf("Error starting transaction in StoreMail: %s", err)
	}

	/*
	 * Insert the mail item
	 */
	statement, err := transaction.Prepare(`
			INSERT INTO mailitem (
				  id
				, dateSent
				, fromAddress
				, toAddressList
				, subject
				, xmailer
				, body
				, contentType
				, boundary
			) VALUES (
				  ?
				, ?
				, ?
				, ?
				, ?
				, ?
				, ?
				, ?
				, ?
			)
		`)

	if err != nil {
		return "", fmt.Errorf("Error preparing insert statement for mail item in StoreMail: %s", err)
	}

	_, err = statement.Exec(
		mailItem.Id,
		mailItem.DateSent,
		mailItem.FromAddress,
		strings.Join(mailItem.ToAddresses, "; "),
		mailItem.Subject,
		mailItem.XMailer,
		mailItem.Body,
		mailItem.ContentType,
		mailItem.Boundary,
	)

	if err != nil {
		transaction.Rollback()
		return "", fmt.Errorf("Error executing insert for mail item in StoreMail: %s", err)
	}

	statement.Close()

	/*
	 * Insert attachments
	 */
	if err = storeAttachments(mailItem.Id, transaction, mailItem.Attachments); err != nil {
		transaction.Rollback()
		return "", fmt.Errorf("Unable to insert attachments in StoreMail: %s", err)
	}

	transaction.Commit()
	log.Printf("New mail item written to database.\n\n")

	return mailItem.Id, nil
}
<commit_msg>Removed need for end date. Wasn't using anyway. Also fixed logic flaw in date calc.<commit_after>// Copyright 2013-2014 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package storage

import (
	"database/sql"
	"fmt"
	"log"
	"strings"

	"github.com/adampresley/golangdb"
	"github.com/mailslurper/libmailslurper/model/attachment"
	"github.com/mailslurper/libmailslurper/model/mailitem"
	"github.com/mailslurper/libmailslurper/sanitization"
)

/*
Creates a global connection handle in a map named "lib".
*/
func ConnectToStorage(connectionInfo *golangdb.DatabaseConnection) error {
	var err error

	log.Println("libmailslurper: IFNO - Connecting to database", connectionInfo.Database)

	err = connectionInfo.Connect("lib")
	if err != nil {
		return err
	}

	switch connectionInfo.Engine {
	case golangdb.SQLITE:
		CreateSqlliteDatabase()
	}

	return nil
}

/*
DeleteMails deletes a set of email and their attachments. If the optional start/end
dates are not empty strings then the delete is date-filtered.
*/
func DeleteMails(startDate string) error {
	where := ""
	parameters := []interface{}{}
	var err error

	if len(startDate) > 0 {
		where = where + " AND dateSent <= ? "
		parameters = append(parameters, startDate)
	}

	sql := "DELETE FROM mailitem WHERE 1=1" + where
	_, err = golangdb.Db["lib"].Exec(sql, parameters...)
	return err
}

/*
Disconnects from the database storage
*/
func DisconnectFromStorage() {
	golangdb.Db["lib"].Close()
}

/*
Returns an attachment by ID
*/
func GetAttachment(mailId, attachmentId string) (attachment.Attachment, error) {
	result := attachment.Attachment{}

	rows, err := golangdb.Db["lib"].Query(`
		SELECT
			  fileName TEXT
			, contentType TEXT
			, content TEXT
		FROM attachment
		WHERE
			id=?
			AND mailItemId=?
	`, attachmentId, mailId)

	if err != nil {
		return result, fmt.Errorf("Error running query to get attachment")
	}

	defer rows.Close()
	rows.Next()

	var fileName string
	var contentType string
	var content string

	rows.Scan(&fileName, &contentType, &content)

	result.Headers = &attachment.AttachmentHeader{
		FileName:    fileName,
		ContentType: contentType,
	}

	result.Contents = content
	return result, nil
}

func getMailQuery(whereClause string) string {
	sql := `
		SELECT
			  mailitem.id AS mailItemId
			, mailitem.dateSent
			, mailitem.fromAddress
			, mailitem.toAddressList
			, mailitem.subject
			, mailitem.xmailer
			, mailitem.body
			, mailitem.contentType
			, mailitem.boundary

		FROM mailitem

		WHERE 1=1 `

	sql = sql + whereClause
	sql = sql + ` ORDER BY mailitem.dateSent DESC `

	return sql
}

/*
Returns a single mail item by ID.
*/
func GetMail(id string) (mailitem.MailItem, error) {
	result := mailitem.MailItem{}
	xssService := sanitization.NewXSSService()

	sql := getMailQuery(" AND mailitem.id=? ")
	rows, err := golangdb.Db["lib"].Query(sql, id)

	if err != nil {
		return result, fmt.Errorf("Error running query to get mail items: %s", err)
	}

	rows.Next()

	var mailItemId string
	var dateSent string
	var fromAddress string
	var toAddressList string
	var subject string
	var xmailer string
	var body string
	var contentType string
	var boundary string

	rows.Scan(&mailItemId, &dateSent, &fromAddress, &toAddressList, &subject, &xmailer, &body, &contentType, &boundary)

	result = mailitem.MailItem{
		Id:          mailItemId,
		DateSent:    dateSent,
		FromAddress: fromAddress,
		ToAddresses: strings.Split(toAddressList, "; "),
		Subject:     xssService.SanitizeString(subject),
		XMailer:     xssService.SanitizeString(xmailer),
		Body:        xssService.SanitizeString(body),
		ContentType: contentType,
		Boundary:    boundary,
	}

	/*
	 * Get attachments
	 */
	sql = `
		SELECT
			  attachment.id AS attachmentId
			, attachment.fileName
			, attachment.contentType

		FROM attachment
		WHERE
			attachment.mailItemId=?`

	attachmentRows, err := golangdb.Db["lib"].Query(sql, mailItemId)
	if err != nil {
		return result, err
	}

	attachments := make([]*attachment.Attachment, 0)

	for attachmentRows.Next() {
		var attachmentId string
		var fileName string
		var contentType string

		attachmentRows.Scan(&attachmentId, &fileName, &contentType)

		newAttachment := &attachment.Attachment{
			Id:     attachmentId,
			MailId: mailItemId,
			Headers: &attachment.AttachmentHeader{
				FileName:    xssService.SanitizeString(fileName),
				ContentType: contentType,
			},
		}

		attachments = append(attachments, newAttachment)
	}

	attachmentRows.Close()

	result.Attachments = attachments

	rows.Close()
	return result, nil
}

/*
Retrieves all stored mail items as an array of MailItem items. Only
returns rows starting at offset and gets up to length records. NOTE:
This code stinks. It gets ALL rows, then returns a slice and the total number
of mail items. Ick!
*/
func GetMailCollection(offset, length int) ([]mailitem.MailItem, int, error) {
	result := make([]mailitem.MailItem, 0)
	xssService := sanitization.NewXSSService()

	sql := getMailQuery("")
	rows, err := golangdb.Db["lib"].Query(sql)

	if err != nil {
		return result, 0, fmt.Errorf("Error running query to get mail items: %s", err)
	}

	/*
	 * Loop over our records and grab attachments on the way.
	 */
	for rows.Next() {
		var mailItemId string
		var dateSent string
		var fromAddress string
		var toAddressList string
		var subject string
		var xmailer string
		var body string
		var contentType string
		var boundary string

		rows.Scan(&mailItemId, &dateSent, &fromAddress, &toAddressList, &subject, &xmailer, &body, &contentType, &boundary)

		newItem := mailitem.MailItem{
			Id:          mailItemId,
			DateSent:    dateSent,
			FromAddress: fromAddress,
			ToAddresses: strings.Split(toAddressList, "; "),
			Subject:     xssService.SanitizeString(subject),
			XMailer:     xssService.SanitizeString(xmailer),
			Body:        xssService.SanitizeString(body),
			ContentType: contentType,
			Boundary:    boundary,
		}

		/*
		 * Get attachments
		 */
		sql = `
			SELECT
				  attachment.id AS attachmentId
				, attachment.fileName
			FROM attachment
			WHERE
				attachment.mailItemId=?`

		attachmentRows, err := golangdb.Db["lib"].Query(sql, mailItemId)
		if err != nil {
			return result, 0, err
		}

		attachments := make([]*attachment.Attachment, 0)

		for attachmentRows.Next() {
			var attachmentId string
			var fileName string

			attachmentRows.Scan(&attachmentId, &fileName)

			newAttachment := &attachment.Attachment{
				Id:      attachmentId,
				MailId:  mailItemId,
				Headers: &attachment.AttachmentHeader{FileName: xssService.SanitizeString(fileName)},
			}

			attachments = append(attachments, newAttachment)
		}

		attachmentRows.Close()

		newItem.Attachments = attachments
		result = append(result, newItem)
	}

	rows.Close()

	totalRecords := len(result)
	return result, totalRecords, nil
}

/*
GetMailCount returns a count of mail items in the storage system.
*/
func GetMailCount() (int, error) {
	var mailItemCount int
	var err error

	err = golangdb.Db["lib"].QueryRow(`SELECT COUNT(id) AS mailItemCount FROM mailitem`).Scan(&mailItemCount)
	if err != nil {
		return 0, fmt.Errorf("Error running query to get mail item count: %s", err)
	}

	return mailItemCount, nil
}

func storeAttachments(mailItemId string, transaction *sql.Tx, attachments []*attachment.Attachment) error {
	for _, a := range attachments {
		attachmentId, err := mailitem.GenerateId()
		if err != nil {
			return fmt.Errorf("Error generating ID for attachment: %s", err)
		}

		statement, err := transaction.Prepare(`
			INSERT INTO attachment (
				  id
				, mailItemId
				, fileName
				, contentType
				, content
			) VALUES (
				  ?
				, ?
				, ?
				, ?
				, ?
			)
		`)

		if err != nil {
			return fmt.Errorf("Error preparing insert attachment statement: %s", err)
		}

		_, err = statement.Exec(
			attachmentId,
			mailItemId,
			a.Headers.FileName,
			a.Headers.ContentType,
			a.Contents,
		)

		if err != nil {
			return fmt.Errorf("Error executing insert attachment in StoreMail: %s", err)
		}

		statement.Close()
		a.Id = attachmentId
	}

	return nil
}

func StoreMail(mailItem *mailitem.MailItem) (string, error) {
	/*
	 * Create a transaction and insert the new mail item
	 */
	transaction, err := golangdb.Db["lib"].Begin()
	if err != nil {
		return "", fmt.Errorf("Error starting transaction in StoreMail: %s", err)
	}

	/*
	 * Insert the mail item
	 */
	statement, err := transaction.Prepare(`
			INSERT INTO mailitem (
				  id
				, dateSent
				, fromAddress
				, toAddressList
				, subject
				, xmailer
				, body
				, contentType
				, boundary
			) VALUES (
				  ?
				, ?
				, ?
				, ?
				, ?
				, ?
				, ?
				, ?
				, ?
			)
		`)

	if err != nil {
		return "", fmt.Errorf("Error preparing insert statement for mail item in StoreMail: %s", err)
	}

	_, err = statement.Exec(
		mailItem.Id,
		mailItem.DateSent,
		mailItem.FromAddress,
		strings.Join(mailItem.ToAddresses, "; "),
		mailItem.Subject,
		mailItem.XMailer,
		mailItem.Body,
		mailItem.ContentType,
		mailItem.Boundary,
	)

	if err != nil {
		transaction.Rollback()
		return "", fmt.Errorf("Error executing insert for mail item in StoreMail: %s", err)
	}

	statement.Close()

	/*
	 * Insert attachments
	 */
	if err = storeAttachments(mailItem.Id, transaction, mailItem.Attachments); err != nil {
		transaction.Rollback()
		return "", fmt.Errorf("Unable to insert attachments in StoreMail: %s", err)
	}

	transaction.Commit()
	log.Printf("New mail item written to database.\n\n")

	return mailItem.Id, nil
}
<|endoftext|>


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

// Package storage is a Google Cloud Storage client.
package storage

import (
	"errors"
	"fmt"
	"io"
	"net/http"

	raw "code.google.com/p/google-api-go-client/storage/v1"
)

const (
	// ScopeFullControl grants permissions to manage your
	// data and permissions in Google Cloud Storage.
	ScopeFullControl = raw.DevstorageFull_controlScope

	// ScopeReadOnly grants permissions to
	// view your data in Google Cloud Storage.
	ScopeReadOnly = raw.DevstorageRead_onlyScope

	// ScopeReadWrite grants permissions to manage your
	// data in Google Cloud Storage.
	ScopeReadWrite = raw.DevstorageRead_writeScope
)

const (
	templUrlMedia = "https://storage.googleapis.com/%s/%s"
)

// Bucket represents a Google Cloud Storage bucket.
type Bucket struct {
	// Name is the name of the bucket.
	Name string `json:"name,omitempty"`
}

type conn struct {
	c *http.Client
	s *raw.Service
}

// BucketClient is a client to perform object operations on.
type BucketClient struct {
	name string
	conn *conn
}

// String returns a string representation of the bucket client.
// E.g. <bucket: my-project-bucket>
func (b *BucketClient) String() string {
	return fmt.Sprintf("<bucket: %v>", b.name)
}

// Client represents a Google Cloud Storage client.
type Client struct {
	conn *conn
}

// New returns a new Google Cloud Storage client. The provided
// RoundTripper should be authorized and authenticated to make
// calls to Google Cloud Storage API.
func New(tr http.RoundTripper) *Client {
	return NewWithClient(&http.Client{Transport: tr})
}

// NewWithClient returns a new Google Cloud Storage client that
// uses the provided http.Client. Provided http.Client is responsible
// to authorize and authenticate the requests made to the
// Google Cloud Storage API.
func NewWithClient(c *http.Client) *Client {
	s, _ := raw.New(c)
	return &Client{conn: &conn{s: s, c: c}}
}

// TODO(jbd): Add storage.buckets.list.
// TODO(jbd): Add storage.buckets.insert.
// TODO(jbd): Add storage.buckets.update.
// TODO(jbd): Add storage.buckets.delete.

// TODO(jbd): Add storage.objects.watch.

// Bucket returns the metadata for the specified bucket.
func (c *Client) Bucket(name string) (*Bucket, error) {
	panic("not yet implemented")
}

// BucketClient returns a bucket client to perform object operations on.
func (c *Client) BucketClient(bucketname string) *BucketClient {
	return &BucketClient{name: bucketname, conn: c.conn}
}

// List lists objects from the bucket. You can specify a query
// to filter the results. If q is nil, no filtering is applied.
func (b *BucketClient) List(q *Query) (*Objects, error) {
	c := b.conn.s.Objects.List(b.name)
	if q != nil {
		c.Delimiter(q.Delimiter)
		c.Prefix(q.Prefix)
		c.Versions(q.Versions)
		c.PageToken(q.Cursor)
		if q.MaxResults > 0 {
			c.MaxResults(int64(q.MaxResults))
		}
	}
	resp, err := c.Do()
	if err != nil {
		return nil, err
	}
	objects := &Objects{
		Results:  make([]*Object, len(resp.Items)),
		Prefixes: make([]string, len(resp.Prefixes)),
	}
	for i, item := range resp.Items {
		objects.Results[i] = newObject(item)
	}
	for i, prefix := range resp.Prefixes {
		objects.Prefixes[i] = prefix
	}
	if resp.NextPageToken != "" {
		next := Query{}
		if q != nil {
			// keep the other filtering
			// criteria if there is a query
			next = *q
		}
		next.Cursor = resp.NextPageToken
		objects.Next = &next
	}
	return objects, nil
}

// Stat returns meta information about the specified object.
func (b *BucketClient) Stat(name string) (*Object, error) {
	o, err := b.conn.s.Objects.Get(b.name, name).Do()
	if err != nil {
		return nil, err
	}
	return newObject(o), nil
}

// Put inserts/updates an object with the provided meta information.
func (b *BucketClient) Put(name string, info *Object) (*Object, error) {
	o, err := b.conn.s.Objects.Insert(b.name, info.toRawObject()).Do()
	if err != nil {
		return nil, err
	}
	return newObject(o), nil
}

// Delete deletes the specified object.
func (b *BucketClient) Delete(name string) error {
	return b.conn.s.Objects.Delete(b.name, name).Do()
}

// Copy copies the source object to the destination with the new
// meta information provided.
// The destination object is inserted into the source bucket
// if the destination object doesn't specify another bucket name.
func (b *BucketClient) Copy(name string, dest *Object) (*Object, error) {
	if dest.Name == "" {
		return nil, errors.New("storage: missing dest name")
	}
	destBucket := dest.Bucket
	if destBucket == "" {
		destBucket = b.name
	}
	o, err := b.conn.s.Objects.Copy(
		b.name, name, destBucket, dest.Name, dest.toRawObject()).Do()
	if err != nil {
		return nil, err
	}
	return newObject(o), nil
}

// NewReader creates a new io.ReadCloser to read the contents
// of the object.
func (b *BucketClient) NewReader(name string) (io.ReadCloser, error) {
	resp, err := b.conn.c.Get(fmt.Sprintf(templUrlMedia, b.name, name))
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

// NewWriter returns a new ObjectWriter to write to the GCS object
// identified by the specified object name.
// If such object doesn't exist, it creates one. If info is not nil,
// write operation also modifies the meta information of the object.
// All read-only fields are ignored during metadata updates.
func (b *BucketClient) NewWriter(name string, info *Object) *ObjectWriter {
	i := Object{}
	if info != nil {
		i = *info
	}
	i.Bucket = b.name
	i.Name = name
	return newObjectWriter(b.conn, &i)
}
<commit_msg>Copy API call requires that dest.Bucket is set.<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package storage is a Google Cloud Storage client.
package storage

import (
	"errors"
	"fmt"
	"io"
	"net/http"

	raw "code.google.com/p/google-api-go-client/storage/v1"
)

const (
	// ScopeFullControl grants permissions to manage your
	// data and permissions in Google Cloud Storage.
	ScopeFullControl = raw.DevstorageFull_controlScope

	// ScopeReadOnly grants permissions to
	// view your data in Google Cloud Storage.
	ScopeReadOnly = raw.DevstorageRead_onlyScope

	// ScopeReadWrite grants permissions to manage your
	// data in Google Cloud Storage.
	ScopeReadWrite = raw.DevstorageRead_writeScope
)

const (
	templUrlMedia = "https://storage.googleapis.com/%s/%s"
)

// Bucket represents a Google Cloud Storage bucket.
type Bucket struct {
	// Name is the name of the bucket.
	Name string `json:"name,omitempty"`
}

type conn struct {
	c *http.Client
	s *raw.Service
}

// BucketClient is a client to perform object operations on.
type BucketClient struct {
	name string
	conn *conn
}

// String returns a string representation of the bucket client.
// E.g. <bucket: my-project-bucket>
func (b *BucketClient) String() string {
	return fmt.Sprintf("<bucket: %v>", b.name)
}

// Client represents a Google Cloud Storage client.
type Client struct {
	conn *conn
}

// New returns a new Google Cloud Storage client. The provided
// RoundTripper should be authorized and authenticated to make
// calls to Google Cloud Storage API.
func New(tr http.RoundTripper) *Client {
	return NewWithClient(&http.Client{Transport: tr})
}

// NewWithClient returns a new Google Cloud Storage client that
// uses the provided http.Client. Provided http.Client is responsible
// to authorize and authenticate the requests made to the
// Google Cloud Storage API.
func NewWithClient(c *http.Client) *Client {
	s, _ := raw.New(c)
	return &Client{conn: &conn{s: s, c: c}}
}

// TODO(jbd): Add storage.buckets.list.
// TODO(jbd): Add storage.buckets.insert.
// TODO(jbd): Add storage.buckets.update.
// TODO(jbd): Add storage.buckets.delete.

// TODO(jbd): Add storage.objects.watch.

// Bucket returns the metadata for the specified bucket.
func (c *Client) Bucket(name string) (*Bucket, error) {
	panic("not yet implemented")
}

// BucketClient returns a bucket client to perform object operations on.
func (c *Client) BucketClient(bucketname string) *BucketClient {
	return &BucketClient{name: bucketname, conn: c.conn}
}

// List lists objects from the bucket. You can specify a query
// to filter the results. If q is nil, no filtering is applied.
func (b *BucketClient) List(q *Query) (*Objects, error) {
	c := b.conn.s.Objects.List(b.name)
	if q != nil {
		c.Delimiter(q.Delimiter)
		c.Prefix(q.Prefix)
		c.Versions(q.Versions)
		c.PageToken(q.Cursor)
		if q.MaxResults > 0 {
			c.MaxResults(int64(q.MaxResults))
		}
	}
	resp, err := c.Do()
	if err != nil {
		return nil, err
	}
	objects := &Objects{
		Results:  make([]*Object, len(resp.Items)),
		Prefixes: make([]string, len(resp.Prefixes)),
	}
	for i, item := range resp.Items {
		objects.Results[i] = newObject(item)
	}
	for i, prefix := range resp.Prefixes {
		objects.Prefixes[i] = prefix
	}
	if resp.NextPageToken != "" {
		next := Query{}
		if q != nil {
			// keep the other filtering
			// criteria if there is a query
			next = *q
		}
		next.Cursor = resp.NextPageToken
		objects.Next = &next
	}
	return objects, nil
}

// Stat returns meta information about the specified object.
func (b *BucketClient) Stat(name string) (*Object, error) {
	o, err := b.conn.s.Objects.Get(b.name, name).Do()
	if err != nil {
		return nil, err
	}
	return newObject(o), nil
}

// Put inserts/updates an object with the provided meta information.
func (b *BucketClient) Put(name string, info *Object) (*Object, error) {
	o, err := b.conn.s.Objects.Insert(b.name, info.toRawObject()).Do()
	if err != nil {
		return nil, err
	}
	return newObject(o), nil
}

// Delete deletes the specified object.
func (b *BucketClient) Delete(name string) error {
	return b.conn.s.Objects.Delete(b.name, name).Do()
}

// Copy copies the source object to the destination with the new
// meta information provided.
// The destination object is inserted into the source bucket
// if the destination object doesn't specify another bucket name.
func (b *BucketClient) Copy(name string, dest *Object) (*Object, error) {
	if dest.Name == "" {
		return nil, errors.New("storage: missing dest name")
	}
	if dest.Bucket == "" {
		dest.Bucket = b.name
	}
	o, err := b.conn.s.Objects.Copy(
		b.name, name, dest.Bucket, dest.Name, dest.toRawObject()).Do()
	if err != nil {
		return nil, err
	}
	return newObject(o), nil
}

// NewReader creates a new io.ReadCloser to read the contents
// of the object.
func (b *BucketClient) NewReader(name string) (io.ReadCloser, error) {
	resp, err := b.conn.c.Get(fmt.Sprintf(templUrlMedia, b.name, name))
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

// NewWriter returns a new ObjectWriter to write to the GCS object
// identified by the specified object name.
// If such object doesn't exist, it creates one. If info is not nil,
// write operation also modifies the meta information of the object.
// All read-only fields are ignored during metadata updates.
func (b *BucketClient) NewWriter(name string, info *Object) *ObjectWriter {
	i := Object{}
	if info != nil {
		i = *info
	}
	i.Bucket = b.name
	i.Name = name
	return newObjectWriter(b.conn, &i)
}
<|endoftext|>