<commit_before>package mailyak

import "io"

const maxLineLen = 60

// lineSplitter breaks the given input into lines of maxLineLen characters
// before writing a "\r\n" newline
type lineSplitter struct {
	w       io.Writer
	lineLen int
	maxLen  int
}

type lineSplitterBuilder struct{}

func (b lineSplitterBuilder) new(w io.Writer) io.Writer {
	return &lineSplitter{w: w, maxLen: maxLineLen}
}

func (w *lineSplitter) Write(p []byte) (int, error) {
	offset := 0
	breaks := (len(p) / w.maxLen)
	for i := 0; i < breaks; i++ {
		w.w.Write(p[offset : offset+w.maxLen])
		w.w.Write([]byte("\r\n"))
		offset += w.maxLen
	}

	w.w.Write(p[offset:])
	return (len(p) + (breaks * 2)), nil
}
<commit_msg>Pass err up<commit_after>package mailyak

import "io"

const maxLineLen = 60

// lineSplitter breaks the given input into lines of maxLineLen characters
// before writing a "\r\n" newline
type lineSplitter struct {
	w       io.Writer
	lineLen int
	maxLen  int
}

type lineSplitterBuilder struct{}

func (b lineSplitterBuilder) new(w io.Writer) io.Writer {
	return &lineSplitter{w: w, maxLen: maxLineLen}
}

func (w *lineSplitter) Write(p []byte) (int, error) {
	offset := 0
	breaks := (len(p) / w.maxLen)

	for i := 0; i < breaks; i++ {
		// Write line
		if i, err := w.w.Write(p[offset : offset+w.maxLen]); err != nil {
			return i, err
		}

		// Write line break
		if i, err := w.w.Write([]byte("\r\n")); err != nil {
			return i, err
		}

		offset += w.maxLen
	}

	// Write remaining
	if i, err := w.w.Write(p[offset:]); err != nil {
		return i, err
	}

	return (len(p) + (breaks * 2)), nil
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"fmt"
	"log"
	"time"
	"strings"
	"strconv"
	"text/template"
	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/appengine/v1beta4"
	"google.golang.org/api/storage/v1"
)

func resourceAppengine() *schema.Resource {
	return &schema.Resource{
		Create: resourceAppengineCreate,
		Read:   resourceAppengineRead,
		Delete: resourceAppengineDelete,

		Schema: map[string]*schema.Schema{
			"moduleName": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"version": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"gstorageBucket": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"gstorageKey": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"scaling": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"minIdleInstances": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							Default:  "1",
						},

						"maxIdleInstances": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							Default: "3",
						},

						"minPendingLatency": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default: "Automatic",
						},

						"maxPendingLatency": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default: "Automatic",
						},
					},
				},
			},
			"topicName": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"servingStatus": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

var (
	remoteBase = "https://storage.googleapis.com/"
)

func urlHandlers() ([]*appengine.UrlMap) {
	handlers := make([]*appengine.UrlMap, 0)
		handlers = append(handlers, &appengine.UrlMap{
			SecurityLevel: "SECURE_OPTIONAL",
			Login: "LOGIN_OPTIONAL",
			UrlRegex:"/",
			Script:&appengine.ScriptHandler{
				ScriptPath:"unused",
			},
		})
		handlers = append(handlers, &appengine.UrlMap{
			SecurityLevel: "SECURE_OPTIONAL",
			Login: "LOGIN_OPTIONAL",
			UrlRegex:"/.*/",
			Script:&appengine.ScriptHandler{
				ScriptPath:"unused",
			},
		})
		handlers = append(handlers, &appengine.UrlMap{
			SecurityLevel: "SECURE_OPTIONAL",
			Login: "LOGIN_OPTIONAL",
			UrlRegex:"/_ah/.*",
			Script:&appengine.ScriptHandler{
				ScriptPath:"unused",
			},
		})
		handlers = append(handlers, &appengine.UrlMap{
			SecurityLevel: "SECURE_OPTIONAL",
			Login: "LOGIN_OPTIONAL",
			UrlRegex:"/endpoint",
			Script:&appengine.ScriptHandler{
				ScriptPath:"unused",
			},
		})

		return handlers
}

// known issues with this function:
//   assumes "/" is delimiter in gstorage and forces that to be last char in key
//   only searches first page, if more then 1k files to load, will only grab first 1k
func generateFileList(d *schema.ResourceData, config *Config) (map[string]appengine.FileInfo, error) {
	listService := storage.NewObjectsService(config.clientStorage)
	bucket := d.Get("gstorageBucket").(string)
	listCall := listService.List(bucket)
	key := d.Get("gstorageKey").(string)
	lastChar := key[len(key)-1:]
	if lastChar != "/" {
		key = key + "/"
	}
	listCall = listCall.Prefix(key)
	objs, err := listCall.Do()
	if err != nil {
		return nil, err
	}

	files := make(map[string]appengine.FileInfo)
	for _, obj := range objs.Items {
		onDiskName := strings.Replace(obj.Name, key, "", 1)  // trims key from file name
		inCloudURL := remoteBase + bucket + "/" + obj.Name
		files[onDiskName] = appengine.FileInfo{SourceUrl:inCloudURL}
	}

	return files, nil
}

func renderAppengineXML(d  *schema.ResourceData, config *Config) (error) {
	type AppengineXmlData struct {
		Project			string
		SourceVersion	string
		Module			string
		TopicName		string
	}

	axd := AppengineXmlData{
		Project: config.Project,
		SourceVersion: d.Get("version").(string),
		Module: d.Get("moduleName").(string),
		TopicName: d.Get("topicName").(string),
	}

	templ, err := template.New("appengine-web.xml.template").ParseFiles("templates/appengine-web.xml.template")
	if err != nil {
		return err
	}

	axdRendered, err := os.Create("appengine-web.xml")
	if err != nil {
		return err
	}
	defer axdRendered.Close()
	err = templ.Execute(axdRendered, axd)
	if err != nil {
		return err
	}

	return nil
}

func pushAppengineXmlToCloud(d *schema.ResourceData, config *Config) (error) {
	key := d.Get("gstorageKey").(string)
	lastChar := key[len(key)-1:]
	if lastChar != "/" {
		key = key + "/"
	}
	key = key + "WEB-INF/appengine-web.xml"
	object := &storage.Object{Name: key}
    file, err := os.Open("appengine-web.xml")
    if err != nil {
    	fmt.Errorf("Error opening %q: %v", "appengine.xml", err)
    }
	objectService := storage.NewObjectsService(config.clientStorage)
	_, err = objectService.Insert(d.Get("gstorageBucket").(string), object).Media(file).Do()
    if err != nil {
        fmt.Errorf("Objects.Insert failed: %v", err)
    }

	return nil
}

func renderAppengineXMLToCloud(d *schema.ResourceData, config *Config) (error) {
	err := renderAppengineXML(d, config)
	if err != nil {
		return err
	}

	err = pushAppengineXmlToCloud(d, config)
	if err != nil {
		return err
	}

	return nil
}

func validateLatency(latency string) (string, error) {
	lastChar := latency[len(latency)-1:]
	if lastChar != "s" {
		return "", fmt.Errorf("latency values must be between 1 and 15 seconds in the form: 3s")
	}
	latency_i, err := strconv.Atoi(latency[:len(latency)-1])
	if err != nil {
		return "", err
	}
	if latency_i < 1 || latency_i > 15 {
		return "", fmt.Errorf("latency values must be between 1 and 15 seconds in the form: 3s")
	}

	return latency, nil
}

func resourceAppengineCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	scaling_raw := d.Get("scaling").([]interface{})
	if len(scaling_raw) > 1 {
		return fmt.Errorf("User supplied more then one scaling setting.  This is wrong")
	}

	scale := scaling_raw[0].(map[string]interface{})
	minPendingLatency, err := validateLatency(scale["minPendingLatency"].(string))
	if err != nil {
		return err
	}

	maxPendingLatency, err := validateLatency(scale["maxPendingLatency"].(string))
	if err != nil {
		return err
	}
	automaticScaling := &appengine.AutomaticScaling{
		MinIdleInstances: int64(scale["minIdleInstances"].(int)),
		MaxIdleInstances: int64(scale["maxIdleInstances"].(int)),
		MinPendingLatency: minPendingLatency,
		MaxPendingLatency: maxPendingLatency,
	}

	err = renderAppengineXMLToCloud(d, config)
	if err != nil {
		return err
	}

	files, err := generateFileList(d, config)
	if err != nil {
		return err
	}
	deployment := &appengine.Deployment{Files:files}

	handlers := urlHandlers()

	inbound_services := make([]string, 1)
	inbound_services[0] = "INBOUND_SERVICE_WARMUP"

	//  Version object for this module
	version := &appengine.Version{
		AutomaticScaling: automaticScaling,
		Deployment:deployment,
		Handlers: handlers,
		Id: d.Get("version").(string),
		Runtime: "java7",
		//InstanceClass: "F2",  this is exploding.  not sure why
		InboundServices: inbound_services,
		Threadsafe: true,
	}

	//  create the application
	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	createCall := moduleVersionService.Create(config.Project, d.Get("moduleName").(string), version)
	operation, err := createCall.Do()
	if err != nil {
		return err
	}

	err = operationWait(operation, config)
	if err != nil {
		return err
	}

	return resourceAppengineRead(d, meta)
}

func operationWait(operation *appengine.Operation, config *Config) (error) {
	//  wait for the creation to complete
	operationService := appengine.NewAppsOperationsService(config.clientAppengine)
	operationGet := operationService.Get(config.Project, strings.Replace(operation.Name, "apps/"+config.Project+"/operations/", "", 1))
	carryon := true
	for carryon {
		operation, err := operationGet.Do()
		if err != nil {
			return err
		}
		carryon = !operation.Done
		time.Sleep(10*time.Second)
	}

	//   if it failed, explode
	if operation.Error != nil {
		log.Printf("[DEBUG] status list from bad operation: %q", operation.Error.Details)
		return fmt.Errorf(operation.Error.Message)
	}

	return nil
}

func resourceAppengineRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	getCall := moduleVersionService.Get(config.Project, d.Get("moduleName").(string), d.Get("version").(string))
	version, err := getCall.Do()
	if err != nil {
		return err
	}

	d.SetId(version.Name)
	d.Set("servingStatus", version.ServingStatus)
	return nil
}

func resourceAppengineDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	deleteCall := moduleVersionService.Delete(config.Project, d.Get("moduleName").(string), d.Get("version").(string))
	operation, err := deleteCall.Do()
	if err != nil {
		if strings.Contains(err.Error(), "Cannot delete the final version of a service (module)") {
			moduleService := appengine.NewAppsModulesService(config.clientAppengine)
			moduleDelete := moduleService.Delete(config.Project, d.Get("moduleName").(string))
			operation, err = moduleDelete.Do()
			if err != nil {
				return err
			}

			err = operationWait(operation, config)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	} else {
		err = operationWait(operation, config)
		if err != nil {
			return err
		}
	}

	d.SetId("")
	return nil
}
<commit_msg>typo fix<commit_after>package main

import (
	"os"
	"fmt"
	"log"
	"time"
	"strings"
	"strconv"
	"text/template"
	"github.com/hashicorp/terraform/helper/schema"
	"google.golang.org/api/appengine/v1beta4"
	"google.golang.org/api/storage/v1"
)

func resourceAppengine() *schema.Resource {
	return &schema.Resource{
		Create: resourceAppengineCreate,
		Read:   resourceAppengineRead,
		Delete: resourceAppengineDelete,

		Schema: map[string]*schema.Schema{
			"moduleName": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"version": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"gstorageBucket": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"gstorageKey": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			"scaling": &schema.Schema{
				Type:     schema.TypeList,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"minIdleInstances": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							Default:  "1",
						},

						"maxIdleInstances": &schema.Schema{
							Type:     schema.TypeInt,
							Optional: true,
							Default: "3",
						},

						"minPendingLatency": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default: "Automatic",
						},

						"maxPendingLatency": &schema.Schema{
							Type:     schema.TypeString,
							Optional: true,
							Default: "Automatic",
						},
					},
				},
			},
			"topicName": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"servingStatus": &schema.Schema{
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

var (
	remoteBase = "https://storage.googleapis.com/"
)

func urlHandlers() ([]*appengine.UrlMap) {
	handlers := make([]*appengine.UrlMap, 0)
		handlers = append(handlers, &appengine.UrlMap{
			SecurityLevel: "SECURE_OPTIONAL",
			Login: "LOGIN_OPTIONAL",
			UrlRegex:"/",
			Script:&appengine.ScriptHandler{
				ScriptPath:"unused",
			},
		})
		handlers = append(handlers, &appengine.UrlMap{
			SecurityLevel: "SECURE_OPTIONAL",
			Login: "LOGIN_OPTIONAL",
			UrlRegex:"/.*/",
			Script:&appengine.ScriptHandler{
				ScriptPath:"unused",
			},
		})
		handlers = append(handlers, &appengine.UrlMap{
			SecurityLevel: "SECURE_OPTIONAL",
			Login: "LOGIN_OPTIONAL",
			UrlRegex:"/_ah/.*",
			Script:&appengine.ScriptHandler{
				ScriptPath:"unused",
			},
		})
		handlers = append(handlers, &appengine.UrlMap{
			SecurityLevel: "SECURE_OPTIONAL",
			Login: "LOGIN_OPTIONAL",
			UrlRegex:"/endpoint",
			Script:&appengine.ScriptHandler{
				ScriptPath:"unused",
			},
		})

		return handlers
}

// known issues with this function:
//   assumes "/" is delimiter in gstorage and forces that to be last char in key
//   only searches first page, if more then 1k files to load, will only grab first 1k
func generateFileList(d *schema.ResourceData, config *Config) (map[string]appengine.FileInfo, error) {
	listService := storage.NewObjectsService(config.clientStorage)
	bucket := d.Get("gstorageBucket").(string)
	listCall := listService.List(bucket)
	key := d.Get("gstorageKey").(string)
	lastChar := key[len(key)-1:]
	if lastChar != "/" {
		key = key + "/"
	}
	listCall = listCall.Prefix(key)
	objs, err := listCall.Do()
	if err != nil {
		return nil, err
	}

	files := make(map[string]appengine.FileInfo)
	for _, obj := range objs.Items {
		onDiskName := strings.Replace(obj.Name, key, "", 1)  // trims key from file name
		inCloudURL := remoteBase + bucket + "/" + obj.Name
		files[onDiskName] = appengine.FileInfo{SourceUrl:inCloudURL}
	}

	return files, nil
}

func renderAppengineXML(d  *schema.ResourceData, config *Config) (error) {
	type AppengineXmlData struct {
		Project			string
		SourceVersion	string
		Module			string
		TopicName		string
	}

	axd := AppengineXmlData{
		Project: config.Project,
		SourceVersion: d.Get("version").(string),
		Module: d.Get("moduleName").(string),
		TopicName: d.Get("topicName").(string),
	}

	templ, err := template.New("appengine-web.xml.template").Parse(axdTemplate)
	if err != nil {
		return err
	}

	axdRendered, err := os.Create("appengine-web.xml")
	if err != nil {
		return err
	}
	defer axdRendered.Close()
	err = templ.Execute(axdRendered, axd)
	if err != nil {
		return err
	}

	return nil
}

func pushAppengineXmlToCloud(d *schema.ResourceData, config *Config) (error) {
	key := d.Get("gstorageKey").(string)
	lastChar := key[len(key)-1:]
	if lastChar != "/" {
		key = key + "/"
	}
	key = key + "WEB-INF/appengine-web.xml"
	object := &storage.Object{Name: key}
    file, err := os.Open("appengine-web.xml")
    if err != nil {
    	fmt.Errorf("Error opening %q: %v", "appengine.xml", err)
    }
	objectService := storage.NewObjectsService(config.clientStorage)
	_, err = objectService.Insert(d.Get("gstorageBucket").(string), object).Media(file).Do()
    if err != nil {
        fmt.Errorf("Objects.Insert failed: %v", err)
    }

	return nil
}

func renderAppengineXMLToCloud(d *schema.ResourceData, config *Config) (error) {
	err := renderAppengineXML(d, config)
	if err != nil {
		return err
	}

	err = pushAppengineXmlToCloud(d, config)
	if err != nil {
		return err
	}

	return nil
}

func validateLatency(latency string) (string, error) {
	lastChar := latency[len(latency)-1:]
	if lastChar != "s" {
		return "", fmt.Errorf("latency values must be between 1 and 15 seconds in the form: 3s")
	}
	latency_i, err := strconv.Atoi(latency[:len(latency)-1])
	if err != nil {
		return "", err
	}
	if latency_i < 1 || latency_i > 15 {
		return "", fmt.Errorf("latency values must be between 1 and 15 seconds in the form: 3s")
	}

	return latency, nil
}

func resourceAppengineCreate(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	scaling_raw := d.Get("scaling").([]interface{})
	if len(scaling_raw) > 1 {
		return fmt.Errorf("User supplied more then one scaling setting.  This is wrong")
	}

	scale := scaling_raw[0].(map[string]interface{})
	minPendingLatency, err := validateLatency(scale["minPendingLatency"].(string))
	if err != nil {
		return err
	}

	maxPendingLatency, err := validateLatency(scale["maxPendingLatency"].(string))
	if err != nil {
		return err
	}
	automaticScaling := &appengine.AutomaticScaling{
		MinIdleInstances: int64(scale["minIdleInstances"].(int)),
		MaxIdleInstances: int64(scale["maxIdleInstances"].(int)),
		MinPendingLatency: minPendingLatency,
		MaxPendingLatency: maxPendingLatency,
	}

	err = renderAppengineXMLToCloud(d, config)
	if err != nil {
		return err
	}

	files, err := generateFileList(d, config)
	if err != nil {
		return err
	}
	deployment := &appengine.Deployment{Files:files}

	handlers := urlHandlers()

	inbound_services := make([]string, 1)
	inbound_services[0] = "INBOUND_SERVICE_WARMUP"

	//  Version object for this module
	version := &appengine.Version{
		AutomaticScaling: automaticScaling,
		Deployment:deployment,
		Handlers: handlers,
		Id: d.Get("version").(string),
		Runtime: "java7",
		//InstanceClass: "F2",  this is exploding.  not sure why
		InboundServices: inbound_services,
		Threadsafe: true,
	}

	//  create the application
	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	createCall := moduleVersionService.Create(config.Project, d.Get("moduleName").(string), version)
	operation, err := createCall.Do()
	if err != nil {
		return err
	}

	err = operationWait(operation, config)
	if err != nil {
		return err
	}

	return resourceAppengineRead(d, meta)
}

func operationWait(operation *appengine.Operation, config *Config) (error) {
	//  wait for the creation to complete
	operationService := appengine.NewAppsOperationsService(config.clientAppengine)
	operationGet := operationService.Get(config.Project, strings.Replace(operation.Name, "apps/"+config.Project+"/operations/", "", 1))
	carryon := true
	for carryon {
		operation, err := operationGet.Do()
		if err != nil {
			return err
		}
		carryon = !operation.Done
		time.Sleep(10*time.Second)
	}

	//   if it failed, explode
	if operation.Error != nil {
		log.Printf("[DEBUG] status list from bad operation: %q", operation.Error.Details)
		return fmt.Errorf(operation.Error.Message)
	}

	return nil
}

func resourceAppengineRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	getCall := moduleVersionService.Get(config.Project, d.Get("moduleName").(string), d.Get("version").(string))
	version, err := getCall.Do()
	if err != nil {
		return err
	}

	d.SetId(version.Name)
	d.Set("servingStatus", version.ServingStatus)
	return nil
}

func resourceAppengineDelete(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	moduleVersionService := appengine.NewAppsModulesVersionsService(config.clientAppengine)
	deleteCall := moduleVersionService.Delete(config.Project, d.Get("moduleName").(string), d.Get("version").(string))
	operation, err := deleteCall.Do()
	if err != nil {
		if strings.Contains(err.Error(), "Cannot delete the final version of a service (module)") {
			moduleService := appengine.NewAppsModulesService(config.clientAppengine)
			moduleDelete := moduleService.Delete(config.Project, d.Get("moduleName").(string))
			operation, err = moduleDelete.Do()
			if err != nil {
				return err
			}

			err = operationWait(operation, config)
			if err != nil {
				return err
			}
		} else {
			return err
		}
	} else {
		err = operationWait(operation, config)
		if err != nil {
			return err
		}
	}

	d.SetId("")
	return nil
}
<|endoftext|>


<commit_before>package poloniex

import (
	"time"
)

type PoloniexTicker struct {
	Last          float64 `json:"last,string"`
	LowestAsk     float64 `json:"lowestAsk,string"`
	HighestBid    float64 `json:"highestBid,string"`
	PercentChange float64 `json:"percentChange,string"`
	BaseVolume    float64 `json:"baseVolume,string"`
	QuoteVolume   float64 `json:"quoteVolume,string"`
	IsFrozen      int     `json:"isFrozen,string"`
	High24Hr      float64 `json:"high24hr,string"`
	Low24Hr       float64 `json:"low24hr,string"`
}

type PoloniexOrderbookResponse struct {
	Asks     [][]interface{} `json:"asks"`
	Bids     [][]interface{} `json:"bids"`
	IsFrozen string          `json:"isFrozen"`
}

type PoloniexOrderbookItem struct {
	Price  float64
	Amount float64
}

type PoloniexOrderbook struct {
	Asks []PoloniexOrderbookItem `json:"asks"`
	Bids []PoloniexOrderbookItem `json:"bids"`
}

type PoloniexTradeHistory struct {
	GlobalTradeID int64   `json:"globalTradeID"`
	TradeID       int64   `json:"tradeID"`
	Date          string  `json:"date"`
	Type          string  `json:"type"`
	Rate          float64 `json:"rate,string"`
	Amount        float64 `json:"amount,string"`
	Total         float64 `json:"total,string"`
}

type PoloniexChartData struct {
	Date            int     `json:"date"`
	High            float64 `json:"high"`
	Low             float64 `json:"low"`
	Open            float64 `json:"open"`
	Close           float64 `json:"close"`
	Volume          float64 `json:"volume"`
	QuoteVolume     float64 `json:"quoteVolume"`
	WeightedAverage float64 `json:"weightedAverage"`
}

type PoloniexCurrencies struct {
	Name               string      `json:"name"`
	MaxDailyWithdrawal string      `json:"maxDailyWithdrawal"`
	TxFee              float64     `json:"txFee,string"`
	MinConfirmations   int         `json:"minConf"`
	DepositAddresses   interface{} `json:"depositAddress"`
	Disabled           int         `json:"disabled"`
	Delisted           int         `json:"delisted"`
	Frozen             int         `json:"frozen"`
}

type PoloniexLoanOrder struct {
	Rate     float64 `json:"rate,string"`
	Amount   float64 `json:"amount,string"`
	RangeMin int     `json:"rangeMin"`
	RangeMax int     `json:"rangeMax"`
}

type PoloniexLoanOrders struct {
	Offers  []PoloniexLoanOrder `json:"offers"`
	Demands []PoloniexLoanOrder `json:"demands"`
}

type PoloniexBalance struct {
	Currency map[string]float64
}

type PoloniexCompleteBalance struct {
	Available float64
	OnOrders  float64
	BTCValue  float64
}

type PoloniexDepositAddresses struct {
	Addresses map[string]string
}

type PoloniexDepositsWithdrawals struct {
	Deposits []struct {
		Currency      string    `json:"currency"`
		Address       string    `json:"address"`
		Amount        float64   `json:"amount,string"`
		Confirmations int       `json:"confirmations"`
		TransactionID string    `json:"txid"`
		Timestamp     time.Time `json:"timestamp"`
		Status        string    `json:"string"`
	} `json:"deposits"`
	Withdrawals []struct {
		WithdrawalNumber int64     `json:"withdrawalNumber"`
		Currency         string    `json:"currency"`
		Address          string    `json:"address"`
		Amount           float64   `json:"amount,string"`
		Confirmations    int       `json:"confirmations"`
		TransactionID    string    `json:"txid"`
		Timestamp        time.Time `json:"timestamp"`
		Status           string    `json:"string"`
		IPAddress        string    `json:"ipAddress"`
	} `json:"withdrawals"`
}

type PoloniexOrder struct {
	OrderNumber int64   `json:"orderNumber,string"`
	Type        string  `json:"type"`
	Rate        float64 `json:"rate,string"`
	Amount      float64 `json:"amount,string"`
	Total       float64 `json:"total,string"`
	Date        string  `json:"date"`
	Margin      float64 `json:"margin"`
}

type PoloniexOpenOrdersResponseAll struct {
	Data map[string][]PoloniexOrder
}

type PoloniexOpenOrdersResponse struct {
	Data []PoloniexOrder
}

type PoloniexAuthentictedTradeHistory struct {
	GlobalTradeID int64   `json:"globalTradeID"`
	TradeID       int64   `json:"tradeID,string"`
	Date          string  `json:"date"`
	Rate          float64 `json:"rate,string"`
	Amount        float64 `json:"amount,string"`
	Total         float64 `json:"total,string"`
	Fee           float64 `json:"fee,string"`
	OrderNumber   int64   `json:"orderNumber,string"`
	Type          string  `json:"type"`
	Category      string  `json:"category"`
}

type PoloniexAuthenticatedTradeHistoryAll struct {
	Data map[string][]PoloniexAuthentictedTradeHistory
}

type PoloniexAuthenticatedTradeHistoryResponse struct {
	Data []PoloniexAuthentictedTradeHistory
}

type PoloniexResultingTrades struct {
	Amount  float64 `json:"amount,string"`
	Date    string  `json:"date"`
	Rate    float64 `json:"rate,string"`
	Total   float64 `json:"total,string"`
	TradeID int64   `json:"tradeID,string"`
	Type    string  `json:"type"`
}

type PoloniexOrderResponse struct {
	OrderNumber int64                     `json:"orderNumber,string"`
	Trades      []PoloniexResultingTrades `json:"resultingTrades"`
}

type PoloniexGenericResponse struct {
	Success int    `json:"success"`
	Error   string `json:"error"`
}

type PoloniexMoveOrderResponse struct {
	Success     int                                  `json:"success"`
	Error       string                               `json:"error"`
	OrderNumber int64                                `json:"orderNumber,string"`
	Trades      map[string][]PoloniexResultingTrades `json:"resultingTrades"`
}

type PoloniexWithdraw struct {
	Response string `json:"response"`
	Error    string `json:"error"`
}

type PoloniexFee struct {
	MakerFee        float64 `json:"makerFee,string"`
	TakerFee        float64 `json:"takerFee,string"`
	ThirtyDayVolume float64 `json:"thirtyDayVolume,string"`
	NextTier        float64 `json:"nextTier,string"`
}

type PoloniexMargin struct {
	TotalValue    float64 `json:"totalValue,string"`
	ProfitLoss    float64 `json:"pl,string"`
	LendingFees   float64 `json:"lendingFees,string"`
	NetValue      float64 `json:"netValue,string"`
	BorrowedValue float64 `json:"totalBorrowedValue,string"`
	CurrentMargin float64 `json:"currentMargin,string"`
}

type PoloniexMarginPosition struct {
	Amount            float64 `json:"amount,string"`
	Total             float64 `json:"total,string"`
	BasePrice         float64 `json:"basePrice,string"`
	LiquidiationPrice float64 `json:"liquidiationPrice"`
	ProfitLoss        float64 `json:"pl,string"`
	LendingFees       float64 `json:"lendingFees,string"`
	Type              string  `json:"type"`
}

type PoloniexLoanOffer struct {
	ID        int64   `json:"id"`
	Rate      float64 `json:"rate,string"`
	Amount    float64 `json:"amount,string"`
	Duration  int     `json:"duration"`
	AutoRenew bool    `json:"autoRenew,int"`
	Date      string  `json:"date"`
}

type PoloniexActiveLoans struct {
	Provided []PoloniexLoanOffer `json:"provided"`
	Used     []PoloniexLoanOffer `json:"used"`
}
<commit_msg>In PoloniexDepositsWithdrawals, use int64 instead of time.Time + fix typo<commit_after>package poloniex

type PoloniexTicker struct {
	Last          float64 `json:"last,string"`
	LowestAsk     float64 `json:"lowestAsk,string"`
	HighestBid    float64 `json:"highestBid,string"`
	PercentChange float64 `json:"percentChange,string"`
	BaseVolume    float64 `json:"baseVolume,string"`
	QuoteVolume   float64 `json:"quoteVolume,string"`
	IsFrozen      int     `json:"isFrozen,string"`
	High24Hr      float64 `json:"high24hr,string"`
	Low24Hr       float64 `json:"low24hr,string"`
}

type PoloniexOrderbookResponse struct {
	Asks     [][]interface{} `json:"asks"`
	Bids     [][]interface{} `json:"bids"`
	IsFrozen string          `json:"isFrozen"`
}

type PoloniexOrderbookItem struct {
	Price  float64
	Amount float64
}

type PoloniexOrderbook struct {
	Asks []PoloniexOrderbookItem `json:"asks"`
	Bids []PoloniexOrderbookItem `json:"bids"`
}

type PoloniexTradeHistory struct {
	GlobalTradeID int64   `json:"globalTradeID"`
	TradeID       int64   `json:"tradeID"`
	Date          string  `json:"date"`
	Type          string  `json:"type"`
	Rate          float64 `json:"rate,string"`
	Amount        float64 `json:"amount,string"`
	Total         float64 `json:"total,string"`
}

type PoloniexChartData struct {
	Date            int     `json:"date"`
	High            float64 `json:"high"`
	Low             float64 `json:"low"`
	Open            float64 `json:"open"`
	Close           float64 `json:"close"`
	Volume          float64 `json:"volume"`
	QuoteVolume     float64 `json:"quoteVolume"`
	WeightedAverage float64 `json:"weightedAverage"`
}

type PoloniexCurrencies struct {
	Name               string      `json:"name"`
	MaxDailyWithdrawal string      `json:"maxDailyWithdrawal"`
	TxFee              float64     `json:"txFee,string"`
	MinConfirmations   int         `json:"minConf"`
	DepositAddresses   interface{} `json:"depositAddress"`
	Disabled           int         `json:"disabled"`
	Delisted           int         `json:"delisted"`
	Frozen             int         `json:"frozen"`
}

type PoloniexLoanOrder struct {
	Rate     float64 `json:"rate,string"`
	Amount   float64 `json:"amount,string"`
	RangeMin int     `json:"rangeMin"`
	RangeMax int     `json:"rangeMax"`
}

type PoloniexLoanOrders struct {
	Offers  []PoloniexLoanOrder `json:"offers"`
	Demands []PoloniexLoanOrder `json:"demands"`
}

type PoloniexBalance struct {
	Currency map[string]float64
}

type PoloniexCompleteBalance struct {
	Available float64
	OnOrders  float64
	BTCValue  float64
}

type PoloniexDepositAddresses struct {
	Addresses map[string]string
}

type PoloniexDepositsWithdrawals struct {
	Deposits []struct {
		Currency      string  `json:"currency"`
		Address       string  `json:"address"`
		Amount        float64 `json:"amount,string"`
		Confirmations int     `json:"confirmations"`
		TransactionID string  `json:"txid"`
		Timestamp     int64   `json:"timestamp"`
		Status        string  `json:"status"`
	} `json:"deposits"`
	Withdrawals []struct {
		WithdrawalNumber int64   `json:"withdrawalNumber"`
		Currency         string  `json:"currency"`
		Address          string  `json:"address"`
		Amount           float64 `json:"amount,string"`
		Confirmations    int     `json:"confirmations"`
		TransactionID    string  `json:"txid"`
		Timestamp        int64   `json:"timestamp"`
		Status           string  `json:"status"`
		IPAddress        string  `json:"ipAddress"`
	} `json:"withdrawals"`
}

type PoloniexOrder struct {
	OrderNumber int64   `json:"orderNumber,string"`
	Type        string  `json:"type"`
	Rate        float64 `json:"rate,string"`
	Amount      float64 `json:"amount,string"`
	Total       float64 `json:"total,string"`
	Date        string  `json:"date"`
	Margin      float64 `json:"margin"`
}

type PoloniexOpenOrdersResponseAll struct {
	Data map[string][]PoloniexOrder
}

type PoloniexOpenOrdersResponse struct {
	Data []PoloniexOrder
}

type PoloniexAuthentictedTradeHistory struct {
	GlobalTradeID int64   `json:"globalTradeID"`
	TradeID       int64   `json:"tradeID,string"`
	Date          string  `json:"date"`
	Rate          float64 `json:"rate,string"`
	Amount        float64 `json:"amount,string"`
	Total         float64 `json:"total,string"`
	Fee           float64 `json:"fee,string"`
	OrderNumber   int64   `json:"orderNumber,string"`
	Type          string  `json:"type"`
	Category      string  `json:"category"`
}

type PoloniexAuthenticatedTradeHistoryAll struct {
	Data map[string][]PoloniexAuthentictedTradeHistory
}

type PoloniexAuthenticatedTradeHistoryResponse struct {
	Data []PoloniexAuthentictedTradeHistory
}

type PoloniexResultingTrades struct {
	Amount  float64 `json:"amount,string"`
	Date    string  `json:"date"`
	Rate    float64 `json:"rate,string"`
	Total   float64 `json:"total,string"`
	TradeID int64   `json:"tradeID,string"`
	Type    string  `json:"type"`
}

type PoloniexOrderResponse struct {
	OrderNumber int64                     `json:"orderNumber,string"`
	Trades      []PoloniexResultingTrades `json:"resultingTrades"`
}

type PoloniexGenericResponse struct {
	Success int    `json:"success"`
	Error   string `json:"error"`
}

type PoloniexMoveOrderResponse struct {
	Success     int                                  `json:"success"`
	Error       string                               `json:"error"`
	OrderNumber int64                                `json:"orderNumber,string"`
	Trades      map[string][]PoloniexResultingTrades `json:"resultingTrades"`
}

type PoloniexWithdraw struct {
	Response string `json:"response"`
	Error    string `json:"error"`
}

type PoloniexFee struct {
	MakerFee        float64 `json:"makerFee,string"`
	TakerFee        float64 `json:"takerFee,string"`
	ThirtyDayVolume float64 `json:"thirtyDayVolume,string"`
	NextTier        float64 `json:"nextTier,string"`
}

type PoloniexMargin struct {
	TotalValue    float64 `json:"totalValue,string"`
	ProfitLoss    float64 `json:"pl,string"`
	LendingFees   float64 `json:"lendingFees,string"`
	NetValue      float64 `json:"netValue,string"`
	BorrowedValue float64 `json:"totalBorrowedValue,string"`
	CurrentMargin float64 `json:"currentMargin,string"`
}

type PoloniexMarginPosition struct {
	Amount            float64 `json:"amount,string"`
	Total             float64 `json:"total,string"`
	BasePrice         float64 `json:"basePrice,string"`
	LiquidiationPrice float64 `json:"liquidiationPrice"`
	ProfitLoss        float64 `json:"pl,string"`
	LendingFees       float64 `json:"lendingFees,string"`
	Type              string  `json:"type"`
}

type PoloniexLoanOffer struct {
	ID        int64   `json:"id"`
	Rate      float64 `json:"rate,string"`
	Amount    float64 `json:"amount,string"`
	Duration  int     `json:"duration"`
	AutoRenew bool    `json:"autoRenew,int"`
	Date      string  `json:"date"`
}

type PoloniexActiveLoans struct {
	Provided []PoloniexLoanOffer `json:"provided"`
	Used     []PoloniexLoanOffer `json:"used"`
}
<|endoftext|>


<commit_before>package photoshare

import (
	"database/sql"
	"fmt"
	"github.com/coopernurse/gorp"
	"github.com/juju/errgo"
	_ "github.com/lib/pq" // PostgreSQL library
	"log"
	"os"
	"strings"
)

func dbConnect(user, pwd, name, host string) (*sql.DB, error) {
	db, err := sql.Open("postgres", fmt.Sprintf("user=%s dbname=%s password=%s host=%s",
		user,
		name,
		pwd,
		host,
	))
	if err != nil {
		return nil, err
	}
	if err := db.Ping(); err != nil {
		return nil, err
	}

	return db, nil
}

func initDB(db *sql.DB, logSql bool) (*gorp.DbMap, error) {
	dbMap := &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}}

	if logSql {
		dbMap.TraceOn("[sql]", log.New(os.Stdout, "", log.Ldate|log.Lmicroseconds))
	}

	dbMap.AddTableWithName(user{}, "users").SetKeys(true, "ID")
	dbMap.AddTableWithName(photo{}, "photos").SetKeys(true, "ID")
	dbMap.AddTableWithName(tag{}, "tags").SetKeys(true, "ID")

	return dbMap, nil
}

type dataMapper interface {
	createPhoto(*photo) error
	removePhoto(*photo) error
	updatePhoto(*photo) error
	updateTags(*photo) error

	createUser(*user) error
	updateUser(*user) error

	updateMany(...interface{}) error

	getPhoto(int64) (*photo, error)
	getPhotoDetail(int64, *user) (*photoDetail, error)
	getTagCounts() ([]tagCount, error)
	getPhotos(*page, string) (*photoList, error)
	getPhotosByOwnerID(*page, int64) (*photoList, error)
	searchPhotos(*page, string) (*photoList, error)

	isUserNameAvailable(user *user) (bool, error)
	isUserEmailAvailable(user *user) (bool, error)
	getActiveUser(userID int64) (*user, error)
	getUserByRecoveryCode(string) (*user, error)
	getUserByEmail(string) (*user, error)
	getUserByNameOrEmail(identifier string) (*user, error)
}

type defaultDataMapper struct {
	*gorp.DbMap
}

type transaction struct {
	*gorp.Transaction
}

func (t *transaction) updateTags(photo *photo) error {

	var (
		args    = []string{"$1"}
		params  = []interface{}{interface{}(photo.ID)}
		isEmpty = true
		counter = 1
	)
	for _, name := range photo.Tags {
		name = strings.TrimSpace(name)
		if name != "" {
			counter++
			args = append(args, fmt.Sprintf("$%d", counter))
			params = append(params, interface{}(strings.ToLower(name)))
			isEmpty = false
		}
	}

	if isEmpty && photo.ID != 0 {
		_, err := t.Exec("DELETE FROM photo_tags WHERE photo_id=$1", photo.ID)
		return errgo.Mask(err)
	}
	if _, err := t.Exec(fmt.Sprintf("SELECT add_tags(%s)", strings.Join(args, ",")), params...); err != nil {
		return errgo.Mask(err)
	}
	return nil

}

func newDataMapper(db *sql.DB, logSql bool) (dataMapper, error) {
	dbMap, err := initDB(db, logSql)
	if err != nil {
		return nil, err
	}
	return &defaultDataMapper{dbMap}, nil
}

func (d *defaultDataMapper) begin() (*transaction, error) {
	tx, err := d.Begin()
	if err != nil {
		return nil, err
	}
	return &transaction{tx}, nil
}

func (d *defaultDataMapper) createPhoto(photo *photo) error {
	t, err := d.begin()
	if err != nil {
		return errgo.Mask(err)
	}
	if err := t.Insert(photo); err != nil {
		return errgo.Mask(err)
	}
	if err := t.updateTags(photo); err != nil {
		t.Rollback()
		return errgo.Mask(err)
	}
	return errgo.Mask(t.Commit())
}

func (d *defaultDataMapper) createUser(user *user) error {
	return errgo.Mask(d.Insert(user))
}

func (d *defaultDataMapper) updatePhoto(photo *photo) error {
	if _, err := d.Update(photo); err != nil {
		return errgo.Mask(err)
	}
	return nil
}

func (d *defaultDataMapper) updateUser(user *user) error {
	if _, err := d.Update(user); err != nil {
		return errgo.Mask(err)
	}
	return nil
}

func (d *defaultDataMapper) removePhoto(photo *photo) error {
	if _, err := d.Delete(photo); err != nil {
		return errgo.Mask(err)
	}
	return nil
}

func (d *defaultDataMapper) updateTags(photo *photo) error {
	tx, err := d.begin()
	if err != nil {
		return errgo.Mask(err)
	}
	if err := tx.updateTags(photo); err != nil {
		tx.Rollback()
		return err
	}
	return errgo.Mask(tx.Commit())
}

func (d *defaultDataMapper) updateMany(items ...interface{}) error {
	tx, err := d.begin()
	if err != nil {
		return errgo.Mask(err)
	}
	for _, item := range items {
		if _, err := tx.Update(item); err != nil {
			tx.Rollback()
			return errgo.Mask(err)
		}
	}
	return errgo.Mask(tx.Commit())
}

func (d *defaultDataMapper) getPhoto(photoID int64) (*photo, error) {

	p := &photo{}

	if photoID == 0 {
		return p, sql.ErrNoRows
	}

	obj, err := d.Get(p, photoID)
	if err != nil {
		return p, errgo.Mask(err)
	}
	if obj == nil {
		return p, sql.ErrNoRows
	}
	return obj.(*photo), nil
}

func (d *defaultDataMapper) getPhotoDetail(photoID int64, user *user) (*photoDetail, error) {

	photo := &photoDetail{}

	if photoID == 0 {
		return photo, sql.ErrNoRows
	}

	q := "SELECT p.*, u.name AS owner_name " +
		"FROM photos p JOIN users u ON u.id = p.owner_id " +
		"WHERE p.id=$1"

	if err := d.SelectOne(photo, q, photoID); err != nil {
		return photo, errgo.Mask(err)
	}

	var tags []tag

	if _, err := d.Select(&tags,
		"SELECT t.* FROM tags t JOIN photo_tags pt ON pt.tag_id=t.id "+
			"WHERE pt.photo_id=$1", photo.ID); err != nil {
		return photo, errgo.Mask(err)
	}
	for _, tag := range tags {
		photo.Tags = append(photo.Tags, tag.Name)
	}

	photo.Permissions = &permissions{
		photo.canEdit(user),
		photo.canDelete(user),
		photo.canVote(user),
	}
	return photo, nil

}

func (d *defaultDataMapper) getPhotosByOwnerID(page *page, ownerID int64) (*photoList, error) {
	var (
		photos []photo
		err    error
		total  int64
	)

	if ownerID == 0 {
		return nil, sql.ErrNoRows
	}
	if total, err = d.SelectInt("SELECT COUNT(id) FROM photos WHERE owner_id=$1", ownerID); err != nil {
		return nil, errgo.Mask(err)
	}

	if _, err = d.Select(&photos,
		"SELECT * FROM photos WHERE owner_id = $1"+
			"ORDER BY (up_votes - down_votes) DESC, created_at DESC LIMIT $2 OFFSET $3",
		ownerID, page.size, page.offset); err != nil {
		return nil, errgo.Mask(err)
	}
	return newPhotoList(photos, total, page.index), nil

}

func (d *defaultDataMapper) searchPhotos(page *page, q string) (*photoList, error) {

	var (
		clauses []string
		params  []interface{}
		err     error
		photos  []photo
		total   int64
	)

	if q == "" {
		return nil, nil
	}

	for num, word := range strings.Split(q, " ") {
		word = strings.TrimSpace(word)
		if word == "" || num > 6 {
			break
		}

		num++

		if strings.HasPrefix(word, "@") {
			word = word[1:]
			clauses = append(clauses, fmt.Sprintf(
				"SELECT p.* FROM photos p "+
					"INNER JOIN users u ON u.id = p.owner_id  "+
					"WHERE UPPER(u.name::text) = UPPER($%d)", num))
		} else if strings.HasPrefix(word, "#") {
			word = word[1:]
			clauses = append(clauses, fmt.Sprintf(
				"SELECT p.* FROM photos p "+
					"INNER JOIN photo_tags pt ON pt.photo_id = p.id "+
					"INNER JOIN tags t ON pt.tag_id=t.id "+
					"WHERE UPPER(t.name::text) = UPPER($%d)", num))
		} else {
			word = "%" + word + "%"
			clauses = append(clauses, fmt.Sprintf(
				"SELECT DISTINCT p.* FROM photos p "+
					"INNER JOIN users u ON u.id = p.owner_id  "+
					"LEFT JOIN photo_tags pt ON pt.photo_id = p.id "+
					"LEFT JOIN tags t ON pt.tag_id=t.id "+
					"WHERE UPPER(p.title::text) LIKE UPPER($%d) OR "+
					"UPPER(u.name::text) LIKE UPPER($%d) OR t.name LIKE $%d",
				num, num, num))
		}

		params = append(params, interface{}(word))
	}

	clausesSql := strings.Join(clauses, " INTERSECT ")

	countSql := fmt.Sprintf("SELECT COUNT(id) FROM (%s) q", clausesSql)

	if total, err = d.SelectInt(countSql, params...); err != nil {
		return nil, errgo.Mask(err)
	}

	numParams := len(params)

	sql := fmt.Sprintf("SELECT * FROM (%s) q ORDER BY (up_votes - down_votes) DESC, created_at DESC LIMIT $%d OFFSET $%d",
		clausesSql, numParams+1, numParams+2)

	params = append(params, interface{}(page.size))
	params = append(params, interface{}(page.offset))

	if _, err = d.Select(&photos, sql, params...); err != nil {
		return nil, errgo.Mask(err)
	}
	return newPhotoList(photos, total, page.index), nil
}

func (d *defaultDataMapper) getPhotos(page *page, orderBy string) (*photoList, error) {

	var (
		total  int64
		photos []photo
		err    error
	)
	if orderBy == "votes" {
		orderBy = "(up_votes - down_votes)"
	} else {
		orderBy = "created_at"
	}

	if total, err = d.SelectInt("SELECT COUNT(id) FROM photos"); err != nil {
		return nil, errgo.Mask(err)
	}

	if _, err = d.Select(&photos,
		"SELECT * FROM photos "+
			"ORDER BY "+orderBy+" DESC LIMIT $1 OFFSET $2", page.size, page.offset); err != nil {
		return nil, errgo.Mask(err)
	}
	return newPhotoList(photos, total, page.index), nil
}

func (d *defaultDataMapper) getTagCounts() ([]tagCount, error) {
	var tags []tagCount
	if _, err := d.Select(&tags, "SELECT name, photo, num_photos FROM tag_counts"); err != nil {
		return tags, errgo.Mask(err)
	}
	return tags, nil
}

func (d *defaultDataMapper) isUserNameAvailable(user *user) (bool, error) {
	var (
		num int64
		err error
	)
	q := "SELECT COUNT(id) FROM users WHERE name=$1"
	if user.ID == 0 {
		num, err = d.SelectInt(q, user.Name)
	} else {
		q += " AND id != $2"
		num, err = d.SelectInt(q, user.Name, user.ID)
	}
	if err != nil {
		return false, errgo.Mask(err)
	}
	return num == 0, nil
}

func (d *defaultDataMapper) isUserEmailAvailable(user *user) (bool, error) {
	var (
		num int64
		err error
	)
	q := "SELECT COUNT(id) FROM users WHERE email=$1"
	if user.ID == 0 {
		num, err = d.SelectInt(q, user.Email)
	} else {
		q += " AND id != $2"
		num, err = d.SelectInt(q, user.Email, user.ID)
	}
	if err != nil {
		return false, errgo.Mask(err)
	}
	return num == 0, nil
}

func (d *defaultDataMapper) getActiveUser(userID int64) (*user, error) {

	user := &user{}
	if err := d.SelectOne(user, "SELECT * FROM users WHERE active=$1 AND id=$2", true, userID); err != nil {
		return user, errgo.Mask(err)
	}
	return user, nil

}

func (d *defaultDataMapper) getUserByRecoveryCode(code string) (*user, error) {

	user := &user{}
	if code == "" {
		return user, sql.ErrNoRows
	}
	if err := d.SelectOne(user, "SELECT * FROM users WHERE active=$1 AND recovery_code=$2", true, code); err != nil {
		return user, errgo.Mask(err)
	}
	return user, nil

}
func (d *defaultDataMapper) getUserByEmail(email string) (*user, error) {
	user := &user{}
	if err := d.SelectOne(user, "SELECT * FROM users WHERE active=$1 AND email=$2", true, email); err != nil {
		return user, errgo.Mask(err)
	}
	return user, nil
}

func (d *defaultDataMapper) getUserByNameOrEmail(identifier string) (*user, error) {
	user := &user{}

	if err := d.SelectOne(user, "SELECT * FROM users WHERE active=$1 AND (email=$2 OR name=$2)", true, identifier); err != nil {
		return user, errgo.Mask(err)
	}

	return user, nil
}
<commit_msg>minor fixes<commit_after>package photoshare

import (
	"database/sql"
	"fmt"
	"github.com/coopernurse/gorp"
	"github.com/juju/errgo"
	_ "github.com/lib/pq" // PostgreSQL library
	"log"
	"os"
	"strings"
)

func dbConnect(user, pwd, name, host string) (*sql.DB, error) {
	db, err := sql.Open("postgres", fmt.Sprintf("user=%s dbname=%s password=%s host=%s",
		user,
		name,
		pwd,
		host,
	))
	if err != nil {
		return nil, err
	}
	if err := db.Ping(); err != nil {
		return nil, err
	}

	return db, nil
}

func initDB(db *sql.DB, logSql bool) (*gorp.DbMap, error) {
	dbMap := &gorp.DbMap{Db: db, Dialect: gorp.PostgresDialect{}}

	if logSql {
		dbMap.TraceOn("[sql]", log.New(os.Stdout, "", log.Ldate|log.Lmicroseconds))
	}

	dbMap.AddTableWithName(user{}, "users").SetKeys(true, "ID")
	dbMap.AddTableWithName(photo{}, "photos").SetKeys(true, "ID")
	dbMap.AddTableWithName(tag{}, "tags").SetKeys(true, "ID")

	return dbMap, nil
}

type dataMapper interface {
	createPhoto(*photo) error
	removePhoto(*photo) error
	updatePhoto(*photo) error
	updateTags(*photo) error

	createUser(*user) error
	updateUser(*user) error

	updateMany(...interface{}) error

	getPhoto(int64) (*photo, error)
	getPhotoDetail(int64, *user) (*photoDetail, error)
	getTagCounts() ([]tagCount, error)
	getPhotos(*page, string) (*photoList, error)
	getPhotosByOwnerID(*page, int64) (*photoList, error)
	searchPhotos(*page, string) (*photoList, error)

	isUserNameAvailable(*user) (bool, error)
	isUserEmailAvailable(*user) (bool, error)
	getActiveUser(userID int64) (*user, error)
	getUserByRecoveryCode(string) (*user, error)
	getUserByEmail(string) (*user, error)
	getUserByNameOrEmail(identifier string) (*user, error)
}

type defaultDataMapper struct {
	*gorp.DbMap
}

type transaction struct {
	*gorp.Transaction
}

func (t *transaction) updateTags(photo *photo) error {

	var (
		args    = []string{"$1"}
		params  = []interface{}{interface{}(photo.ID)}
		isEmpty = true
		counter = 1
	)
	for _, name := range photo.Tags {
		name = strings.TrimSpace(name)
		if name != "" {
			counter++
			args = append(args, fmt.Sprintf("$%d", counter))
			params = append(params, interface{}(strings.ToLower(name)))
			isEmpty = false
		}
	}

	if isEmpty && photo.ID != 0 {
		_, err := t.Exec("DELETE FROM photo_tags WHERE photo_id=$1", photo.ID)
		return errgo.Mask(err)
	}
	if _, err := t.Exec(fmt.Sprintf("SELECT add_tags(%s)", strings.Join(args, ",")), params...); err != nil {
		return errgo.Mask(err)
	}
	return nil

}

func newDataMapper(db *sql.DB, logSql bool) (dataMapper, error) {
	dbMap, err := initDB(db, logSql)
	if err != nil {
		return nil, err
	}
	return &defaultDataMapper{dbMap}, nil
}

func (d *defaultDataMapper) begin() (*transaction, error) {
	tx, err := d.Begin()
	if err != nil {
		return nil, err
	}
	return &transaction{tx}, nil
}

func (d *defaultDataMapper) createPhoto(photo *photo) error {
	t, err := d.begin()
	if err != nil {
		return errgo.Mask(err)
	}
	if err := t.Insert(photo); err != nil {
		return errgo.Mask(err)
	}
	if err := t.updateTags(photo); err != nil {
		t.Rollback()
		return errgo.Mask(err)
	}
	return errgo.Mask(t.Commit())
}

func (d *defaultDataMapper) createUser(user *user) error {
	return errgo.Mask(d.Insert(user))
}

func (d *defaultDataMapper) updatePhoto(photo *photo) error {
	if _, err := d.Update(photo); err != nil {
		return errgo.Mask(err)
	}
	return nil
}

func (d *defaultDataMapper) updateUser(user *user) error {
	if _, err := d.Update(user); err != nil {
		return errgo.Mask(err)
	}
	return nil
}

func (d *defaultDataMapper) removePhoto(photo *photo) error {
	if _, err := d.Delete(photo); err != nil {
		return errgo.Mask(err)
	}
	return nil
}

func (d *defaultDataMapper) updateTags(photo *photo) error {
	tx, err := d.begin()
	if err != nil {
		return errgo.Mask(err)
	}
	if err := tx.updateTags(photo); err != nil {
		tx.Rollback()
		return err
	}
	return errgo.Mask(tx.Commit())
}

func (d *defaultDataMapper) updateMany(items ...interface{}) error {
	tx, err := d.begin()
	if err != nil {
		return errgo.Mask(err)
	}
	for _, item := range items {
		if _, err := tx.Update(item); err != nil {
			tx.Rollback()
			return errgo.Mask(err)
		}
	}
	return errgo.Mask(tx.Commit())
}

func (d *defaultDataMapper) getPhoto(photoID int64) (*photo, error) {

	p := &photo{}

	if photoID == 0 {
		return p, sql.ErrNoRows
	}

	obj, err := d.Get(p, photoID)
	if err != nil {
		return p, errgo.Mask(err)
	}
	if obj == nil {
		return p, sql.ErrNoRows
	}
	return obj.(*photo), nil
}

func (d *defaultDataMapper) getPhotoDetail(photoID int64, user *user) (*photoDetail, error) {

	photo := &photoDetail{}

	if photoID == 0 {
		return photo, sql.ErrNoRows
	}

	q := "SELECT p.*, u.name AS owner_name " +
		"FROM photos p JOIN users u ON u.id = p.owner_id " +
		"WHERE p.id=$1"

	if err := d.SelectOne(photo, q, photoID); err != nil {
		return photo, errgo.Mask(err)
	}

	var tags []tag

	if _, err := d.Select(&tags,
		"SELECT t.* FROM tags t JOIN photo_tags pt ON pt.tag_id=t.id "+
			"WHERE pt.photo_id=$1", photo.ID); err != nil {
		return photo, errgo.Mask(err)
	}
	for _, tag := range tags {
		photo.Tags = append(photo.Tags, tag.Name)
	}

	photo.Permissions = &permissions{
		photo.canEdit(user),
		photo.canDelete(user),
		photo.canVote(user),
	}
	return photo, nil

}

func (d *defaultDataMapper) getPhotosByOwnerID(page *page, ownerID int64) (*photoList, error) {
	var (
		photos []photo
		err    error
		total  int64
	)

	if ownerID == 0 {
		return nil, sql.ErrNoRows
	}
	if total, err = d.SelectInt("SELECT COUNT(id) FROM photos WHERE owner_id=$1", ownerID); err != nil {
		return nil, errgo.Mask(err)
	}

	if _, err = d.Select(&photos,
		"SELECT * FROM photos WHERE owner_id = $1"+
			"ORDER BY (up_votes - down_votes) DESC, created_at DESC LIMIT $2 OFFSET $3",
		ownerID, page.size, page.offset); err != nil {
		return nil, errgo.Mask(err)
	}
	return newPhotoList(photos, total, page.index), nil

}

func (d *defaultDataMapper) searchPhotos(page *page, q string) (*photoList, error) {

	var (
		clauses []string
		params  []interface{}
		err     error
		photos  []photo
		total   int64
	)

	if q == "" {
		return nil, nil
	}

	for num, word := range strings.Split(q, " ") {
		word = strings.TrimSpace(word)
		if word == "" || num > 6 {
			break
		}

		num++

		if strings.HasPrefix(word, "@") {
			word = word[1:]
			clauses = append(clauses, fmt.Sprintf(
				"SELECT p.* FROM photos p "+
					"INNER JOIN users u ON u.id = p.owner_id  "+
					"WHERE UPPER(u.name::text) = UPPER($%d)", num))
		} else if strings.HasPrefix(word, "#") {
			word = word[1:]
			clauses = append(clauses, fmt.Sprintf(
				"SELECT p.* FROM photos p "+
					"INNER JOIN photo_tags pt ON pt.photo_id = p.id "+
					"INNER JOIN tags t ON pt.tag_id=t.id "+
					"WHERE UPPER(t.name::text) = UPPER($%d)", num))
		} else {
			word = "%" + word + "%"
			clauses = append(clauses, fmt.Sprintf(
				"SELECT DISTINCT p.* FROM photos p "+
					"INNER JOIN users u ON u.id = p.owner_id  "+
					"LEFT JOIN photo_tags pt ON pt.photo_id = p.id "+
					"LEFT JOIN tags t ON pt.tag_id=t.id "+
					"WHERE UPPER(p.title::text) LIKE UPPER($%d) OR "+
					"UPPER(u.name::text) LIKE UPPER($%d) OR t.name LIKE $%d",
				num, num, num))
		}

		params = append(params, interface{}(word))
	}

	clausesSql := strings.Join(clauses, " INTERSECT ")

	countSql := fmt.Sprintf("SELECT COUNT(id) FROM (%s) q", clausesSql)

	if total, err = d.SelectInt(countSql, params...); err != nil {
		return nil, errgo.Mask(err)
	}

	numParams := len(params)

	sql := fmt.Sprintf("SELECT * FROM (%s) q ORDER BY (up_votes - down_votes) DESC, created_at DESC LIMIT $%d OFFSET $%d",
		clausesSql, numParams+1, numParams+2)

	params = append(params, interface{}(page.size))
	params = append(params, interface{}(page.offset))

	if _, err = d.Select(&photos, sql, params...); err != nil {
		return nil, errgo.Mask(err)
	}
	return newPhotoList(photos, total, page.index), nil
}

func (d *defaultDataMapper) getPhotos(page *page, orderBy string) (*photoList, error) {

	var (
		total  int64
		photos []photo
		err    error
	)
	if orderBy == "votes" {
		orderBy = "(up_votes - down_votes)"
	} else {
		orderBy = "created_at"
	}

	if total, err = d.SelectInt("SELECT COUNT(id) FROM photos"); err != nil {
		return nil, errgo.Mask(err)
	}

	if _, err = d.Select(&photos,
		"SELECT * FROM photos "+
			"ORDER BY "+orderBy+" DESC LIMIT $1 OFFSET $2", page.size, page.offset); err != nil {
		return nil, errgo.Mask(err)
	}
	return newPhotoList(photos, total, page.index), nil
}

func (d *defaultDataMapper) getTagCounts() ([]tagCount, error) {
	var tags []tagCount
	if _, err := d.Select(&tags, "SELECT name, photo, num_photos FROM tag_counts"); err != nil {
		return tags, errgo.Mask(err)
	}
	return tags, nil
}

func (d *defaultDataMapper) isUserNameAvailable(user *user) (bool, error) {
	var (
		num int64
		err error
	)
	q := "SELECT COUNT(id) FROM users WHERE name=$1"
	if user.ID == 0 {
		num, err = d.SelectInt(q, user.Name)
	} else {
		q += " AND id != $2"
		num, err = d.SelectInt(q, user.Name, user.ID)
	}
	if err != nil {
		return false, errgo.Mask(err)
	}
	return num == 0, nil
}

func (d *defaultDataMapper) isUserEmailAvailable(user *user) (bool, error) {
	var (
		num int64
		err error
	)
	q := "SELECT COUNT(id) FROM users WHERE email=$1"
	if user.ID == 0 {
		num, err = d.SelectInt(q, user.Email)
	} else {
		q += " AND id != $2"
		num, err = d.SelectInt(q, user.Email, user.ID)
	}
	if err != nil {
		return false, errgo.Mask(err)
	}
	return num == 0, nil
}

func (d *defaultDataMapper) getActiveUser(userID int64) (*user, error) {

	user := &user{}
	if err := d.SelectOne(user, "SELECT * FROM users WHERE active=$1 AND id=$2", true, userID); err != nil {
		return user, errgo.Mask(err)
	}
	return user, nil

}

func (d *defaultDataMapper) getUserByRecoveryCode(code string) (*user, error) {

	user := &user{}
	if code == "" {
		return user, sql.ErrNoRows
	}
	if err := d.SelectOne(user, "SELECT * FROM users WHERE active=$1 AND recovery_code=$2", true, code); err != nil {
		return user, errgo.Mask(err)
	}
	return user, nil

}
func (d *defaultDataMapper) getUserByEmail(email string) (*user, error) {
	user := &user{}
	if err := d.SelectOne(user, "SELECT * FROM users WHERE active=$1 AND email=$2", true, email); err != nil {
		return user, errgo.Mask(err)
	}
	return user, nil
}

func (d *defaultDataMapper) getUserByNameOrEmail(identifier string) (*user, error) {
	user := &user{}

	if err := d.SelectOne(user, "SELECT * FROM users WHERE active=$1 AND (email=$2 OR name=$2)", true, identifier); err != nil {
		return user, errgo.Mask(err)
	}

	return user, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors 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 components

import (
	"net/http"
	"sync"

	mesos "github.com/mesos/mesos-go/mesosproto"
	"k8s.io/kubernetes/contrib/mesos/pkg/backoff"
	"k8s.io/kubernetes/contrib/mesos/pkg/offers"
	"k8s.io/kubernetes/contrib/mesos/pkg/queue"
	"k8s.io/kubernetes/contrib/mesos/pkg/runtime"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/algorithm"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/binder"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/deleter"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/errorhandler"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/framework"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/podreconciler"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/schedulerloop"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/config"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podschedulers"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/queuer"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/client/record"
	client "k8s.io/kubernetes/pkg/client/unversioned"
)

// Scheduler implements types.Scheduler
type sched struct {
	podReconciler podreconciler.PodReconciler
	framework     framework.Framework
	loop          schedulerloop.SchedulerLoop

	// unsafe state, needs to be guarded, especially changes to podtask.T objects
	sync.RWMutex
	taskRegistry podtask.Registry
}

func New(c *config.Config, fw framework.Framework, ps podschedulers.PodScheduler,
	client *client.Client, recorder record.EventRecorder, terminate <-chan struct{}, mux *http.ServeMux, lw *cache.ListWatch) scheduler.Scheduler {

	core := &sched{
		framework:    fw,
		taskRegistry: podtask.NewInMemoryRegistry(),
	}

	// Watch and queue pods that need scheduling.
	updates := make(chan queue.Entry, c.UpdatesBacklog)
	podUpdates := &podStoreAdapter{queue.NewHistorical(updates)}
	reflector := cache.NewReflector(lw, &api.Pod{}, podUpdates, 0)

	q := queuer.New(podUpdates)

	algorithm := algorithm.New(core, podUpdates, ps)

	podDeleter := deleter.New(core, q)

	core.podReconciler = podreconciler.New(core, client, q, podDeleter)

	bo := backoff.New(c.InitialPodBackoff.Duration, c.MaxPodBackoff.Duration)
	newBC := func(podKey string) queue.BreakChan {
		return queue.BreakChan(core.Offers().Listen(podKey, func(offer *mesos.Offer) bool {
			core.Lock()
			defer core.Unlock()
			switch task, state := core.Tasks().ForPod(podKey); state {
			case podtask.StatePending:
				// Assess fitness of pod with the current offer. The scheduler normally
				// "backs off" when it can't find an offer that matches up with a pod.
				// The backoff period for a pod can terminate sooner if an offer becomes
				// available that matches up.
				return !task.Has(podtask.Launched) && ps.FitPredicate()(task, offer, nil)
			default:
				// no point in continuing to check for matching offers
				return true
			}
		}))
	}
	errorHandler := errorhandler.New(core, bo, q, newBC)

	binder := binder.New(core)

	startLatch := make(chan struct{})

	runtime.On(startLatch, func() {
		reflector.Run() // TODO(jdef) should listen for termination
		podDeleter.Run(updates, terminate)
		q.Run(terminate)

		q.InstallDebugHandlers(mux)
		podtask.InstallDebugHandlers(core.Tasks(), mux)
	})

	core.loop = schedulerloop.New(client, algorithm, recorder, q.Yield, errorHandler.Error, binder, startLatch)
	return core
}

func (c *sched) Run(done <-chan struct{}) {
	c.loop.Run(done)
}

func (c *sched) Reconcile(t *podtask.T) {
	c.podReconciler.Reconcile(t)
}

func (c *sched) Tasks() podtask.Registry {
	return c.taskRegistry
}

func (c *sched) Offers() offers.Registry {
	return c.framework.Offers()
}

func (c *sched) KillTask(id string) error {
	return c.framework.KillTask(id)
}

func (c *sched) LaunchTask(t *podtask.T) error {
	return c.framework.LaunchTask(t)
}
<commit_msg>Fix comment<commit_after>/*
Copyright 2015 The Kubernetes Authors 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 components

import (
	"net/http"
	"sync"

	mesos "github.com/mesos/mesos-go/mesosproto"
	"k8s.io/kubernetes/contrib/mesos/pkg/backoff"
	"k8s.io/kubernetes/contrib/mesos/pkg/offers"
	"k8s.io/kubernetes/contrib/mesos/pkg/queue"
	"k8s.io/kubernetes/contrib/mesos/pkg/runtime"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/algorithm"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/binder"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/deleter"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/errorhandler"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/framework"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/podreconciler"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/components/schedulerloop"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/config"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podschedulers"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask"
	"k8s.io/kubernetes/contrib/mesos/pkg/scheduler/queuer"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/client/record"
	client "k8s.io/kubernetes/pkg/client/unversioned"
)

// sched implements the Scheduler interface.
type sched struct {
	podReconciler podreconciler.PodReconciler
	framework     framework.Framework
	loop          schedulerloop.SchedulerLoop

	// unsafe state, needs to be guarded, especially changes to podtask.T objects
	sync.RWMutex
	taskRegistry podtask.Registry
}

func New(c *config.Config, fw framework.Framework, ps podschedulers.PodScheduler,
	client *client.Client, recorder record.EventRecorder, terminate <-chan struct{}, mux *http.ServeMux, lw *cache.ListWatch) scheduler.Scheduler {

	core := &sched{
		framework:    fw,
		taskRegistry: podtask.NewInMemoryRegistry(),
	}

	// Watch and queue pods that need scheduling.
	updates := make(chan queue.Entry, c.UpdatesBacklog)
	podUpdates := &podStoreAdapter{queue.NewHistorical(updates)}
	reflector := cache.NewReflector(lw, &api.Pod{}, podUpdates, 0)

	q := queuer.New(podUpdates)

	algorithm := algorithm.New(core, podUpdates, ps)

	podDeleter := deleter.New(core, q)

	core.podReconciler = podreconciler.New(core, client, q, podDeleter)

	bo := backoff.New(c.InitialPodBackoff.Duration, c.MaxPodBackoff.Duration)
	newBC := func(podKey string) queue.BreakChan {
		return queue.BreakChan(core.Offers().Listen(podKey, func(offer *mesos.Offer) bool {
			core.Lock()
			defer core.Unlock()
			switch task, state := core.Tasks().ForPod(podKey); state {
			case podtask.StatePending:
				// Assess fitness of pod with the current offer. The scheduler normally
				// "backs off" when it can't find an offer that matches up with a pod.
				// The backoff period for a pod can terminate sooner if an offer becomes
				// available that matches up.
				return !task.Has(podtask.Launched) && ps.FitPredicate()(task, offer, nil)
			default:
				// no point in continuing to check for matching offers
				return true
			}
		}))
	}
	errorHandler := errorhandler.New(core, bo, q, newBC)

	binder := binder.New(core)

	startLatch := make(chan struct{})

	runtime.On(startLatch, func() {
		reflector.Run() // TODO(jdef) should listen for termination
		podDeleter.Run(updates, terminate)
		q.Run(terminate)

		q.InstallDebugHandlers(mux)
		podtask.InstallDebugHandlers(core.Tasks(), mux)
	})

	core.loop = schedulerloop.New(client, algorithm, recorder, q.Yield, errorHandler.Error, binder, startLatch)
	return core
}

func (c *sched) Run(done <-chan struct{}) {
	c.loop.Run(done)
}

func (c *sched) Reconcile(t *podtask.T) {
	c.podReconciler.Reconcile(t)
}

func (c *sched) Tasks() podtask.Registry {
	return c.taskRegistry
}

func (c *sched) Offers() offers.Registry {
	return c.framework.Offers()
}

func (c *sched) KillTask(id string) error {
	return c.framework.KillTask(id)
}

func (c *sched) LaunchTask(t *podtask.T) error {
	return c.framework.LaunchTask(t)
}
<|endoftext|>


<commit_before>/*
Zabbix sender protocol defenition:

HEADER:   ZBXD\x01, 5 byte
DATALEN:  8 byte, little-endian
DATA:     JSON data

Request packet to zabbix host:
<HEADER><DATALEN>{
        "request": "sender data",
        "data": [
                {
                        "host": "Host name 1",
                        "key": "item_key",
                        "value": "33",
                        "clock": 1381482894
                },
                {
                        "host": "Host name 2",
                        "key": "item_key",
                        "value": "55",
                        "clock": 1381482894
                }
        ],
        "clock": 1381482905
}

Response packet:
<HEADER><DATALEN>{
    "response":"success",
    "info":"Processed 2 Failed 0 Total 2 Seconds spent 0.002070"
}
*/

package zabbix

import (
	"encoding/binary"
	"encoding/json"
  "fmt"
	"io/ioutil"
	"net"
	"os"
	"time"
)

// Metric class
type Metric struct {
	Host  string `json:"host"`
	Key   string `json:"key"`
	Value string `json:"value"`
	Clock int64  `json:"clock"`
}

// Metric class constructor
func NewMetric(host, key, value string, clock ...int64) *Metric {
	m := new(Metric)
	m.Host = host
	m.Key = key
	m.Value = value
	// use current time, if `clock` is not specified
	if m.Clock = time.Now().Unix(); len(clock) > 0 {
		m.Clock = int64(clock[0])
	}
	return m
}

// Packet class
type Packet struct {
	Request string    `json:"request"`
	Data    []*Metric `json:"data"`
	Clock   int64     `json:"clock"`
}

// Packet class cunstructor
func NewPacket(data []*Metric, clock ...int64) *Packet {
  p := new(Packet)
  p.Request = `sender data`
  p.Data = data
	// use current time, if `clock` is not specified
	if p.Clock = time.Now().Unix(); len(clock) > 0 {
		p.Clock = int64(clock[0])
	}
  return p
}

// DataLen. Packet class method, return 8 bytes with packet length in little endian order
func (p Packet) DataLen() []byte {
	dataLen := make([]byte, 8)
  jsonData, _ := json.Marshal(p)
	binary.LittleEndian.PutUint32(dataLen, uint32(len(jsonData)))
  return dataLen
}

// Sender class
type Sender struct {
  Host string
  Port int
}

// Sender class constructor
func NewSender(host string, port int) *Sender {
  s := new(Sender)
  s.Host = host
  s.Port = port
  return s
}

// Method Sender class, return zabbix header
func (s Sender) getHeader() []byte {
	return []byte("ZBXD\x01")
}

// Method Sender class, resolve uri by name:port
func (s Sender) getTCPAddr() *net.TCPAddr {
  // format: hostname:port
  addr := fmt.Sprintf("%s:%d", s.Host, s.Port)

  // Resolve hostname:port to ip:port
  iaddr, err := net.ResolveTCPAddr("tcp", addr)

  if err != nil {
		fmt.Printf("Connection failed: %s", err.Error())
		os.Exit(1)
	}

  return iaddr
}

// Method Sender class, make connection to uri
func (s Sender) connect() *net.TCPConn {
  iaddr := s.getTCPAddr()

	// Open connection to zabbix host
  conn, err := net.DialTCP("tcp", nil, iaddr)

  if err != nil {
		fmt.Printf("Connection failed: %s", err.Error())
		os.Exit(1)
	}

  return conn
}

// Method Sender class, read data from connection
func (s Sender) read(conn *net.TCPConn) []byte {
	res := make([]byte, 1024)

  res, err := ioutil.ReadAll(conn)
	if err != nil {
		fmt.Printf("Error whule receiving the data: %s", err.Error())
		os.Exit(1)
	}

  return res
}

// Method Sender class, send packet to zabbix
func (s Sender) Send(packet *Packet) {

  conn := s.connect()
	defer conn.Close()

  dataPacket, _ := json.Marshal(packet)

  // Make zabbix header
	fmt.Printf("HEADER: % x (%s)\n", s.getHeader(), s.getHeader())
  fmt.Printf("DATALEN: % x, %d byte\n", packet.DataLen(), len(packet.DataLen()))
	fmt.Printf("BODY: %s\n", string(dataPacket))

  // Fill buffer
  buffer := append(s.getHeader(), packet.DataLen()...)
  buffer = append(buffer, dataPacket...)

  // Sent packet to zabbix
  _, err := conn.Write(buffer)
	if err != nil {
		fmt.Printf("Error while sending the data: %s", err.Error())
		os.Exit(1)
	}

  res := s.read(conn)
  fmt.Printf("RESPONSE: %s\n", string(res))
}
<commit_msg>Style fix.<commit_after>// Package implement zabbix sender protocol for send metrics to zabbix.
package zabbix

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"
)

// Metric class.
type Metric struct {
	Host  string `json:"host"`
	Key   string `json:"key"`
	Value string `json:"value"`
	Clock int64  `json:"clock"`
}

// Metric class constructor.
func NewMetric(host, key, value string, clock ...int64) *Metric {
	m := new(Metric)
	m.Host = host
	m.Key = key
	m.Value = value
	// use current time, if `clock` is not specified
	if m.Clock = time.Now().Unix(); len(clock) > 0 {
		m.Clock = int64(clock[0])
	}
	return m
}

// Packet class.
type Packet struct {
	Request string    `json:"request"`
	Data    []*Metric `json:"data"`
	Clock   int64     `json:"clock"`
}

// Packet class cunstructor.
func NewPacket(data []*Metric, clock ...int64) *Packet {
	p := new(Packet)
	p.Request = `sender data`
	p.Data = data
	// use current time, if `clock` is not specified
	if p.Clock = time.Now().Unix(); len(clock) > 0 {
		p.Clock = int64(clock[0])
	}
	return p
}

// DataLen Packet class method, return 8 bytes with packet length in little endian order.
func (p Packet) DataLen() []byte {
	dataLen := make([]byte, 8)
	JSONData, _ := json.Marshal(p)
	binary.LittleEndian.PutUint32(dataLen, uint32(len(JSONData)))
	return dataLen
}

// Sender class.
type Sender struct {
	Host string
	Port int
}

// Sender class constructor.
func NewSender(host string, port int) *Sender {
	s := new(Sender)
	s.Host = host
	s.Port = port
	return s
}

// Method Sender class, return zabbix header.
func (s Sender) getHeader() []byte {
	return []byte("ZBXD\x01")
}

// Method Sender class, resolve uri by name:port.
func (s Sender) getTCPAddr() *net.TCPAddr {
	// format: hostname:port
	addr := fmt.Sprintf("%s:%d", s.Host, s.Port)

	// Resolve hostname:port to ip:port
	iaddr, err := net.ResolveTCPAddr("tcp", addr)

	if err != nil {
		fmt.Printf("Connection failed: %s", err.Error())
		os.Exit(1)
	}

	return iaddr
}

// Method Sender class, make connection to uri.
func (s Sender) connect() *net.TCPConn {
	// Open connection to zabbix host
	iaddr := s.getTCPAddr()
	conn, err := net.DialTCP("tcp", nil, iaddr)

	if err != nil {
		fmt.Printf("Connection failed: %s", err.Error())
		os.Exit(1)
	}

	return conn
}

// Method Sender class, read data from connection.
func (s Sender) read(conn *net.TCPConn) []byte {
	res := make([]byte, 1024)
	res, err := ioutil.ReadAll(conn)
	if err != nil {
		fmt.Printf("Error whule receiving the data: %s", err.Error())
		os.Exit(1)
	}

	return res
}

// Method Sender class, send packet to zabbix.
func (s Sender) Send(packet *Packet) []byte {
	conn := s.connect()
	defer conn.Close()

	dataPacket, _ := json.Marshal(packet)

	// Make zabbix header
	/*
	   fmt.Printf("HEADER: % x (%s)\n", s.getHeader(), s.getHeader())
	   fmt.Printf("DATALEN: % x, %d byte\n", packet.DataLen(), len(packet.DataLen()))
	   fmt.Printf("BODY: %s\n", string(dataPacket))
	*/

	// Fill buffer
	buffer := append(s.getHeader(), packet.DataLen()...)
	buffer = append(buffer, dataPacket...)

	// Sent packet to zabbix
	_, err := conn.Write(buffer)
	if err != nil {
		fmt.Printf("Error while sending the data: %s", err.Error())
		os.Exit(1)
	}

	res := s.read(conn)
	/*
	   fmt.Printf("RESPONSE: %s\n", string(res))
	*/
	return res
}
<|endoftext|>


<commit_before>package multipass

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"html"
	"log"
	"net"
	"net/http"
	"net/smtp"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/mholt/caddy/caddyhttp/httpserver"

	jose "gopkg.in/square/go-jose.v1"
)

var ErrInvalidToken = errors.New("invalid token")

type Auth struct {
	*Multipass
	Next httpserver.Handler
}

type Rule struct {
	Basepath  string
	Expires   time.Duration
	Resources []string
	Handles   []string

	SMTPAddr, SMTPUser, SMTPPass string
	MailFrom, MailTmpl           string
}

type Multipass struct {
	Resources []string
	Basepath  string
	SiteAddr  string
	Expires   time.Duration

	Handler HandleService
	signer  jose.Signer
	key     *rsa.PrivateKey
}

func NewMultipassFromRule(r Rule) (*Multipass, error) {
	m, err := NewMultipass()
	if err != nil {
		return nil, err
	}
	if len(r.Resources) > 0 {
		m.Resources = r.Resources
	}
	if len(r.Basepath) > 0 {
		m.Basepath = path.Join("/", r.Basepath)
	}
	if r.Expires > 0 {
		m.Expires = r.Expires
	}

	mailTmpl := emailTemplate
	if len(r.MailTmpl) > 0 {
		mailTmpl = r.MailTmpl
	}

	host := "localhost"
	port := "25"
	if h, p, err := net.SplitHostPort(r.SMTPAddr); err == nil {
		host = h
		port = p
	} else {
		host = r.SMTPAddr
	}

	var auth smtp.Auth
	if len(r.SMTPUser) > 0 && len(r.SMTPPass) > 0 {
		auth = smtp.PlainAuth("", r.SMTPUser, r.SMTPPass, host)
	}

	addr := strings.Join([]string{host, port}, ":")
	from := html.UnescapeString(r.MailFrom)
	handler := NewEmailHandler(addr, auth, from, mailTmpl)
	for _, handle := range r.Handles {
		handler.Register(handle)
	}
	m.Handler = handler

	return m, nil
}

func NewMultipass() (*Multipass, error) {
	pk, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, err
	}
	signer, err := jose.NewSigner(jose.PS512, pk)
	if err != nil {
		return nil, err
	}
	return &Multipass{
		Resources: []string{"/"},
		Basepath:  "/",
		Expires:   time.Hour * 24,
		key:       pk,
		signer:    signer,
	}, nil
}

// Claims are part of the JSON web token
type Claims struct {
	Handle    string   `json:"handle"`
	Resources []string `json:"resources"`
	Expires   int64    `json:"exp"`
}

func (m *Multipass) AccessToken(handle string) (tokenStr string, err error) {
	exp := time.Now().Add(m.Expires)
	claims := &Claims{
		Handle:    handle,
		Resources: m.Resources,
		Expires:   exp.Unix(),
	}
	payload, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}
	jws, err := m.signer.Sign(payload)
	if err != nil {
		return "", err
	}

	return jws.CompactSerialize()
}

func NewLoginURL(siteaddr, basepath, token string, v url.Values) (*url.URL, error) {
	u, err := url.Parse(siteaddr)
	if err != nil {
		return u, err
	}
	u.Path = path.Join(basepath, "login")
	v.Set("token", token)
	u.RawQuery = v.Encode()
	return u, nil
}

func loginHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	if r.Method == "POST" {
		r.ParseForm()
		handle := r.PostForm.Get("handle")
		if len(handle) == 0 {
			loc := path.Join(m.Basepath, "login")
			http.Redirect(w, r, loc, http.StatusSeeOther)
			return http.StatusSeeOther, nil
		}
		if m.Handler.Listed(handle) {
			token, err := m.AccessToken(handle)
			if err != nil {
				log.Print(err)
			}
			values := url.Values{}
			if s := r.PostForm.Get("url"); len(s) > 0 {
				values.Set("url", s)
			}
			loginURL, err := NewLoginURL(m.SiteAddr, m.Basepath, token, values)
			if err != nil {
				log.Print(err)
			}
			if err := m.Handler.Notify(handle, loginURL.String()); err != nil {
				log.Print(err)
			}
		}
		location := path.Join(m.Basepath, "/login/confirm")
		http.Redirect(w, r, location, http.StatusSeeOther)
		return http.StatusSeeOther, nil
	}
	if r.Method == "GET" {
		if tokenStr := r.URL.Query().Get("token"); len(tokenStr) > 0 {
			cookie := &http.Cookie{
				Name:  "jwt_token",
				Value: tokenStr,
				Path:  "/",
			}
			http.SetCookie(w, cookie)

			nexturl := m.SiteAddr
			if s := r.URL.Query().Get("url"); len(s) > 0 {
				nexturl = s
			}
			http.Redirect(w, r, nexturl, http.StatusSeeOther)
			return http.StatusSeeOther, nil
		}
		nextURL, err := url.Parse(m.SiteAddr)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		nextURL.Path = r.URL.Path
		nextURL.RawQuery = r.URL.RawQuery
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write([]byte(`

Multipass

This resource is protected by Multipass. Enter your user handle to gain access.

<form action="` + path.Join(m.Basepath, "/login") + `" method=POST>
<input type=hidden name=url value="` + nextURL.String() + `"/>
<input type=text name=handle />
<input type=submit>
</form>
`))
		return http.StatusOK, nil
	}
	return http.StatusMethodNotAllowed, nil
}

func confirmHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	w.Header().Add("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte("A login link has been sent to you"))
	return http.StatusOK, nil
}

func signoutHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	if cookie, err := r.Cookie("jwt_token"); err == nil {
		cookie.Expires = time.Now().AddDate(-1, 0, 0)
		cookie.MaxAge = -1
		cookie.Path = "/"
		http.SetCookie(w, cookie)
	}
	loc := path.Join(m.Basepath, "login")
	http.Redirect(w, r, loc, http.StatusSeeOther)
	return http.StatusSeeOther, nil
}

func publickeyHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	data, err := x509.MarshalPKIXPublicKey(&m.key.PublicKey)
	if err != nil {
		return http.StatusInternalServerError, err
	}
	block := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: data,
	}
	w.Header().Set("Content-Type", "application/pkix-cert")
	if err := pem.Encode(w, block); err != nil {
		return http.StatusInternalServerError, err
	}
	return http.StatusOK, nil
}

func tokenHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	// Extract token from HTTP header, query parameter or cookie
	tokenStr, err := extractToken(r)
	if err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	var claims *Claims
	if claims, err = validateToken(tokenStr, m.key.PublicKey); err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Authorize handle claim
	if ok := m.Handler.Listed(claims.Handle); !ok {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Verify path claim
	var match bool
	for _, path := range claims.Resources {
		if httpserver.Path(r.URL.Path).Matches(path) {
			match = true
			continue
		}
	}
	if !match {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	return http.StatusOK, nil
}

func (a *Auth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	m := a.Multipass

	switch r.URL.Path {
	case path.Join(m.Basepath, "pub.cer"):
		return publickeyHandler(w, r, m)
	case path.Join(m.Basepath, "login"):
		return loginHandler(w, r, m)
	case path.Join(m.Basepath, "login/confirm"):
		return confirmHandler(w, r, m)
	case path.Join(m.Basepath, "signout"):
		return signoutHandler(w, r, m)
	}

	for _, path := range m.Resources {
		if httpserver.Path(r.URL.Path).Matches(path) {
			if code, err := tokenHandler(w, r, m); err != nil {
				w.WriteHeader(code)
				return loginHandler(w, r, m)
			}
		}
	}

	return a.Next.ServeHTTP(w, r)
}

// extractToken returns the JWT token embedded in the given request.
// JWT tokens can be embedded in the header prefixed with "Bearer ", with a
// "token" key query parameter or a cookie named "jwt_token".
func extractToken(r *http.Request) (string, error) {
	//from header
	if h := r.Header.Get("Authorization"); strings.HasPrefix(h, "Bearer ") {
		if len(h) > 7 {
			return h[7:], nil
		}
	}

	//from query parameter
	if token := r.URL.Query().Get("token"); len(token) > 0 {
		return token, nil
	}

	//from cookie
	if cookie, err := r.Cookie("jwt_token"); err == nil {
		return cookie.Value, nil
	}

	return "", fmt.Errorf("no token found")
}

func validateToken(token string, key rsa.PublicKey) (*Claims, error) {
	claims := &Claims{}

	// Verify token signature
	payload, err := verifyToken(token, key)
	if err != nil {
		return nil, err
	}
	// Unmarshal token claims
	if err := json.Unmarshal(payload, claims); err != nil {
		return nil, err
	}
	// Verify expire claim
	if time.Unix(claims.Expires, 0).Before(time.Now()) {
		return nil, errors.New("Token expired")
	}
	return claims, nil
}

func verifyToken(token string, key rsa.PublicKey) ([]byte, error) {
	var data []byte

	obj, err := jose.ParseSigned(token)
	if err != nil {
		return data, err
	}
	data, err = obj.Verify(&key)
	if err != nil {
		return data, err
	}
	return data, nil
}
<commit_msg>Add condition to match multipass basepath<commit_after>package multipass

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"html"
	"log"
	"net"
	"net/http"
	"net/smtp"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/mholt/caddy/caddyhttp/httpserver"

	jose "gopkg.in/square/go-jose.v1"
)

var ErrInvalidToken = errors.New("invalid token")

type Auth struct {
	*Multipass
	Next httpserver.Handler
}

type Rule struct {
	Basepath  string
	Expires   time.Duration
	Resources []string
	Handles   []string

	SMTPAddr, SMTPUser, SMTPPass string
	MailFrom, MailTmpl           string
}

type Multipass struct {
	Resources []string
	Basepath  string
	SiteAddr  string
	Expires   time.Duration

	Handler HandleService
	signer  jose.Signer
	key     *rsa.PrivateKey
}

func NewMultipassFromRule(r Rule) (*Multipass, error) {
	m, err := NewMultipass()
	if err != nil {
		return nil, err
	}
	if len(r.Resources) > 0 {
		m.Resources = r.Resources
	}
	if len(r.Basepath) > 0 {
		m.Basepath = path.Join("/", r.Basepath)
	}
	if r.Expires > 0 {
		m.Expires = r.Expires
	}

	mailTmpl := emailTemplate
	if len(r.MailTmpl) > 0 {
		mailTmpl = r.MailTmpl
	}

	host := "localhost"
	port := "25"
	if h, p, err := net.SplitHostPort(r.SMTPAddr); err == nil {
		host = h
		port = p
	} else {
		host = r.SMTPAddr
	}

	var auth smtp.Auth
	if len(r.SMTPUser) > 0 && len(r.SMTPPass) > 0 {
		auth = smtp.PlainAuth("", r.SMTPUser, r.SMTPPass, host)
	}

	addr := strings.Join([]string{host, port}, ":")
	from := html.UnescapeString(r.MailFrom)
	handler := NewEmailHandler(addr, auth, from, mailTmpl)
	for _, handle := range r.Handles {
		handler.Register(handle)
	}
	m.Handler = handler

	return m, nil
}

func NewMultipass() (*Multipass, error) {
	pk, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil, err
	}
	signer, err := jose.NewSigner(jose.PS512, pk)
	if err != nil {
		return nil, err
	}
	return &Multipass{
		Resources: []string{"/"},
		Basepath:  "/",
		Expires:   time.Hour * 24,
		key:       pk,
		signer:    signer,
	}, nil
}

// Claims are part of the JSON web token
type Claims struct {
	Handle    string   `json:"handle"`
	Resources []string `json:"resources"`
	Expires   int64    `json:"exp"`
}

func (m *Multipass) AccessToken(handle string) (tokenStr string, err error) {
	exp := time.Now().Add(m.Expires)
	claims := &Claims{
		Handle:    handle,
		Resources: m.Resources,
		Expires:   exp.Unix(),
	}
	payload, err := json.Marshal(claims)
	if err != nil {
		return "", err
	}
	jws, err := m.signer.Sign(payload)
	if err != nil {
		return "", err
	}

	return jws.CompactSerialize()
}

func NewLoginURL(siteaddr, basepath, token string, v url.Values) (*url.URL, error) {
	u, err := url.Parse(siteaddr)
	if err != nil {
		return u, err
	}
	u.Path = path.Join(basepath, "login")
	v.Set("token", token)
	u.RawQuery = v.Encode()
	return u, nil
}

func loginHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	if r.Method == "POST" {
		r.ParseForm()
		handle := r.PostForm.Get("handle")
		if len(handle) == 0 {
			loc := path.Join(m.Basepath, "login")
			http.Redirect(w, r, loc, http.StatusSeeOther)
			return http.StatusSeeOther, nil
		}
		if m.Handler.Listed(handle) {
			token, err := m.AccessToken(handle)
			if err != nil {
				log.Print(err)
			}
			values := url.Values{}
			if s := r.PostForm.Get("url"); len(s) > 0 {
				values.Set("url", s)
			}
			loginURL, err := NewLoginURL(m.SiteAddr, m.Basepath, token, values)
			if err != nil {
				log.Print(err)
			}
			if err := m.Handler.Notify(handle, loginURL.String()); err != nil {
				log.Print(err)
			}
		}
		location := path.Join(m.Basepath, "/login/confirm")
		http.Redirect(w, r, location, http.StatusSeeOther)
		return http.StatusSeeOther, nil
	}
	if r.Method == "GET" {
		if tokenStr := r.URL.Query().Get("token"); len(tokenStr) > 0 {
			cookie := &http.Cookie{
				Name:  "jwt_token",
				Value: tokenStr,
				Path:  "/",
			}
			http.SetCookie(w, cookie)

			nexturl := m.SiteAddr
			if s := r.URL.Query().Get("url"); len(s) > 0 {
				nexturl = s
			}
			http.Redirect(w, r, nexturl, http.StatusSeeOther)
			return http.StatusSeeOther, nil
		}
		nextURL, err := url.Parse(m.SiteAddr)
		if err != nil {
			return http.StatusInternalServerError, err
		}
		nextURL.Path = r.URL.Path
		nextURL.RawQuery = r.URL.RawQuery
		w.Header().Set("Content-Type", "text/html; charset=utf-8")
		w.Write([]byte(`

Multipass

This resource is protected by Multipass. Enter your user handle to gain access.

<form action="` + path.Join(m.Basepath, "/login") + `" method=POST>
<input type=hidden name=url value="` + nextURL.String() + `"/>
<input type=text name=handle />
<input type=submit>
</form>
`))
		return http.StatusOK, nil
	}
	return http.StatusMethodNotAllowed, nil
}

func confirmHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	w.Header().Add("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte("A login link has been sent to you"))
	return http.StatusOK, nil
}

func signoutHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	if cookie, err := r.Cookie("jwt_token"); err == nil {
		cookie.Expires = time.Now().AddDate(-1, 0, 0)
		cookie.MaxAge = -1
		cookie.Path = "/"
		http.SetCookie(w, cookie)
	}
	loc := path.Join(m.Basepath, "login")
	http.Redirect(w, r, loc, http.StatusSeeOther)
	return http.StatusSeeOther, nil
}

func publickeyHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	data, err := x509.MarshalPKIXPublicKey(&m.key.PublicKey)
	if err != nil {
		return http.StatusInternalServerError, err
	}
	block := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: data,
	}
	w.Header().Set("Content-Type", "application/pkix-cert")
	if err := pem.Encode(w, block); err != nil {
		return http.StatusInternalServerError, err
	}
	return http.StatusOK, nil
}

func tokenHandler(w http.ResponseWriter, r *http.Request, m *Multipass) (int, error) {
	// Extract token from HTTP header, query parameter or cookie
	tokenStr, err := extractToken(r)
	if err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	var claims *Claims
	if claims, err = validateToken(tokenStr, m.key.PublicKey); err != nil {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Authorize handle claim
	if ok := m.Handler.Listed(claims.Handle); !ok {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	// Verify path claim
	var match bool
	for _, path := range claims.Resources {
		if httpserver.Path(r.URL.Path).Matches(path) {
			match = true
			continue
		}
	}
	if !match {
		return http.StatusUnauthorized, ErrInvalidToken
	}
	return http.StatusOK, nil
}

func (a *Auth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	m := a.Multipass

	if httpserver.Path(r.URL.Path).Matches(m.Basepath) {
		switch r.URL.Path {
		case path.Join(m.Basepath, "pub.cer"):
			return publickeyHandler(w, r, m)
		case path.Join(m.Basepath, "login"):
			return loginHandler(w, r, m)
		case path.Join(m.Basepath, "login/confirm"):
			return confirmHandler(w, r, m)
		case path.Join(m.Basepath, "signout"):
			return signoutHandler(w, r, m)
		default:
			return http.StatusNotFound, nil
		}
	}

	for _, path := range m.Resources {
		if httpserver.Path(r.URL.Path).Matches(path) {
			if code, err := tokenHandler(w, r, m); err != nil {
				w.WriteHeader(code)
				return loginHandler(w, r, m)
			}
		}
	}

	return a.Next.ServeHTTP(w, r)
}

// extractToken returns the JWT token embedded in the given request.
// JWT tokens can be embedded in the header prefixed with "Bearer ", with a
// "token" key query parameter or a cookie named "jwt_token".
func extractToken(r *http.Request) (string, error) {
	//from header
	if h := r.Header.Get("Authorization"); strings.HasPrefix(h, "Bearer ") {
		if len(h) > 7 {
			return h[7:], nil
		}
	}

	//from query parameter
	if token := r.URL.Query().Get("token"); len(token) > 0 {
		return token, nil
	}

	//from cookie
	if cookie, err := r.Cookie("jwt_token"); err == nil {
		return cookie.Value, nil
	}

	return "", fmt.Errorf("no token found")
}

func validateToken(token string, key rsa.PublicKey) (*Claims, error) {
	claims := &Claims{}

	// Verify token signature
	payload, err := verifyToken(token, key)
	if err != nil {
		return nil, err
	}
	// Unmarshal token claims
	if err := json.Unmarshal(payload, claims); err != nil {
		return nil, err
	}
	// Verify expire claim
	if time.Unix(claims.Expires, 0).Before(time.Now()) {
		return nil, errors.New("Token expired")
	}
	return claims, nil
}

func verifyToken(token string, key rsa.PublicKey) ([]byte, error) {
	var data []byte

	obj, err := jose.ParseSigned(token)
	if err != nil {
		return data, err
	}
	data, err = obj.Verify(&key)
	if err != nil {
		return data, err
	}
	return data, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package flag

import (
	goflag "flag"
	"fmt"
	"strings"

	"github.com/spf13/pflag"
)

// StringSlice implements goflag.Value and plfag.Value,
// and allows set to be invoked repeatedly to accumulate values.
type StringSlice struct {
	value   *[]string
	changed bool
}

func NewStringSlice(s *[]string) *StringSlice {
	return &StringSlice{value: s}
}

var _ goflag.Value = &StringSlice{}
var _ pflag.Value = &StringSlice{}

func (s *StringSlice) String() string {
	if s == nil || s.value == nil {
		return ""
	}
	return strings.Join(*s.value, " ")
}

func (s *StringSlice) Set(val string) error {
	if s.value == nil {
		return fmt.Errorf("no target (nil pointer to []string)")
	}
	if *s.value == nil || !s.changed {
		*s.value = make([]string, 0)
	}
	*s.value = append(*s.value, val)
	s.changed = true
	return nil
}

func (StringSlice) Type() string {
	return "sliceString"
}
<commit_msg>remove redundant clause in string_slice_flag<commit_after>/*
Copyright 2021 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package flag

import (
	goflag "flag"
	"fmt"
	"strings"

	"github.com/spf13/pflag"
)

// StringSlice implements goflag.Value and plfag.Value,
// and allows set to be invoked repeatedly to accumulate values.
type StringSlice struct {
	value   *[]string
	changed bool
}

func NewStringSlice(s *[]string) *StringSlice {
	return &StringSlice{value: s}
}

var _ goflag.Value = &StringSlice{}
var _ pflag.Value = &StringSlice{}

func (s *StringSlice) String() string {
	if s == nil || s.value == nil {
		return ""
	}
	return strings.Join(*s.value, " ")
}

func (s *StringSlice) Set(val string) error {
	if s.value == nil {
		return fmt.Errorf("no target (nil pointer to []string)")
	}
	if !s.changed {
		*s.value = make([]string, 0)
	}
	*s.value = append(*s.value, val)
	s.changed = true
	return nil
}

func (StringSlice) Type() string {
	return "sliceString"
}
<|endoftext|>