<commit_before>package dbgp

import (
	"bufio"
	"encoding/base64"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"

	"strings"
)

type Conn struct {
	sock   *bufio.ReadWriter
	client DBGPClient
}

//var commands map[string]*flag.FlagSet

var nul = []byte{0}

var protocolVersion = 18

// Creates a new DBGP client connection with an rw for the communication and
// a DBGPClient
func NewConn(conn io.ReadWriter, client DBGPClient) *Conn {
	rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
	return &Conn{rw, client}
}

// Initializes connection with the server
func (c *Conn) init() error {
	return c.sendXml(xml_init{xml.Name{}, c.client.Init(), "1.0"})
}

func (c *Conn) next() ([]string, error) {
	raw, err := c.sock.ReadString(0)
	if err != nil {
		return nil, err
	}
	return strings.Split(strings.TrimSuffix(raw, "\x00"), " "), nil
}

func (c *Conn) Run() error {
	if err := c.init(); err != nil {
		return err
	}
	flgs := new(flag.FlagSet)
	txId := flgs.Int("i", 0, "")
	depth := flgs.Int("d", 0, "")
	fileName := flgs.String("f", "", "")
	context := flgs.Int("c", 0, "")

	for {
		// reinit flags
		flgs.Init("", flag.ContinueOnError)
		payloadRaw := false // controls whether payload shoudl be marshalled or if it's already prepared

		parts, err := c.next()
		log.Println(parts)
		if err != nil {
			c.writeError("unknown", *txId, err)
		}

		cmd := parts[0]
		flgs.Parse(parts[1:])

		attrs := make(map[string]interface{})
		var payload interface{}
		switch cmd {
		case "step_into":
			status, reason := c.client.StepInto()
			attrs["status"] = status
			attrs["reason"] = reason
		case "stack_depth":
			attrs["depth"] = c.client.StackDepth()
		case "source":
			log.Println("[conn.go]", "source", *fileName)
			fn := strings.Replace(*fileName, "file://", "", 1)
			f, err := os.Open(fn)
			if err != nil {
				log.Println("[conn.go] error opening file:", fn, err)
				break
			}
			a, err := ioutil.ReadAll(f)
			if err != nil {
				log.Println("[conn.go] error reading file:", err)
				break
			}
			attrs["encoding"] = "base64"
			payload = "<![CDATA[" + base64.StdEncoding.EncodeToString(a) + "]]>"
			payloadRaw = true
		case "stack_get":
			stackEntries := c.client.StackGet(*depth)
			wrapped := make([]stack, len(stackEntries))
			for i, se := range stackEntries {
				wrapped[i] = stack{se}
			}
			payload = wrapped

		case "context_names":
			names := c.client.ContextNames(*depth)
			payload = names

		case "context_get":
			contextProperties := c.client.ContextGet(*depth, *context)
			payload = contextProperties
		default:
			err = ErrUnimplemented
		}
		if err != nil {
			err = c.writeError(cmd, *txId, err)
		} else {
			err = c.writeResponse(cmd, *txId, attrs, payload, payloadRaw)
		}
		if err != nil {
			panic(err)
		}
	}
}

func (c *Conn) writeError(cmd string, txId int, err error) error {
	if _, ok := err.(dbgpError); !ok {
		err = dbgpError{999, err.Error()}
	}

	e := struct {
		XMLName string `xml:"error"`
		dbgpError
	}{"error", err.(dbgpError)}

	return c.writeResponse(cmd, txId, nil, e, false)
}

func (c *Conn) writeResponse(cmd string, txId int, attrs map[string]interface{}, payload interface{}, payloadRaw bool) error {
	if attrs == nil {
		attrs = make(map[string]interface{})
	}

	attrs["command"] = cmd
	attrs["transaction_id"] = txId
	attrs["xmlns"] = "urn:debugger_protocol_v1"

	attrsToStrings := make([]string, 0, len(attrs))
	for k, v := range attrs {
		attrsToStrings = append(attrsToStrings, fmt.Sprint(k, `="`, v, `"`))
	}

	var (
		payloadBytes []byte
		err          error
	)
	if payloadRaw {
		payloadBytes = []byte(fmt.Sprint(payload))
	} else {
		payloadBytes, err = xml.MarshalIndent(payload, "", " ")
		if err != nil {
			return err
		}

	}

	r := fmt.Sprintf(`<response %s>%s</response>`,
		strings.Join(attrsToStrings, " "),
		string(payloadBytes))

	return c.sendBytes([]byte(r))
}

// Consumes the next command from the server
//func (c *Conn) next() (cmd, error) {
//	s, err := c.sock.ReadString(0)
//	if err != nil {
//		return cmd{}, err
//	}
//	bits := strings.Split(s, " ")
//	return cmd{bits[0], bits[1:], false}, nil
//}

func (c *Conn) sendXml(v interface{}) error {
	b, err := xml.MarshalIndent(v, "", "  ")
	if err != nil {
		return err
	}
	length := len(b) + len(xml.Header)
	c.sock.WriteString(fmt.Sprint(length))
	c.sock.Write(nul)
	c.sock.WriteString(xml.Header)
	_, err = c.sock.Write(b)
	if err != nil {
		return err
	}
	c.sock.Write(nul)
	return c.sock.Flush()
}

type stack struct {
	Stack
}

func (c *Conn) sendBytes(b []byte) error {
	c.sock.WriteString(fmt.Sprint(len(b)))
	c.sock.Write(nul)
	_, err := c.sock.Write(b)
	if err != nil {
		return err
	}
	c.sock.Write(nul)
	return c.sock.Flush()
}

// Encodes an init messages
//
//<init appid="APPID"
//      idekey="IDE_KEY"
//      session="DBGP_COOKIE"
//      thread="THREAD_ID"
//      parent="PARENT_APPID"
//      language="LANGUAGE_NAME"
//      protocol_version="1.0"
//      fileuri="file://path/to/file">
type xml_init struct {
	XMLName xml.Name `xml:"init"`
	InitResponse
	ProtocolVersion string `xml:"protocol_version,attr"`
}

// <?xml version="1.0" encoding="utf-8"?>
// <response xmlns="urn:debugger_protocol_v1" command="feature_get"
//   feature_name="supports_async" supported="1"
//   transaction_id="1"><![CDATA[1]]></response>.
//
// <?xml version="1.0" encoding="utf-8"?>
// <response xmlns="urn:debugger_protocol_v1" command="step_into" status="break" reason="ok" transaction_id="31"/>.
type xml_response struct {
	XMLName xml.Name     `xml:"response"`
	XMLNs   string       `xml:"xmlns,attr"`
	Command string       `xml:"command,attr"`
	TxId    int          `xml:"transaction_id,attr"`
	Payload interface{}  `xml:"omitempty"`
	Extra   string       `xml:",innerxml"`
	Data    xml.CharData `xml:"chardata,omitempty"`
}

type xml_error struct {
	XMLName xml.Name `xml:"response"`
	XMLNs   string   `xml:"xmlns,attr"`
	Command string   `xml:"command,attr"`
	TxId    int      `xml:"transaction_id,attr"`
	Error   error
	Data    xml.CharData `xml:"chardata,omitempty"`
}
<commit_msg>Slight reformat<commit_after>package dbgp

import (
	"bufio"
	"encoding/base64"
	"encoding/xml"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"

	"strings"
)

type Conn struct {
	sock   *bufio.ReadWriter
	client DBGPClient
}

//var commands map[string]*flag.FlagSet

var nul = []byte{0}

var protocolVersion = 18

// Creates a new DBGP client connection with an rw for the communication and
// a DBGPClient
func NewConn(conn io.ReadWriter, client DBGPClient) *Conn {
	rw := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
	return &Conn{rw, client}
}

// Initializes connection with the server
func (c *Conn) init() error {
	return c.sendXml(xml_init{xml.Name{}, c.client.Init(), "1.0"})
}

func (c *Conn) next() ([]string, error) {
	raw, err := c.sock.ReadString(0)
	if err != nil {
		return nil, err
	}
	return strings.Split(strings.TrimSuffix(raw, "\x00"), " "), nil
}

func (c *Conn) Run() error {
	if err := c.init(); err != nil {
		return err
	}
	flgs := new(flag.FlagSet)
	txId := flgs.Int("i", 0, "")
	depth := flgs.Int("d", 0, "")
	fileName := flgs.String("f", "", "")
	context := flgs.Int("c", 0, "")

	for {
		// reinit flags
		flgs.Init("", flag.ContinueOnError)

		parts, err := c.next()
		log.Println(parts)
		if err != nil {
			c.writeError("unknown", *txId, err)
		}

		cmd := parts[0]
		flgs.Parse(parts[1:])

		attrs := make(map[string]interface{})
		var (
			payload    interface{}
			payloadRaw bool // controls whether payload shoudl be marshalled or if it's already prepared
		)
		switch cmd {
		case "step_into":
			status, reason := c.client.StepInto()
			attrs["status"] = status
			attrs["reason"] = reason
		case "stack_depth":
			attrs["depth"] = c.client.StackDepth()
		case "source":
			log.Println("[conn.go]", "source", *fileName)
			fn := strings.Replace(*fileName, "file://", "", 1)
			f, err := os.Open(fn)
			if err != nil {
				log.Println("[conn.go] error opening file:", fn, err)
				break
			}
			a, err := ioutil.ReadAll(f)
			if err != nil {
				log.Println("[conn.go] error reading file:", err)
				break
			}
			attrs["encoding"] = "base64"
			payload = "<![CDATA[" + base64.StdEncoding.EncodeToString(a) + "]]>"
			payloadRaw = true
		case "stack_get":
			stackEntries := c.client.StackGet(*depth)
			wrapped := make([]stack, len(stackEntries))
			for i, se := range stackEntries {
				wrapped[i] = stack{se}
			}
			payload = wrapped

		case "context_names":
			names := c.client.ContextNames(*depth)
			payload = names

		case "context_get":
			contextProperties := c.client.ContextGet(*depth, *context)
			payload = contextProperties
		default:
			err = ErrUnimplemented
		}
		if err != nil {
			err = c.writeError(cmd, *txId, err)
		} else {
			err = c.writeResponse(cmd, *txId, attrs, payload, payloadRaw)
		}
		if err != nil {
			panic(err)
		}
	}
}

func (c *Conn) writeError(cmd string, txId int, err error) error {
	if _, ok := err.(dbgpError); !ok {
		err = dbgpError{999, err.Error()}
	}

	e := struct {
		XMLName string `xml:"error"`
		dbgpError
	}{"error", err.(dbgpError)}

	return c.writeResponse(cmd, txId, nil, e, false)
}

func (c *Conn) writeResponse(cmd string, txId int, attrs map[string]interface{}, payload interface{}, payloadRaw bool) error {
	if attrs == nil {
		attrs = make(map[string]interface{})
	}

	attrs["command"] = cmd
	attrs["transaction_id"] = txId
	attrs["xmlns"] = "urn:debugger_protocol_v1"

	attrsToStrings := make([]string, 0, len(attrs))
	for k, v := range attrs {
		attrsToStrings = append(attrsToStrings, fmt.Sprint(k, `="`, v, `"`))
	}

	var (
		payloadBytes []byte
		err          error
	)
	if payloadRaw {
		payloadBytes = []byte(fmt.Sprint(payload))
	} else {
		payloadBytes, err = xml.MarshalIndent(payload, "", " ")
		if err != nil {
			return err
		}

	}

	r := fmt.Sprintf(`<response %s>%s</response>`,
		strings.Join(attrsToStrings, " "),
		string(payloadBytes))

	return c.sendBytes([]byte(r))
}

// Consumes the next command from the server
//func (c *Conn) next() (cmd, error) {
//	s, err := c.sock.ReadString(0)
//	if err != nil {
//		return cmd{}, err
//	}
//	bits := strings.Split(s, " ")
//	return cmd{bits[0], bits[1:], false}, nil
//}

func (c *Conn) sendXml(v interface{}) error {
	b, err := xml.MarshalIndent(v, "", "  ")
	if err != nil {
		return err
	}
	length := len(b) + len(xml.Header)
	c.sock.WriteString(fmt.Sprint(length))
	c.sock.Write(nul)
	c.sock.WriteString(xml.Header)
	_, err = c.sock.Write(b)
	if err != nil {
		return err
	}
	c.sock.Write(nul)
	return c.sock.Flush()
}

type stack struct {
	Stack
}

func (c *Conn) sendBytes(b []byte) error {
	c.sock.WriteString(fmt.Sprint(len(b)))
	c.sock.Write(nul)
	_, err := c.sock.Write(b)
	if err != nil {
		return err
	}
	c.sock.Write(nul)
	return c.sock.Flush()
}

// Encodes an init messages
//
//<init appid="APPID"
//      idekey="IDE_KEY"
//      session="DBGP_COOKIE"
//      thread="THREAD_ID"
//      parent="PARENT_APPID"
//      language="LANGUAGE_NAME"
//      protocol_version="1.0"
//      fileuri="file://path/to/file">
type xml_init struct {
	XMLName xml.Name `xml:"init"`
	InitResponse
	ProtocolVersion string `xml:"protocol_version,attr"`
}

// <?xml version="1.0" encoding="utf-8"?>
// <response xmlns="urn:debugger_protocol_v1" command="feature_get"
//   feature_name="supports_async" supported="1"
//   transaction_id="1"><![CDATA[1]]></response>.
//
// <?xml version="1.0" encoding="utf-8"?>
// <response xmlns="urn:debugger_protocol_v1" command="step_into" status="break" reason="ok" transaction_id="31"/>.
type xml_response struct {
	XMLName xml.Name     `xml:"response"`
	XMLNs   string       `xml:"xmlns,attr"`
	Command string       `xml:"command,attr"`
	TxId    int          `xml:"transaction_id,attr"`
	Payload interface{}  `xml:"omitempty"`
	Extra   string       `xml:",innerxml"`
	Data    xml.CharData `xml:"chardata,omitempty"`
}

type xml_error struct {
	XMLName xml.Name `xml:"response"`
	XMLNs   string   `xml:"xmlns,attr"`
	Command string   `xml:"command,attr"`
	TxId    int      `xml:"transaction_id,attr"`
	Error   error
	Data    xml.CharData `xml:"chardata,omitempty"`
}
<|endoftext|>


<commit_before>package async

import (
	"reflect"
)

type deffered struct {
	ret  []reflect.Value
	done bool
	wait chan bool
}

func Deffered(f interface{}) *deffered {
	def := &deffered{nil, false, make(chan bool)}
	go func() {
		ff := reflect.ValueOf(f)
		def.ret = ff.Call(nil)
		def.done = true
		close(def.wait)
	}()
	return def
}

func (def *deffered) IsDone() bool {
	return def.done
}
<commit_msg>added monadic binding<commit_after>package async

import (
	"reflect"
)

type deffered struct {
	ret  []reflect.Value
	done bool
	wait chan bool
}

func Deffered(f interface{}) *deffered {
	def := &deffered{nil, false, make(chan bool)}
	go func() {
		ffun := reflect.ValueOf(f)
		def.ret = ffun.Call(nil)
		def.done = true
		close(def.wait)
	}()
	return def
}

func Init(v ...interface{}) *deffered {
	def := &deffered{make([]reflect.Value, len(v)), true, make(chan bool)}
	for i := range v {
		def.ret[i] = reflect.ValueOf(v[i])
	}
	close(def.wait)
	return def
}

func (def *deffered) IsDone() bool {
	return def.done
}

func (def *deffered) Bind(f interface{}) *deffered {
	newdef := &deffered{nil, false, make(chan bool)}
	go func() {
		for _ = range def.wait {
		}

		ffun := reflect.ValueOf(f)
		ftyp := reflect.TypeOf(f)
		newdef.ret = ffun.Call(def.ret[0:ftyp.NumIn()])
		newdef.done = true
		close(newdef.wait)
	}()
	return newdef
}
<|endoftext|>


<commit_before>package goboots

import (
	"bytes"
	"code.google.com/p/go-uuid/uuid"
	"errors"
	"fmt"
	"github.com/gabstv/i18ngo"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"text/template"
	"time"
)

const (
	ErrNil = 0
	// TLS
	ErrTLSNil = 100
)

var (
	sessionDbs   map[string]ISessionDBEngine
	curSessionDb ISessionDBEngine
	controllers  []IController
)

func RegisterController(controller IController) {
	//TODO: error if controller with same name exists!
	if controllers == nil {
		controllers = make([]IController, 0)
	}
	controllers = append(controllers, controller)
}

func RegisterSessionStorageDriver(name string, engine ISessionDBEngine) {
	if sessionDbs == nil {
		sessionDbs = make(map[string]ISessionDBEngine, 0)
	}
	sessionDbs[name] = engine
}

func (app *App) InitSessionStorage(driver string) error {
	if sessionDbs == nil {
		return errors.New("No session storage registered.")
	}
	if _, ok := sessionDbs[driver]; !ok {
		return errors.New("The session storage driver " + driver + " does not exist.")
	}
	curSessionDb = sessionDbs[driver]
	curSessionDb.SetApp(app)
	return nil
}

func CloseSessionStorage() {
	if curSessionDb != nil {
		curSessionDb.Close()
	}
}

func initAnySessionStorage() {
	if curSessionDb != nil {
		return
	}
	if sessionDbs == nil {
		//TODO: should panic
		return
	}
	for _, v := range sessionDbs {
		curSessionDb = v
		break
	}
}

type AppError struct {
	Id      int
	Message string
}

func (e *AppError) Error() string {
	return e.Message
}

type env struct {
	path       string
	controller string
	action     string
	params     []string
	request    *http.Request
	body       string
	form       map[string][]string
}

type ISession interface {
	GetData() (string, map[string]interface{}, time.Time)
}

type Session struct {
	SID     string
	Data    map[string]interface{}
	Flash   SessFlash `json:"-" bson:"-"` // never save flash
	Time    time.Time
	Updated time.Time
	r       *http.Request
	w       http.ResponseWriter
}

type SessFlash struct {
	vals map[string]interface{}
}

func (s *SessFlash) init() {
	if s.vals == nil {
		s.vals = make(map[string]interface{})
	}
}

func (s *SessFlash) Set(key string, val interface{}) {
	s.init()
	s.vals[key] = val
}

func (s *SessFlash) Get(key string) interface{} {
	s.init()
	return s.vals[key]
}

func (s *SessFlash) Get2(key string) (interface{}, bool) {
	s.init()
	v, ok := s.vals[key]
	return v, ok
}

func (s *SessFlash) Del(key string) {
	s.init()
	delete(s.vals, key)
}

func (s *SessFlash) Clear() {
	s.vals = nil
}

func (s *SessFlash) All() map[string]interface{} {
	s.init()
	return s.vals
}

func (s *Session) GetData() (string, map[string]interface{}, time.Time) {
	return s.SID, s.Data, s.Time
}

func (s *Session) Flush() {
	FlushSession(s)
}

func (s *Session) GetBool(key string) (bool, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return false, false
	}
	return bool(reflect.ValueOf(iface).Bool()), true
}

func (s *Session) GetBoolD(key string, defaultValue bool) bool {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return bool(reflect.ValueOf(iface).Bool())
}

func (s *Session) GetInt32(key string) (int, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return 0, false
	}
	return int(reflect.ValueOf(iface).Int()), true
}

func (s *Session) GetInt32D(key string, defaultValue int) int {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return int(reflect.ValueOf(iface).Int())
}

func (s *Session) GetString(key string) (string, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return "", false
	}
	return reflect.ValueOf(iface).String(), true
}

func (s *Session) GetStringD(key string, defaultValue string) string {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return reflect.ValueOf(iface).String()
}

func (s *Session) Expire(t time.Time) {
	SetCookieAdv(s.w, "goboots_sessid", s.SID, "/", "", t, 0, false, true)
}

func (s *Session) GetExpires() time.Time {
	cookie, err := s.r.Cookie("goboots_sessid")
	if err != nil {
		log.Println("Error @ (s *Session) GetExpires()", err)
		return time.Now()
	}
	return cookie.Expires
}

func GetSession(w http.ResponseWriter, r *http.Request) *Session {
	initAnySessionStorage()
	var cookie *http.Cookie
	var sid string
	var err error
	//LOAD
	cookie, err = r.Cookie("goboots_sessid")
	if err == nil {
		sid = cookie.Value
		if _validateSidString(sid) {
			var msession *Session
			// get from a saved location
			msession, err = curSessionDb.GetSession(sid)
			if err == nil {
				msession.r = r
				msession.w = w
				return msession
			}
			fmt.Printf("SESSION ERROR :( [%s] %s\n", sid, err.Error())
			// not found, generate a new sid
		} else {
			fmt.Printf("COULD NOT VALIDATE :( [%s]\n", sid)
		}
	}
	// gen session
	sid = uuid.New()

	session := &Session{
		SID:     sid,
		Data:    make(map[string]interface{}),
		Time:    time.Now(),
		Updated: time.Now(),
	}
	err = curSessionDb.NewSession(session)
	if err != nil {
		log.Println("[FATAL] [curSessionDb.NewSession] Could not get session!", err)
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("The server encountered an error while processing your request [CONN_ERR_GET_SESSION]."))
		return nil
	}
	//__panic(err)
	SetCookieAdv(w, "goboots_sessid", sid, "/", "", time.Now().AddDate(0, 1, 0), 0, false, true)
	session.w = w
	session.r = r
	return session
}

func GetUserLang(w http.ResponseWriter, r *http.Request) string {
	var l string
	// get lang from cookies
	cookie, err := r.Cookie("lang")
	if err != nil && cookie != nil {
		l = cookie.Value
		return l
	}
	// get lang from http request
	alh := r.Header.Get("Accept-Language")
	// 2013-07-29 : not all clients actually send this header (googlebot/wget etc)
	if len(alh) > 1 {
		validlangs := i18ngo.GetLanguageCodes()
		// break alh
		alh0 := strings.Split(alh, ",")
		for _, v := range alh0 {
			lcd := strings.Split(v, ";")[0]
			var lc string
			if len(lcd) >= 2 {
				lc = lcd[0:2]
			}
			lc = lcd
			if StringIndexOf(validlangs, lc) != -1 {
				l = lc
				break
			}
		}
		if len(l) > 0 {
			// just update the cookies
			if w != nil {
				SetCookieSimple(w, "lang", l)
			}
		} else {
			l = i18ngo.GetDefaultLanguageCode()
		}
		return l
	}
	return i18ngo.GetDefaultLanguageCode()
}

func SetUserLang(w http.ResponseWriter, r *http.Request, langcode string) {
	SetCookieSimple(w, "lang", langcode)
}

func FlushSession(s *Session) error {
	return curSessionDb.PutSession(s)
}

func DestroySession(w http.ResponseWriter, r *http.Request, s *Session) {
	curSessionDb.RemoveSession(s)
	SetCookieAdv(w, "goboots_sessid", "", "/", "", time.Now(), 1, false, true)
}

// DEPRECATED
func _validateSidString(sid string) bool {
	if len(sid) < 32 {
		return false
	}
	for _, c := range sid {
		// check if sid contains invalid chars
		if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '-')) {
			return false
		}
	}
	return true
}

type templateInfo struct {
	path       string
	data       *template.Template
	lastUpdate time.Time
}

func SetCookieAdv(w http.ResponseWriter, name string, value string, path string, domain string, expires time.Time, maxage int, secure bool, httpOnly bool) {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     path,
		Domain:   domain,
		Expires:  expires,
		MaxAge:   maxage,
		Secure:   secure,
		HttpOnly: httpOnly,
	}
	http.SetCookie(w, cookie)
}

func SetCookieSimple(w http.ResponseWriter, key string, value string) {
	SetCookieAdv(w, key, value, "/", "", time.Now().AddDate(100, 0, 0), 0, false, false)
}

func StrConcat(strings ...string) string {
	var buffer bytes.Buffer
	for _, str := range strings {
		buffer.WriteString(str)
	}
	return buffer.String()
}

func DeleteItem(slice interface{}, i int) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())))
}

func InsertItem(slice interface{}, i int, val interface{}) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i+1), v.Slice(i, v.Len())))
	v.Index(i).Set(reflect.ValueOf(val))
}

func StringIndexOf(haystack []string, needle string) int {
	for i, v := range haystack {
		if v == needle {
			return i
		}
	}
	return -1
}

func IntMax(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func __panic(err error) {
	if err != nil {
		log.Panicln(err)
	}
}

func FormatPath(rawpath string) string {
	cwd, _ := os.Getwd()
	if strings.HasPrefix(rawpath, "./") {
		return filepath.Clean(cwd + rawpath[1:])
	} else if strings.HasPrefix(rawpath, "../") {
		return filepath.Clean(cwd + "/" + rawpath)
	}
	return filepath.Clean(rawpath)
}
<commit_msg>err handling<commit_after>package goboots

import (
	"bytes"
	"code.google.com/p/go-uuid/uuid"
	"errors"
	"fmt"
	"github.com/gabstv/i18ngo"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"text/template"
	"time"
)

const (
	ErrNil = 0
	// TLS
	ErrTLSNil = 100
)

var (
	sessionDbs   map[string]ISessionDBEngine
	curSessionDb ISessionDBEngine
	controllers  []IController
)

func RegisterController(controller IController) {
	//TODO: error if controller with same name exists!
	if controllers == nil {
		controllers = make([]IController, 0)
	}
	controllers = append(controllers, controller)
}

func RegisterSessionStorageDriver(name string, engine ISessionDBEngine) {
	if sessionDbs == nil {
		sessionDbs = make(map[string]ISessionDBEngine, 0)
	}
	sessionDbs[name] = engine
}

func (app *App) InitSessionStorage(driver string) error {
	if sessionDbs == nil {
		return errors.New("No session storage registered.")
	}
	if _, ok := sessionDbs[driver]; !ok {
		return errors.New("The session storage driver " + driver + " does not exist.")
	}
	curSessionDb = sessionDbs[driver]
	curSessionDb.SetApp(app)
	return nil
}

func CloseSessionStorage() {
	if curSessionDb != nil {
		curSessionDb.Close()
	}
}

func initAnySessionStorage() {
	if curSessionDb != nil {
		return
	}
	if sessionDbs == nil {
		//TODO: should panic
		return
	}
	for _, v := range sessionDbs {
		curSessionDb = v
		break
	}
}

type AppError struct {
	Id      int
	Message string
}

func (e *AppError) Error() string {
	return e.Message
}

type env struct {
	path       string
	controller string
	action     string
	params     []string
	request    *http.Request
	body       string
	form       map[string][]string
}

type ISession interface {
	GetData() (string, map[string]interface{}, time.Time)
}

type Session struct {
	SID     string
	Data    map[string]interface{}
	Flash   SessFlash `json:"-" bson:"-"` // never save flash
	Time    time.Time
	Updated time.Time
	r       *http.Request
	w       http.ResponseWriter
}

type SessFlash struct {
	vals map[string]interface{}
}

func (s *SessFlash) init() {
	if s.vals == nil {
		s.vals = make(map[string]interface{})
	}
}

func (s *SessFlash) Set(key string, val interface{}) {
	s.init()
	s.vals[key] = val
}

func (s *SessFlash) Get(key string) interface{} {
	s.init()
	return s.vals[key]
}

func (s *SessFlash) Get2(key string) (interface{}, bool) {
	s.init()
	v, ok := s.vals[key]
	return v, ok
}

func (s *SessFlash) Del(key string) {
	s.init()
	delete(s.vals, key)
}

func (s *SessFlash) Clear() {
	s.vals = nil
}

func (s *SessFlash) All() map[string]interface{} {
	s.init()
	return s.vals
}

func (s *Session) GetData() (string, map[string]interface{}, time.Time) {
	return s.SID, s.Data, s.Time
}

func (s *Session) Flush() {
	FlushSession(s)
}

func (s *Session) GetBool(key string) (bool, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return false, false
	}
	return bool(reflect.ValueOf(iface).Bool()), true
}

func (s *Session) GetBoolD(key string, defaultValue bool) bool {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return bool(reflect.ValueOf(iface).Bool())
}

func (s *Session) GetInt32(key string) (int, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return 0, false
	}
	return int(reflect.ValueOf(iface).Int()), true
}

func (s *Session) GetInt32D(key string, defaultValue int) int {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return int(reflect.ValueOf(iface).Int())
}

func (s *Session) GetString(key string) (string, bool) {
	iface, ok := s.Data[key]
	if !ok {
		return "", false
	}
	return reflect.ValueOf(iface).String(), true
}

func (s *Session) GetStringD(key string, defaultValue string) string {
	iface, ok := s.Data[key]
	if !ok {
		return defaultValue
	}
	return reflect.ValueOf(iface).String()
}

func (s *Session) Expire(t time.Time) {
	SetCookieAdv(s.w, "goboots_sessid", s.SID, "/", "", t, 0, false, true)
}

func (s *Session) GetExpires() time.Time {
	cookie, err := s.r.Cookie("goboots_sessid")
	if err != nil {
		log.Println("Error @ (s *Session) GetExpires()", err)
		return time.Now()
	}
	return cookie.Expires
}

func GetSession(w http.ResponseWriter, r *http.Request) *Session {
	initAnySessionStorage()
	var cookie *http.Cookie
	var sid string
	var err error
	//LOAD
	cookie, err = r.Cookie("goboots_sessid")
	if err == nil {
		sid = cookie.Value
		if _validateSidString(sid) {
			var msession *Session
			// get from a saved location
			msession, err = curSessionDb.GetSession(sid)
			if err == nil {
				msession.r = r
				msession.w = w
				return msession
			}
			fmt.Printf("SESSION ERROR :( [%s] %s\n", sid, err.Error())
			// not found, generate a new sid
		} else {
			fmt.Printf("COULD NOT VALIDATE :( [%s]\n", sid)
		}
	}
	// gen session
	sid = uuid.New()

	session := &Session{
		SID:     sid,
		Data:    make(map[string]interface{}),
		Time:    time.Now(),
		Updated: time.Now(),
	}
	err = curSessionDb.NewSession(session)
	if err != nil {
		log.Println("[FATAL] [curSessionDb.NewSession] Could not get session!", err)
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("The server encountered an error while processing your request [ERR_CONN_NEW_SESSION]."))
		return nil
	}
	//__panic(err)
	SetCookieAdv(w, "goboots_sessid", sid, "/", "", time.Now().AddDate(0, 1, 0), 0, false, true)
	session.w = w
	session.r = r
	return session
}

func GetUserLang(w http.ResponseWriter, r *http.Request) string {
	var l string
	// get lang from cookies
	cookie, err := r.Cookie("lang")
	if err != nil && cookie != nil {
		l = cookie.Value
		return l
	}
	// get lang from http request
	alh := r.Header.Get("Accept-Language")
	// 2013-07-29 : not all clients actually send this header (googlebot/wget etc)
	if len(alh) > 1 {
		validlangs := i18ngo.GetLanguageCodes()
		// break alh
		alh0 := strings.Split(alh, ",")
		for _, v := range alh0 {
			lcd := strings.Split(v, ";")[0]
			var lc string
			if len(lcd) >= 2 {
				lc = lcd[0:2]
			}
			lc = lcd
			if StringIndexOf(validlangs, lc) != -1 {
				l = lc
				break
			}
		}
		if len(l) > 0 {
			// just update the cookies
			if w != nil {
				SetCookieSimple(w, "lang", l)
			}
		} else {
			l = i18ngo.GetDefaultLanguageCode()
		}
		return l
	}
	return i18ngo.GetDefaultLanguageCode()
}

func SetUserLang(w http.ResponseWriter, r *http.Request, langcode string) {
	SetCookieSimple(w, "lang", langcode)
}

func FlushSession(s *Session) error {
	return curSessionDb.PutSession(s)
}

func DestroySession(w http.ResponseWriter, r *http.Request, s *Session) {
	curSessionDb.RemoveSession(s)
	SetCookieAdv(w, "goboots_sessid", "", "/", "", time.Now(), 1, false, true)
}

// DEPRECATED
func _validateSidString(sid string) bool {
	if len(sid) < 32 {
		return false
	}
	for _, c := range sid {
		// check if sid contains invalid chars
		if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '-')) {
			return false
		}
	}
	return true
}

type templateInfo struct {
	path       string
	data       *template.Template
	lastUpdate time.Time
}

func SetCookieAdv(w http.ResponseWriter, name string, value string, path string, domain string, expires time.Time, maxage int, secure bool, httpOnly bool) {
	cookie := &http.Cookie{
		Name:     name,
		Value:    value,
		Path:     path,
		Domain:   domain,
		Expires:  expires,
		MaxAge:   maxage,
		Secure:   secure,
		HttpOnly: httpOnly,
	}
	http.SetCookie(w, cookie)
}

func SetCookieSimple(w http.ResponseWriter, key string, value string) {
	SetCookieAdv(w, key, value, "/", "", time.Now().AddDate(100, 0, 0), 0, false, false)
}

func StrConcat(strings ...string) string {
	var buffer bytes.Buffer
	for _, str := range strings {
		buffer.WriteString(str)
	}
	return buffer.String()
}

func DeleteItem(slice interface{}, i int) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i), v.Slice(i+1, v.Len())))
}

func InsertItem(slice interface{}, i int, val interface{}) {
	v := reflect.ValueOf(slice).Elem()
	v.Set(reflect.AppendSlice(v.Slice(0, i+1), v.Slice(i, v.Len())))
	v.Index(i).Set(reflect.ValueOf(val))
}

func StringIndexOf(haystack []string, needle string) int {
	for i, v := range haystack {
		if v == needle {
			return i
		}
	}
	return -1
}

func IntMax(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func __panic(err error) {
	if err != nil {
		log.Panicln(err)
	}
}

func FormatPath(rawpath string) string {
	cwd, _ := os.Getwd()
	if strings.HasPrefix(rawpath, "./") {
		return filepath.Clean(cwd + rawpath[1:])
	} else if strings.HasPrefix(rawpath, "../") {
		return filepath.Clean(cwd + "/" + rawpath)
	}
	return filepath.Clean(rawpath)
}
<|endoftext|>


<commit_before>package mokumokuren

import (
	"fmt"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"log"
	//"net"
	//"strconv"
	"sync"
	"time"
)

const CQLEN = 64

type PacketEvent struct {
	Packet    gopacket.Packet
	Timestamp time.Time
	Reverse   bool
	Finish    bool
}

type FlowKey struct {
	Sip string // FIXME this is a stupid hack but net.IP isn't comparable
	Dip string // FIXME this is a stupid hack but net.IP isn't comparable
	Sp  uint16
	Dp  uint16
	P   uint8
}

// Return a string representation of this FlowKey suitable for printing
func (key FlowKey) String() string {
	return fmt.Sprintf("%s:%d -> %s:%d %d", key.Sip, key.Sp, key.Dip, key.Dp, key.P)
}

// Return the reverse of this FlowKey,
// with source and destination address and port flipped.
func (k FlowKey) Reverse() FlowKey {
	return FlowKey{k.Dip, k.Sip, k.Dp, k.Sp, k.P}
}

// Extract a flow key from a packet
func ExtractFlowKey(pkt gopacket.Packet) (k FlowKey) {
	nl := pkt.NetworkLayer()
	if nl == nil {
		// ain't nobody got time for non-IP packets. empty flow key.
		return
	}

	switch nl.(type) {
	case *layers.IPv4:
		k.Sip = nl.(*layers.IPv4).SrcIP.String()
		k.Dip = nl.(*layers.IPv4).SrcIP.String()
		k.P = uint8(nl.(*layers.IPv4).Protocol)
	case *layers.IPv6:
		k.Sip = nl.(*layers.IPv6).SrcIP.String()
		k.Dip = nl.(*layers.IPv6).SrcIP.String()
		k.P = uint8(nl.(*layers.IPv6).NextHeader)
	default:
		// um i got nothing, empty flow key.
		return
	}

	tl := pkt.TransportLayer()
	if tl == nil {
		// no transport layer, so try to decode ICMP
		if icmpl := pkt.Layer(layers.LayerTypeICMPv4).(*layers.ICMPv4); icmpl != nil {
			icmptype := icmpl.TypeCode.Type()
			if icmptype == layers.ICMPv4TypeDestinationUnreachable ||
				icmptype == layers.ICMPv4TypeTimeExceeded ||
				icmptype == layers.ICMPv4TypeParameterProblem {
				// Account ICMPv4 messages from routers to the
				// reverse flow they belong to
				sk := ExtractFlowKey(gopacket.NewPacket(icmpl.LayerPayload(),
					layers.LayerTypeIPv4,
					gopacket.Default))
				k.Sip = sk.Dip
				k.Sp = sk.Dp
				k.P = sk.P
			} else {
				k.Sp = uint16(icmpl.TypeCode.Type())
				k.Dp = uint16(icmpl.TypeCode.Code())
			}
		} else if icmpl := pkt.Layer(layers.LayerTypeICMPv6).(*layers.ICMPv6); icmpl != nil {
			icmptype := icmpl.TypeCode.Type()
			if icmptype == layers.ICMPv6TypeDestinationUnreachable ||
				icmptype == layers.ICMPv6TypeTimeExceeded ||
				icmptype == layers.ICMPv6TypePacketTooBig ||
				icmptype == layers.ICMPv6TypeParameterProblem {
				// Account ICMPv6 messages from routers to the
				// reverse flow they belong to
				sk := ExtractFlowKey(gopacket.NewPacket(icmpl.LayerPayload(),
					layers.LayerTypeIPv6,
					gopacket.Default))
				k.Sip = sk.Dip
				k.Sp = sk.Dp
				k.P = sk.P
			} else {
				k.Sp = uint16(icmpl.TypeCode.Type())
				k.Dp = uint16(icmpl.TypeCode.Code())
			}
		} else {
			// no icmp, no transport, no ports for you
			return
		}
	}

	switch tl.(type) {
	case *layers.TCP:
		k.Sp = uint16(tl.(*layers.TCP).SrcPort)
		k.Dp = uint16(tl.(*layers.TCP).DstPort)
	case *layers.UDP:
		k.Sp = uint16(tl.(*layers.UDP).SrcPort)
		k.Dp = uint16(tl.(*layers.UDP).DstPort)
	case *layers.UDPLite:
		k.Sp = uint16(tl.(*layers.UDPLite).SrcPort)
		k.Dp = uint16(tl.(*layers.UDPLite).DstPort)
	case *layers.SCTP:
		k.Sp = uint16(tl.(*layers.SCTP).SrcPort)
		k.Dp = uint16(tl.(*layers.SCTP).DstPort)
	default:
		// no transport layer we know about, so no ports.
		return
	}

	// key set
	return
}

// FIXME: consider making FlowEntry an interface
// and add a factory instead of doing runtime typing here.
// ...runtime typing is kind of pythonic...
type FlowEntry struct {
	Key FlowKey

	Counters  map[int]int
	Data      map[int]interface{}
	StartTime time.Time
	LastTime  time.Time

	packetChannel chan PacketEvent
	reapChannel   chan FlowKey
	flowFinishing chan struct{}
	flowDone      chan struct{}
}

// Mark this flow as finished. To be called by a layer chain function
// on the last packet of the flow, or by an idle flow reaper.

func (fe *FlowEntry) Finish() {
	fe.reapChannel <- fe.Key
}

func (fe *FlowEntry) String() string {
	// FIXME this gets fixed when moving to statically typed flow entries
	return fmt.Sprintf("%v", *fe)
}

type FlowChainFn func(*FlowEntry) bool

type PacketChainFn func(*FlowEntry, PacketEvent) bool

type LayerChainFn func(*FlowEntry, PacketEvent, gopacket.Layer) bool

type layerChainEntry struct {
	LayerType gopacket.LayerType
	Fn        LayerChainFn
}

const IDLE_TIMEOUT = 30

// Contains the set of currently active flows
type FlowTable struct {
	// Function chain to run on the first packet in any given flow.
	// Used to set up state for subsequent chains
	initialChain []PacketChainFn

	// Function chain to run for each layer of each incoming packet
	layerChain []layerChainEntry

	// Function chain to run on concluded flows
	emitterChain []FlowChainFn

	// The current time as of the last packet added to the flow
	packetClock time.Time

	// Currently active flows, maps a flow key to a flow entry.
	active map[FlowKey]*FlowEntry

	// Idle queue
	idleq IdleQueue

	// Lock guarding access to the active table
	activeLock sync.RWMutex

	// Channel for flow entries to be reaped from the active queue
	reapChannel chan FlowKey
	reaperDone  chan struct{}

	// Channel for packet clock ticks
	tickChannel chan time.Time
}

func NewFlowTable() (ft *FlowTable) {
	ft = new(FlowTable)
	ft.initialChain = make([]PacketChainFn, 0)
	ft.layerChain = make([]layerChainEntry, 0)
	ft.emitterChain = make([]FlowChainFn, 0)
	ft.active = make(map[FlowKey]*FlowEntry)
	ft.reapChannel = make(chan FlowKey, CQLEN)
	ft.reaperDone = make(chan struct{})
	ft.tickChannel = make(chan time.Time)

	// start the flow table's emitter
	go ft.reapFinishedFlowEntries()

	// start the idle reaper
	go ft.reapIdleFlowEntries()

	return
}

func (ft *FlowTable) HandlePacket(pkt gopacket.Packet) {
	var emptyFlowKey FlowKey

	// advance the packet clock
	timestamp := pkt.Metadata().Timestamp
	ft.tickPacketClock(timestamp)

	// extract a flow key from the packet
	k := ExtractFlowKey(pkt)

	// drop packets with the zero key
	if k == emptyFlowKey {
		return
	}

	// get a flow entry for the flow key, tick the idle queue,
	// and send it the packet for further processing if not ignored.
	fe, rev := ft.flowEntry(k)
	if fe != nil {
		ft.idleq.Tick(k, timestamp)
		fe.packetChannel <- PacketEvent{pkt, timestamp, rev, false}
	}
}

func (ft *FlowTable) Shutdown() {

	log.Printf("in shutdown")

	ft.activeLock.RLock()
	keylist := make([]FlowKey, len(ft.active))
	for k := range ft.active {
		keylist = append(keylist, k)
	}
	ft.activeLock.RUnlock()

	for _, k := range keylist {
		ft.reapChannel <- k
		log.Printf("reaped %v", k)

	}

	// Shut down the reapers
	close(ft.tickChannel)
	close(ft.reapChannel)

	// and wait
	_ = <-ft.reaperDone
}

func (ft *FlowTable) AddInitialFunction(fn PacketChainFn) {
	ft.initialChain = append(ft.initialChain, fn)
	log.Printf("initialChain now %v", ft.initialChain)
}

func (ft *FlowTable) AddLayerFunction(fn LayerChainFn, layerType gopacket.LayerType) {
	ft.layerChain = append(ft.layerChain, layerChainEntry{layerType, fn})
	log.Printf("layerChain now %v", ft.layerChain)
}

func (ft *FlowTable) AddEmitterFunction(fn FlowChainFn) {
	ft.emitterChain = append(ft.emitterChain, fn)
	log.Printf("emitterChain now %v", ft.emitterChain)
}

func (ft *FlowTable) flowEntry(key FlowKey) (fe *FlowEntry, rev bool) {
	// First look for a flow entry in the active table
	ft.activeLock.RLock()
	fe = ft.active[key]
	ft.activeLock.RUnlock()

	if fe != nil {
		return fe, false
	}

	// Now look for a reverse flow entry
	ft.activeLock.RLock()
	fe = ft.active[key.Reverse()]
	ft.activeLock.RUnlock()

	if fe != nil {
		return fe, true
	}

	// No entry available. Create a new one.
	fe = new(FlowEntry)
	fe.Key = key
	fe.Counters = make(map[int]int)
	fe.Data = make(map[int]interface{})
	fe.packetChannel = make(chan PacketEvent)
	fe.reapChannel = ft.reapChannel
	fe.flowFinishing = make(chan struct{})
	fe.flowDone = make(chan struct{})

	// Now start running the function chain for this flow entry
	// in its own goroutine
	go func() {
		initial := true

		// Run until we get a finishing signal.
		for {
			select {
			case pe := <-fe.packetChannel:
				if fe.LastTime.Before(pe.Timestamp) {
					fe.LastTime = pe.Timestamp
				}

				// The initial function chain is used to set up
				// state in the FlowEntry to be used by
				// functions in the layer chain.
				if initial {
					fe.StartTime = pe.Timestamp
					for _, fn := range ft.initialChain {
						if !fn(fe, pe) {
							break
						}
					}
					initial = false
				}

				// The layer chain contains functions to be
				// called for specified layers in a
				// specified order
				for _, le := range ft.layerChain {
					layer := pe.Packet.Layer(le.LayerType)
					if layer != nil {
						if !le.Fn(fe, pe, layer) {
							break
						}
					}
				}
			case <-fe.flowFinishing:
				break
			}
		}

		log.Printf("flowroutine for key %v exiting", fe.Key)

		// let the reaper know we're done
		close(fe.flowDone)
	}()

	// Add the flow to the active table
	ft.activeLock.Lock()
	ft.active[key] = fe
	ft.activeLock.Unlock()
	log.Printf("added new flow entry for key %s", key.String())

	return fe, false
}

func quantize(tick time.Time, quantum int64) time.Time {
	return time.Unix((tick.Unix()/quantum)*quantum, 0)
}

const BIN_QUANTUM = 10

func (ft *FlowTable) tickPacketClock(tick time.Time) {
	if ft.packetClock.After(tick) {
		return
	}

	var lastClock time.Time
	if ft.packetClock.After(time.Time{}) {
		lastClock = ft.packetClock
	} else {
		lastClock = tick
		log.Printf("initializing packet clock %v", tick)
	}

	ft.packetClock = tick

	// generate ticks for reapIdleFlowEntries
	for i, j := quantize(lastClock, BIN_QUANTUM), quantize(tick, BIN_QUANTUM); i.Before(j); i = i.Add(BIN_QUANTUM * time.Second) {
		log.Printf("ticking to %v", i)
		ft.tickChannel <- i
	}

}

func (ft *FlowTable) reapIdleFlowEntries() {
	log.Println("idle reaper up")

	for tick := range ft.tickChannel {
		endtime := tick.Add(IDLE_TIMEOUT * time.Second)
		for {
			k, ok := ft.idleq.NextIdleBefore(endtime)
			if ok {
				ft.reapChannel <- k
			} else {
				break
			}
		}
	}
	log.Println("idle reaper down")
}

func (ft *FlowTable) reapFinishedFlowEntries() {
	log.Println("finished reaper up")

	for k := range ft.reapChannel {
		// get the flow
		ft.activeLock.RLock()
		fe := ft.active[k]
		ft.activeLock.RUnlock()

		// remove the flow from the active table
		ft.activeLock.Lock()
		delete(ft.active, k)
		ft.activeLock.Unlock()

		// signal flow's goroutine to complete
		fe.flowFinishing <- struct{}{}

		// and wait for it to do so
		_ = <-fe.flowDone

		// now run the emitter chain
		for _, fn := range ft.emitterChain {
			if !fn(fe) {
				break
			}
		}
	}

	// signal done
	close(ft.reaperDone)

	log.Println("finished reaper down")
}
<commit_msg>fix keylist extraction in shutdown<commit_after>package mokumokuren

import (
	"fmt"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"log"
	//"net"
	//"strconv"
	"sync"
	"time"
)

const CQLEN = 64

type PacketEvent struct {
	Packet    gopacket.Packet
	Timestamp time.Time
	Reverse   bool
	Finish    bool
}

type FlowKey struct {
	Sip string // FIXME this is a stupid hack but net.IP isn't comparable
	Dip string // FIXME this is a stupid hack but net.IP isn't comparable
	Sp  uint16
	Dp  uint16
	P   uint8
}

// Return a string representation of this FlowKey suitable for printing
func (key FlowKey) String() string {
	return fmt.Sprintf("%s:%d -> %s:%d %d", key.Sip, key.Sp, key.Dip, key.Dp, key.P)
}

// Return the reverse of this FlowKey,
// with source and destination address and port flipped.
func (k FlowKey) Reverse() FlowKey {
	return FlowKey{k.Dip, k.Sip, k.Dp, k.Sp, k.P}
}

// Extract a flow key from a packet
func ExtractFlowKey(pkt gopacket.Packet) (k FlowKey) {
	nl := pkt.NetworkLayer()
	if nl == nil {
		// ain't nobody got time for non-IP packets. empty flow key.
		return
	}

	switch nl.(type) {
	case *layers.IPv4:
		k.Sip = nl.(*layers.IPv4).SrcIP.String()
		k.Dip = nl.(*layers.IPv4).DstIP.String()
		k.P = uint8(nl.(*layers.IPv4).Protocol)
	case *layers.IPv6:
		k.Sip = nl.(*layers.IPv6).SrcIP.String()
		k.Dip = nl.(*layers.IPv6).DstIP.String()
		k.P = uint8(nl.(*layers.IPv6).NextHeader)
	default:
		// um i got nothing, empty flow key.
		return
	}

	tl := pkt.TransportLayer()
	if tl == nil {
		// no transport layer, so try to decode ICMP
		if icmpl := pkt.Layer(layers.LayerTypeICMPv4).(*layers.ICMPv4); icmpl != nil {
			icmptype := icmpl.TypeCode.Type()
			if icmptype == layers.ICMPv4TypeDestinationUnreachable ||
				icmptype == layers.ICMPv4TypeTimeExceeded ||
				icmptype == layers.ICMPv4TypeParameterProblem {
				// Account ICMPv4 messages from routers to the
				// reverse flow they belong to
				sk := ExtractFlowKey(gopacket.NewPacket(icmpl.LayerPayload(),
					layers.LayerTypeIPv4,
					gopacket.Default))
				k.Sip = sk.Dip
				k.Sp = sk.Dp
				k.P = sk.P
			} else {
				k.Sp = uint16(icmpl.TypeCode.Type())
				k.Dp = uint16(icmpl.TypeCode.Code())
			}
		} else if icmpl := pkt.Layer(layers.LayerTypeICMPv6).(*layers.ICMPv6); icmpl != nil {
			icmptype := icmpl.TypeCode.Type()
			if icmptype == layers.ICMPv6TypeDestinationUnreachable ||
				icmptype == layers.ICMPv6TypeTimeExceeded ||
				icmptype == layers.ICMPv6TypePacketTooBig ||
				icmptype == layers.ICMPv6TypeParameterProblem {
				// Account ICMPv6 messages from routers to the
				// reverse flow they belong to
				sk := ExtractFlowKey(gopacket.NewPacket(icmpl.LayerPayload(),
					layers.LayerTypeIPv6,
					gopacket.Default))
				k.Sip = sk.Dip
				k.Sp = sk.Dp
				k.P = sk.P
			} else {
				k.Sp = uint16(icmpl.TypeCode.Type())
				k.Dp = uint16(icmpl.TypeCode.Code())
			}
		} else {
			// no icmp, no transport, no ports for you
			return
		}
	}

	switch tl.(type) {
	case *layers.TCP:
		k.Sp = uint16(tl.(*layers.TCP).SrcPort)
		k.Dp = uint16(tl.(*layers.TCP).DstPort)
	case *layers.UDP:
		k.Sp = uint16(tl.(*layers.UDP).SrcPort)
		k.Dp = uint16(tl.(*layers.UDP).DstPort)
	case *layers.UDPLite:
		k.Sp = uint16(tl.(*layers.UDPLite).SrcPort)
		k.Dp = uint16(tl.(*layers.UDPLite).DstPort)
	case *layers.SCTP:
		k.Sp = uint16(tl.(*layers.SCTP).SrcPort)
		k.Dp = uint16(tl.(*layers.SCTP).DstPort)
	default:
		// no transport layer we know about, so no ports.
		return
	}

	// key set
	return
}

// FIXME: make FlowEntry an interface
// and add a factory instead of doing runtime typing here.

type FlowEntry struct {
	Key FlowKey

	Counters  map[int]int
	Data      map[int]interface{}
	StartTime time.Time
	LastTime  time.Time

	packetChannel chan PacketEvent
	reapChannel   chan FlowKey
	flowFinishing chan struct{}
	flowDone      chan struct{}
}

// Mark this flow as finished. To be called by a layer chain function
// on the last packet of the flow, or by an idle flow reaper.

func (fe *FlowEntry) Finish() {
	fe.reapChannel <- fe.Key
}

func (fe *FlowEntry) String() string {
	// FIXME this gets fixed when moving to statically typed flow entries
	return fmt.Sprintf("%v", *fe)
}

type FlowChainFn func(*FlowEntry) bool

type PacketChainFn func(*FlowEntry, PacketEvent) bool

type LayerChainFn func(*FlowEntry, PacketEvent, gopacket.Layer) bool

type layerChainEntry struct {
	LayerType gopacket.LayerType
	Fn        LayerChainFn
}

const IDLE_TIMEOUT = 30

// Contains the set of currently active flows
type FlowTable struct {
	// Function chain to run on the first packet in any given flow.
	// Used to set up state for subsequent chains
	initialChain []PacketChainFn

	// Function chain to run for each layer of each incoming packet
	layerChain []layerChainEntry

	// Function chain to run on concluded flows
	emitterChain []FlowChainFn

	// The current time as of the last packet added to the flow
	packetClock time.Time

	// Currently active flows, maps a flow key to a flow entry.
	active map[FlowKey]*FlowEntry

	// Idle queue
	idleq IdleQueue

	// Lock guarding access to the active table
	activeLock sync.RWMutex

	// Channel for flow entries to be reaped from the active queue
	reapChannel chan FlowKey
	reaperDone  chan struct{}

	// Channel for packet clock ticks
	tickChannel chan time.Time
}

func NewFlowTable() (ft *FlowTable) {
	ft = new(FlowTable)
	ft.initialChain = make([]PacketChainFn, 0)
	ft.layerChain = make([]layerChainEntry, 0)
	ft.emitterChain = make([]FlowChainFn, 0)
	ft.active = make(map[FlowKey]*FlowEntry)
	ft.reapChannel = make(chan FlowKey, CQLEN)
	ft.reaperDone = make(chan struct{})
	ft.tickChannel = make(chan time.Time)

	// start the flow table's emitter
	go ft.reapFinishedFlowEntries()

	// start the idle reaper
	go ft.reapIdleFlowEntries()

	return
}

func (ft *FlowTable) HandlePacket(pkt gopacket.Packet) {
	var emptyFlowKey FlowKey

	// advance the packet clock
	timestamp := pkt.Metadata().Timestamp
	ft.tickPacketClock(timestamp)

	// extract a flow key from the packet
	k := ExtractFlowKey(pkt)

	// drop packets with the zero key
	if k == emptyFlowKey {
		return
	}

	// get a flow entry for the flow key, tick the idle queue,
	// and send it the packet for further processing if not ignored.
	fe, rev := ft.flowEntry(k)
	if fe != nil {
		ft.idleq.Tick(k, timestamp)
		fe.packetChannel <- PacketEvent{pkt, timestamp, rev, false}
	}
}

func (ft *FlowTable) Shutdown() {

	log.Printf("in shutdown")

	ft.activeLock.RLock()
	keylist := make([]FlowKey, len(ft.active))
	i := 0
	for k := range ft.active {
		keylist[i] = k
		i++
	}
	ft.activeLock.RUnlock()

	for _, k := range keylist {
		ft.reapChannel <- k
		log.Printf("reaped %v on shutdown", k)
	}

	// Shut down the reapers
	close(ft.tickChannel)
	close(ft.reapChannel)

	// and wait
	_ = <-ft.reaperDone
}

func (ft *FlowTable) AddInitialFunction(fn PacketChainFn) {
	ft.initialChain = append(ft.initialChain, fn)
	log.Printf("initialChain now %v", ft.initialChain)
}

func (ft *FlowTable) AddLayerFunction(fn LayerChainFn, layerType gopacket.LayerType) {
	ft.layerChain = append(ft.layerChain, layerChainEntry{layerType, fn})
	log.Printf("layerChain now %v", ft.layerChain)
}

func (ft *FlowTable) AddEmitterFunction(fn FlowChainFn) {
	ft.emitterChain = append(ft.emitterChain, fn)
	log.Printf("emitterChain now %v", ft.emitterChain)
}

func (ft *FlowTable) flowEntry(key FlowKey) (fe *FlowEntry, rev bool) {
	// First look for a flow entry in the active table
	ft.activeLock.RLock()
	fe = ft.active[key]
	ft.activeLock.RUnlock()

	if fe != nil {
		return fe, false
	}

	// Now look for a reverse flow entry
	ft.activeLock.RLock()
	fe = ft.active[key.Reverse()]
	ft.activeLock.RUnlock()

	if fe != nil {
		return fe, true
	}

	// No entry available. Create a new one.
	fe = new(FlowEntry)
	fe.Key = key
	fe.Counters = make(map[int]int)
	fe.Data = make(map[int]interface{})
	fe.packetChannel = make(chan PacketEvent)
	fe.reapChannel = ft.reapChannel
	fe.flowFinishing = make(chan struct{})
	fe.flowDone = make(chan struct{})

	// Now start running the function chain for this flow entry
	// in its own goroutine
	go func() {
		initial := true

		// Run until we get a finishing signal.
		for {
			select {
			case pe := <-fe.packetChannel:
				if fe.LastTime.Before(pe.Timestamp) {
					fe.LastTime = pe.Timestamp
				}

				// The initial function chain is used to set up
				// state in the FlowEntry to be used by
				// functions in the layer chain.
				if initial {
					fe.StartTime = pe.Timestamp
					for _, fn := range ft.initialChain {
						if !fn(fe, pe) {
							break
						}
					}
					initial = false
				}

				// The layer chain contains functions to be
				// called for specified layers in a
				// specified order
				for _, le := range ft.layerChain {
					layer := pe.Packet.Layer(le.LayerType)
					if layer != nil {
						if !le.Fn(fe, pe, layer) {
							break
						}
					}
				}
			case <-fe.flowFinishing:
				break
			}
		}

		log.Printf("flowroutine for key %v exiting", fe.Key)

		// let the reaper know we're done
		close(fe.flowDone)
	}()

	// Add the flow to the active table
	ft.activeLock.Lock()
	ft.active[key] = fe
	ft.activeLock.Unlock()
	log.Printf("added new flow entry for key %s", fe.Key.String())

	return fe, false
}

func quantize(tick time.Time, quantum int64) time.Time {
	return time.Unix((tick.Unix()/quantum)*quantum, 0)
}

const BIN_QUANTUM = 10

func (ft *FlowTable) tickPacketClock(tick time.Time) {
	if ft.packetClock.After(tick) {
		return
	}

	var lastClock time.Time
	if ft.packetClock.After(time.Time{}) {
		lastClock = ft.packetClock
	} else {
		lastClock = tick
		log.Printf("initializing packet clock %v", tick)
	}

	ft.packetClock = tick

	// generate ticks for reapIdleFlowEntries
	for i, j := quantize(lastClock, BIN_QUANTUM), quantize(tick, BIN_QUANTUM); i.Before(j); i = i.Add(BIN_QUANTUM * time.Second) {
		log.Printf("ticking to %v", i)
		ft.tickChannel <- i
	}

}

func (ft *FlowTable) reapIdleFlowEntries() {
	log.Println("idle reaper up")

	for tick := range ft.tickChannel {
		endtime := tick.Add(IDLE_TIMEOUT * time.Second)
		for {
			k, ok := ft.idleq.NextIdleBefore(endtime)
			if ok {
				ft.reapChannel <- k
			} else {
				break
			}
		}
	}
	log.Println("idle reaper down")
}

func (ft *FlowTable) reapFinishedFlowEntries() {
	log.Println("finished reaper up")

	for k := range ft.reapChannel {
		// get the flow
		ft.activeLock.RLock()
		fe := ft.active[k]
		ft.activeLock.RUnlock()

		// remove the flow from the active table
		ft.activeLock.Lock()
		delete(ft.active, k)
		ft.activeLock.Unlock()

		// signal flow's goroutine to complete
		fe.flowFinishing <- struct{}{}

		// and wait for it to do so
		_ = <-fe.flowDone

		// now run the emitter chain
		for _, fn := range ft.emitterChain {
			if !fn(fe) {
				break
			}
		}
	}

	// signal done
	close(ft.reaperDone)

	log.Println("finished reaper down")
}
<|endoftext|>


<commit_before>package mongo

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

	"github.com/juju/loggo"

	"launchpad.net/juju-core/upstart"
	"launchpad.net/juju-core/utils"
)

const (
	maxFiles = 65000
	maxProcs = 20000
)

var (
	logger = loggo.GetLogger("juju.agent.mongo")

	oldMongoServiceName = "juju-db"

	// JujuMongodPath holds the default path to the juju-specific mongod.
	JujuMongodPath = "/usr/lib/juju/bin/mongod"
)

// MongoPath returns the executable path to be used to run mongod on this
// machine. If the juju-bundled version of mongo exists, it will return that
// path, otherwise it will return the command to run mongod from the path.
func MongodPath() (string, error) {
	if _, err := os.Stat(JujuMongodPath); err == nil {
		return JujuMongodPath, nil
	}

	path, err := exec.LookPath("mongod")
	if err != nil {
		return "", err
	}
	return path, nil
}

// EnsureMongoServer ensures that the correct mongo upstart script is installed
// and running.
//
// This method will remove old versions of the mongo upstart script as necessary
// before installing the new version.
func EnsureMongoServer(dir string, port int) error {
	name := makeServiceName(mongoScriptVersion)
	service, err := MongoUpstartService(name, dir, port)
	if err != nil {
		return err
	}
	if service.Installed() {
		return nil
	}

	if err := removeOldMongoServices(mongoScriptVersion); err != nil {
		return err
	}

	if err := makeJournalDirs(dir); err != nil {
		return err
	}

	if err := service.Install(); err != nil {
		return fmt.Errorf("failed to install mongo service %q: %v", service.Name, err)
	}
	return service.Start()
}

func makeJournalDirs(dir string) error {
	journalDir := path.Join(dir, "journal")

	if err := os.MkdirAll(journalDir, 0700); err != nil {
		logger.Errorf("failed to make mongo journal dir %s: %v", journalDir, err)
		return err
	}

	// manually create the prealloc files, since otherwise they get created as 100M files.
	zeroes := make([]byte, 64*1024) // should be enough for anyone
	for x := 0; x < 3; x++ {
		name := fmt.Sprintf("prealloc.%d", x)
		filename := filepath.Join(journalDir, name)
		f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
		if err != nil {
			return fmt.Errorf("failed to open mongo prealloc file %q: %v", filename, err)
		}
		defer f.Close()
		for total := 0; total < 1024*1024; {
			n, err := f.Write(zeroes)
			if err != nil {
				return fmt.Errorf("failed to write to mongo prealloc file %q: %v", filename, err)
			}
			total += n
		}
	}
	return nil
}

// removeOldMongoServices looks for any old juju mongo upstart scripts and
// removes them.
func removeOldMongoServices(curVersion int) error {
	old := upstart.NewService(oldMongoServiceName)
	if err := old.StopAndRemove(); err != nil {
		logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err)
		return err
	}

	// the new formatting for the script name started at version 2
	for x := 2; x < curVersion; x++ {
		old := upstart.NewService(makeServiceName(x))
		if err := old.StopAndRemove(); err != nil {
			logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err)
			return err
		}
	}
	return nil
}

func makeServiceName(version int) string {
	return fmt.Sprintf("juju-db-v%d", version)
}

// mongoScriptVersion keeps track of changes to the mongo upstart script.
// Update this version when you update the script that gets installed from
// MongoUpstartService.
const mongoScriptVersion = 2

// MongoUpstartService returns the upstart config for the mongo state service.
//
// This method assumes there is a server.pem keyfile in dataDir.
func MongoUpstartService(name, dataDir string, port int) (*upstart.Conf, error) {

	keyFile := path.Join(dataDir, "server.pem")
	svc := upstart.NewService(name)

	dbDir := path.Join(dataDir, "db")

	mongodpath, err := MongodPath()
	if err != nil {
		return nil, err
	}

	conf := &upstart.Conf{
		Service: *svc,
		Desc:    "juju state database",
		Limit: map[string]string{
			"nofile": fmt.Sprintf("%d %d", maxFiles, maxFiles),
			"nproc":  fmt.Sprintf("%d %d", maxProcs, maxProcs),
		},
		Cmd: mongodpath +
			" --auth" +
			" --dbpath=" + dbDir +
			" --sslOnNormalPorts" +
			" --sslPEMKeyFile " + utils.ShQuote(keyFile) +
			" --sslPEMKeyPassword ignored" +
			" --bind_ip 0.0.0.0" +
			" --port " + fmt.Sprint(port) +
			" --noprealloc" +
			" --syslog" +
			" --smallfiles",
		// TODO(Nate): uncomment when we commit HA stuff
		// +
		//	" --replSet juju",
	}
	return conf, nil
}
<commit_msg>[r=dimitern],[bug=1286279] agent/mongo: Revert attempted use of juju-mongodb<commit_after>package mongo

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

	"github.com/juju/loggo"

	"launchpad.net/juju-core/upstart"
	"launchpad.net/juju-core/utils"
)

const (
	maxFiles = 65000
	maxProcs = 20000
)

var (
	logger = loggo.GetLogger("juju.agent.mongo")

	oldMongoServiceName = "juju-db"

	// JujuMongodPath holds the default path to the juju-specific mongod.
	JujuMongodPath = "/usr/lib/juju/bin/mongod"
)

// MongoPath returns the executable path to be used to run mongod on this
// machine. If the juju-bundled version of mongo exists, it will return that
// path, otherwise it will return the command to run mongod from the path.
func MongodPath() (string, error) {
	if _, err := os.Stat(JujuMongodPath); err == nil {
		return JujuMongodPath, nil
	}

	path, err := exec.LookPath("mongod")
	if err != nil {
		return "", err
	}
	return path, nil
}

// EnsureMongoServer ensures that the correct mongo upstart script is installed
// and running.
//
// This method will remove old versions of the mongo upstart script as necessary
// before installing the new version.
func EnsureMongoServer(dir string, port int) error {
	name := makeServiceName(mongoScriptVersion)
	service, err := MongoUpstartService(name, dir, port)
	if err != nil {
		return err
	}
	if service.Installed() {
		return nil
	}

	if err := removeOldMongoServices(mongoScriptVersion); err != nil {
		return err
	}

	if err := makeJournalDirs(dir); err != nil {
		return err
	}

	if err := service.Install(); err != nil {
		return fmt.Errorf("failed to install mongo service %q: %v", service.Name, err)
	}
	return service.Start()
}

func makeJournalDirs(dir string) error {
	journalDir := path.Join(dir, "journal")

	if err := os.MkdirAll(journalDir, 0700); err != nil {
		logger.Errorf("failed to make mongo journal dir %s: %v", journalDir, err)
		return err
	}

	// manually create the prealloc files, since otherwise they get created as 100M files.
	zeroes := make([]byte, 64*1024) // should be enough for anyone
	for x := 0; x < 3; x++ {
		name := fmt.Sprintf("prealloc.%d", x)
		filename := filepath.Join(journalDir, name)
		f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
		if err != nil {
			return fmt.Errorf("failed to open mongo prealloc file %q: %v", filename, err)
		}
		defer f.Close()
		for total := 0; total < 1024*1024; {
			n, err := f.Write(zeroes)
			if err != nil {
				return fmt.Errorf("failed to write to mongo prealloc file %q: %v", filename, err)
			}
			total += n
		}
	}
	return nil
}

// removeOldMongoServices looks for any old juju mongo upstart scripts and
// removes them.
func removeOldMongoServices(curVersion int) error {
	old := upstart.NewService(oldMongoServiceName)
	if err := old.StopAndRemove(); err != nil {
		logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err)
		return err
	}

	// the new formatting for the script name started at version 2
	for x := 2; x < curVersion; x++ {
		old := upstart.NewService(makeServiceName(x))
		if err := old.StopAndRemove(); err != nil {
			logger.Errorf("Failed to remove old mongo upstart service %q: %v", old.Name, err)
			return err
		}
	}
	return nil
}

func makeServiceName(version int) string {
	return fmt.Sprintf("juju-db-v%d", version)
}

// mongoScriptVersion keeps track of changes to the mongo upstart script.
// Update this version when you update the script that gets installed from
// MongoUpstartService.
const mongoScriptVersion = 2

// MongoUpstartService returns the upstart config for the mongo state service.
//
// This method assumes there is a server.pem keyfile in dataDir.
func MongoUpstartService(name, dataDir string, port int) (*upstart.Conf, error) {

	keyFile := path.Join(dataDir, "server.pem")
	svc := upstart.NewService(name)

	dbDir := path.Join(dataDir, "db")

	conf := &upstart.Conf{
		Service: *svc,
		Desc:    "juju state database",
		Limit: map[string]string{
			"nofile": fmt.Sprintf("%d %d", maxFiles, maxFiles),
			"nproc":  fmt.Sprintf("%d %d", maxProcs, maxProcs),
		},
		Cmd: "/usr/bin/mongod" +
			" --auth" +
			" --dbpath=" + dbDir +
			" --sslOnNormalPorts" +
			" --sslPEMKeyFile " + utils.ShQuote(keyFile) +
			" --sslPEMKeyPassword ignored" +
			" --bind_ip 0.0.0.0" +
			" --port " + fmt.Sprint(port) +
			" --noprealloc" +
			" --syslog" +
			" --smallfiles",
		// TODO(Nate): uncomment when we commit HA stuff
		// +
		//	" --replSet juju",
	}
	return conf, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"os"

	"github.com/jvikstedt/alarm-bot/configuration"
	"github.com/jvikstedt/alarm-bot/mailer"
	"github.com/jvikstedt/alarm-bot/tracker"
)

var mail *mailer.Mailer
var conf *configuration.Configuration

func init() {
	setupConf()
	setupMailer()
}

func main() {
	fmt.Println("Starting AlarmBot")
	for _, c := range conf.TestObjects {
		t := tracker.NewTracker(c.Name, c.HistoryCount)
		trackResult, err := t.Perform(c.URL, c.MatchString, c.Status)
		if err != nil {
			fmt.Println(err)
			mail.Send("AlarmBot Error @ "+trackResult.TargetURL, err.Error(), c.MailTo)
		} else {
			fmt.Println(trackResult)
		}
		t.SaveHistory()
	}
	fmt.Println("Closing AlarmBot")
}

func setupConf() {
	confName := os.Getenv("ALARM_BOT_CONFIG")
	if confName == "" {
		confName = "./config.json"
	}
	conf = configuration.NewConfiguration(confName)
}

func setupMailer() {
	mail = mailer.NewMailer(conf.MailSetting.Host, conf.MailSetting.From, conf.MailSetting.Password, conf.MailSetting.Port)
}
<commit_msg>Moved default config file one up<commit_after>package main

import (
	"fmt"

	"os"

	"github.com/jvikstedt/alarm-bot/configuration"
	"github.com/jvikstedt/alarm-bot/mailer"
	"github.com/jvikstedt/alarm-bot/tracker"
)

var mail *mailer.Mailer
var conf *configuration.Configuration

func init() {
	setupConf()
	setupMailer()
}

func main() {
	fmt.Println("Starting AlarmBot")
	for _, c := range conf.TestObjects {
		t := tracker.NewTracker(c.Name, c.HistoryCount)
		trackResult, err := t.Perform(c.URL, c.MatchString, c.Status)
		if err != nil {
			fmt.Println(err)
			mail.Send("AlarmBot Error @ "+trackResult.TargetURL, err.Error(), c.MailTo)
		} else {
			fmt.Println(trackResult)
		}
		t.SaveHistory()
	}
	fmt.Println("Closing AlarmBot")
}

func setupConf() {
	confName := os.Getenv("ALARM_BOT_CONFIG")
	if confName == "" {
		confName = "../config.json"
	}
	conf = configuration.NewConfiguration(confName)
}

func setupMailer() {
	mail = mailer.NewMailer(conf.MailSetting.Host, conf.MailSetting.From, conf.MailSetting.Password, conf.MailSetting.Port)
}
<|endoftext|>


<commit_before>package spider

type Data map[string]interface{}

func (this Data) Has(key string) bool {
    _, exists := this[key]

    return exists
}

func (this Data) GetString(key string) (string, bool) {
    if v, ok := this[key]; ok {
        if vv, ok := v.(string); ok {
            return vv, true
        }
    }

    return "", false
}

func (this Data) MustGetString(key string) string {
    if v, ok := this.GetString(key); ok {
        return v
    }

    panic("Task has no string data: " + key)
}

func (this Data) GetInt(key string) (int, bool) {
    if v, ok := this[key]; ok {
        if vv, ok := v.(int); ok {
            return vv, true
        }
    }

    return 0, false
}

func (this Data) MustGetInt(key string) int {
    if v, ok := this.GetInt(key); ok {
        return v
    }

    panic("Task has no int data: " + key)
}

func (this Data) GetBytes(key string) ([]byte, bool) {
    if v, ok := this[key]; ok {
        if vv, ok := v.([]byte); ok {
            return vv, true
        }
    }

    return nil, false
}

func (this Data) MustGetBytes(key string) ([]byte) {
    if v, ok := this.GetBytes(key); ok {
        return v
    }

    panic("Task has no bytes data: " + key)
}<commit_msg>add copy right<commit_after>// Copyright 2015 Liu Dong <ddliuhb@gmail.com>.
// Licensed under the MIT license.

package spider

type Data map[string]interface{}

func (this Data) Has(key string) bool {
    _, exists := this[key]

    return exists
}

func (this Data) GetString(key string) (string, bool) {
    if v, ok := this[key]; ok {
        if vv, ok := v.(string); ok {
            return vv, true
        }
    }

    return "", false
}

func (this Data) MustGetString(key string) string {
    if v, ok := this.GetString(key); ok {
        return v
    }

    panic("Task has no string data: " + key)
}

func (this Data) GetInt(key string) (int, bool) {
    if v, ok := this[key]; ok {
        if vv, ok := v.(int); ok {
            return vv, true
        }
    }

    return 0, false
}

func (this Data) MustGetInt(key string) int {
    if v, ok := this.GetInt(key); ok {
        return v
    }

    panic("Task has no int data: " + key)
}

func (this Data) GetBytes(key string) ([]byte, bool) {
    if v, ok := this[key]; ok {
        if vv, ok := v.([]byte); ok {
            return vv, true
        }
    }

    return nil, false
}

func (this Data) MustGetBytes(key string) ([]byte) {
    if v, ok := this.GetBytes(key); ok {
        return v
    }

    panic("Task has no bytes data: " + key)
}<|endoftext|>


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

package service

import (
	"fmt"
)

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

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

type GetNodeDataArgs struct {
	Site, Path, File string
}

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

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

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

type UpdateNodeArgs struct {
	Site string
	Node NodeInfo
}

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

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

package service

import (
	"fmt"
)

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

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

type GetNodeDataArgs struct {
	Site, Path, File string
}

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

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

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

type UpdateNodeArgs struct {
	Site string
	Node NodeInfo
}

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

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


<commit_before>package aws

import (
	"fmt"
	"log"
	"net"
	"time"

	"github.com/hashicorp/terraform/helper/resource"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsEipAssociation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsEipAssociationCreate,
		Read:   resourceAwsEipAssociationRead,
		Delete: resourceAwsEipAssociationDelete,

		Schema: map[string]*schema.Schema{
			"allocation_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"allow_reassociation": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},

			"instance_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"network_interface_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"private_ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"public_ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsEipAssociationCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	request := &ec2.AssociateAddressInput{}

	if v, ok := d.GetOk("allocation_id"); ok {
		request.AllocationId = aws.String(v.(string))
	}
	if v, ok := d.GetOk("allow_reassociation"); ok {
		request.AllowReassociation = aws.Bool(v.(bool))
	}
	if v, ok := d.GetOk("instance_id"); ok {
		request.InstanceId = aws.String(v.(string))
	}
	if v, ok := d.GetOk("network_interface_id"); ok {
		request.NetworkInterfaceId = aws.String(v.(string))
	}
	if v, ok := d.GetOk("private_ip_address"); ok {
		request.PrivateIpAddress = aws.String(v.(string))
	}
	if v, ok := d.GetOk("public_ip"); ok {
		request.PublicIp = aws.String(v.(string))
	}

	log.Printf("[DEBUG] EIP association configuration: %#v", request)

	var resp *ec2.AssociateAddressOutput
	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
		var err error
		resp, err = conn.AssociateAddress(request)
		if err != nil {
			if isAWSErr(err, "InvalidInstanceID", "pending instance") {
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("Error associating EIP: %s", err)
	}

	log.Printf("[DEBUG] EIP Assoc Response: %s", resp)

	supportedPlatforms := meta.(*AWSClient).supportedplatforms
	if len(supportedPlatforms) > 0 && !hasEc2Classic(supportedPlatforms) && resp.AssociationId == nil {
		// We expect no association ID in EC2 Classic
		// but still error out if ID is missing and we _know_ it's NOT EC2 Classic
		return fmt.Errorf("Received no EIP Association ID in account that doesn't support EC2 Classic (%q): %s",
			supportedPlatforms, resp)
	}

	if resp.AssociationId == nil {
		// This is required field for EC2 Classic per docs
		d.SetId(*request.PublicIp)
	} else {
		d.SetId(*resp.AssociationId)
	}

	return resourceAwsEipAssociationRead(d, meta)
}

func resourceAwsEipAssociationRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	request, err := describeAddressesById(d.Id(), meta.(*AWSClient).supportedplatforms)
	if err != nil {
		return err
	}

	response, err := conn.DescribeAddresses(request)
	if err != nil {
		return fmt.Errorf("Error reading EC2 Elastic IP %s: %#v", d.Get("allocation_id").(string), err)
	}

	if response.Addresses == nil || len(response.Addresses) == 0 {
		log.Printf("[INFO] EIP Association ID Not Found. Refreshing from state")
		d.SetId("")
		return nil
	}

	return readAwsEipAssociation(d, response.Addresses[0])
}

func resourceAwsEipAssociationDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	var opts *ec2.DisassociateAddressInput
	// We assume EC2 Classic if ID is a valid IPv4 address
	ip := net.ParseIP(d.Id())
	if ip != nil {
		supportedPlatforms := meta.(*AWSClient).supportedplatforms
		if len(supportedPlatforms) > 0 && !hasEc2Classic(supportedPlatforms) {
			return fmt.Errorf("Received IPv4 address as ID in account that doesn't support EC2 Classic (%q)",
				supportedPlatforms)
		}

		opts = &ec2.DisassociateAddressInput{
			PublicIp: aws.String(d.Id()),
		}
	} else {
		opts = &ec2.DisassociateAddressInput{
			AssociationId: aws.String(d.Id()),
		}
	}

	_, err := conn.DisassociateAddress(opts)
	if err != nil {
		return fmt.Errorf("Error deleting Elastic IP association: %s", err)
	}

	return nil
}

func readAwsEipAssociation(d *schema.ResourceData, address *ec2.Address) error {
	if err := d.Set("allocation_id", address.AllocationId); err != nil {
		return err
	}
	if err := d.Set("instance_id", address.InstanceId); err != nil {
		return err
	}
	if err := d.Set("network_interface_id", address.NetworkInterfaceId); err != nil {
		return err
	}
	if err := d.Set("private_ip_address", address.PrivateIpAddress); err != nil {
		return err
	}
	if err := d.Set("public_ip", address.PublicIp); err != nil {
		return err
	}

	return nil
}

func describeAddressesById(id string, supportedPlatforms []string) (*ec2.DescribeAddressesInput, error) {
	// We assume EC2 Classic if ID is a valid IPv4 address
	ip := net.ParseIP(id)
	if ip != nil {
		if len(supportedPlatforms) > 0 && !hasEc2Classic(supportedPlatforms) {
			return nil, fmt.Errorf("Received IPv4 address as ID in account that doesn't support EC2 Classic (%q)",
				supportedPlatforms)
		}

		return &ec2.DescribeAddressesInput{
			Filters: []*ec2.Filter{
				&ec2.Filter{
					Name:   aws.String("public-ip"),
					Values: []*string{aws.String(id)},
				},
				&ec2.Filter{
					Name:   aws.String("domain"),
					Values: []*string{aws.String("standard")},
				},
			},
		}, nil
	}

	return &ec2.DescribeAddressesInput{
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("association-id"),
				Values: []*string{aws.String(id)},
			},
		},
	}, nil
}
<commit_msg>resource/aws_eip_association: Prevent deletion error InvalidAssociationID.NotFound<commit_after>package aws

import (
	"fmt"
	"log"
	"net"
	"time"

	"github.com/hashicorp/terraform/helper/resource"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsEipAssociation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsEipAssociationCreate,
		Read:   resourceAwsEipAssociationRead,
		Delete: resourceAwsEipAssociationDelete,

		Schema: map[string]*schema.Schema{
			"allocation_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"allow_reassociation": &schema.Schema{
				Type:     schema.TypeBool,
				Optional: true,
				ForceNew: true,
			},

			"instance_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"network_interface_id": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"private_ip_address": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},

			"public_ip": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				ForceNew: true,
			},
		},
	}
}

func resourceAwsEipAssociationCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	request := &ec2.AssociateAddressInput{}

	if v, ok := d.GetOk("allocation_id"); ok {
		request.AllocationId = aws.String(v.(string))
	}
	if v, ok := d.GetOk("allow_reassociation"); ok {
		request.AllowReassociation = aws.Bool(v.(bool))
	}
	if v, ok := d.GetOk("instance_id"); ok {
		request.InstanceId = aws.String(v.(string))
	}
	if v, ok := d.GetOk("network_interface_id"); ok {
		request.NetworkInterfaceId = aws.String(v.(string))
	}
	if v, ok := d.GetOk("private_ip_address"); ok {
		request.PrivateIpAddress = aws.String(v.(string))
	}
	if v, ok := d.GetOk("public_ip"); ok {
		request.PublicIp = aws.String(v.(string))
	}

	log.Printf("[DEBUG] EIP association configuration: %#v", request)

	var resp *ec2.AssociateAddressOutput
	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
		var err error
		resp, err = conn.AssociateAddress(request)
		if err != nil {
			if isAWSErr(err, "InvalidInstanceID", "pending instance") {
				return resource.RetryableError(err)
			}
			return resource.NonRetryableError(err)
		}
		return nil
	})
	if err != nil {
		return fmt.Errorf("Error associating EIP: %s", err)
	}

	log.Printf("[DEBUG] EIP Assoc Response: %s", resp)

	supportedPlatforms := meta.(*AWSClient).supportedplatforms
	if len(supportedPlatforms) > 0 && !hasEc2Classic(supportedPlatforms) && resp.AssociationId == nil {
		// We expect no association ID in EC2 Classic
		// but still error out if ID is missing and we _know_ it's NOT EC2 Classic
		return fmt.Errorf("Received no EIP Association ID in account that doesn't support EC2 Classic (%q): %s",
			supportedPlatforms, resp)
	}

	if resp.AssociationId == nil {
		// This is required field for EC2 Classic per docs
		d.SetId(*request.PublicIp)
	} else {
		d.SetId(*resp.AssociationId)
	}

	return resourceAwsEipAssociationRead(d, meta)
}

func resourceAwsEipAssociationRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	request, err := describeAddressesById(d.Id(), meta.(*AWSClient).supportedplatforms)
	if err != nil {
		return err
	}

	response, err := conn.DescribeAddresses(request)
	if err != nil {
		return fmt.Errorf("Error reading EC2 Elastic IP %s: %#v", d.Get("allocation_id").(string), err)
	}

	if response.Addresses == nil || len(response.Addresses) == 0 {
		log.Printf("[INFO] EIP Association ID Not Found. Refreshing from state")
		d.SetId("")
		return nil
	}

	return readAwsEipAssociation(d, response.Addresses[0])
}

func resourceAwsEipAssociationDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	var opts *ec2.DisassociateAddressInput
	// We assume EC2 Classic if ID is a valid IPv4 address
	ip := net.ParseIP(d.Id())
	if ip != nil {
		supportedPlatforms := meta.(*AWSClient).supportedplatforms
		if len(supportedPlatforms) > 0 && !hasEc2Classic(supportedPlatforms) {
			return fmt.Errorf("Received IPv4 address as ID in account that doesn't support EC2 Classic (%q)",
				supportedPlatforms)
		}

		opts = &ec2.DisassociateAddressInput{
			PublicIp: aws.String(d.Id()),
		}
	} else {
		opts = &ec2.DisassociateAddressInput{
			AssociationId: aws.String(d.Id()),
		}
	}

	_, err := conn.DisassociateAddress(opts)
	if err != nil {
		if isAWSErr(err, "InvalidAssociationID.NotFound", "") {
			return nil
		}
		return fmt.Errorf("Error deleting Elastic IP association: %s", err)
	}

	return nil
}

func readAwsEipAssociation(d *schema.ResourceData, address *ec2.Address) error {
	if err := d.Set("allocation_id", address.AllocationId); err != nil {
		return err
	}
	if err := d.Set("instance_id", address.InstanceId); err != nil {
		return err
	}
	if err := d.Set("network_interface_id", address.NetworkInterfaceId); err != nil {
		return err
	}
	if err := d.Set("private_ip_address", address.PrivateIpAddress); err != nil {
		return err
	}
	if err := d.Set("public_ip", address.PublicIp); err != nil {
		return err
	}

	return nil
}

func describeAddressesById(id string, supportedPlatforms []string) (*ec2.DescribeAddressesInput, error) {
	// We assume EC2 Classic if ID is a valid IPv4 address
	ip := net.ParseIP(id)
	if ip != nil {
		if len(supportedPlatforms) > 0 && !hasEc2Classic(supportedPlatforms) {
			return nil, fmt.Errorf("Received IPv4 address as ID in account that doesn't support EC2 Classic (%q)",
				supportedPlatforms)
		}

		return &ec2.DescribeAddressesInput{
			Filters: []*ec2.Filter{
				&ec2.Filter{
					Name:   aws.String("public-ip"),
					Values: []*string{aws.String(id)},
				},
				&ec2.Filter{
					Name:   aws.String("domain"),
					Values: []*string{aws.String("standard")},
				},
			},
		}, nil
	}

	return &ec2.DescribeAddressesInput{
		Filters: []*ec2.Filter{
			&ec2.Filter{
				Name:   aws.String("association-id"),
				Values: []*string{aws.String(id)},
			},
		},
	}, nil
}
<|endoftext|>


<commit_before>package upload

import (
	"io"
	"os"

	"github.com/materials-commons/mcstore/pkg/app"
)

type AssemblerFactory interface {
	Assembler(uploadID, fileID string) *Assembler
}

type MCDirAssemblerFactory struct {
	FinisherFactory
}

func NewMCDirAssemblerFactory(ff FinisherFactory) *MCDirAssemblerFactory {
	return &MCDirAssemblerFactory{
		FinisherFactory: ff,
	}
}

func (f *MCDirAssemblerFactory) Assembler(uploadID, fileID string) *Assembler {
	itemSupplier := newDirItemSupplier(app.MCDir.UploadDir(uploadID))
	fileDir := app.MCDir.FileDir(fileID)
	if err := os.MkdirAll(fileDir, 0700); err != nil {
		app.Log.Error(app.Logf("Cannot create path (%s) to assemble file: %s ", fileDir, fileID))
		return nil
	}

	destination, err := os.Create(app.MCDir.FilePath(fileID))
	if err != nil {
		app.Log.Error(app.Logf("Cannot create %s to assemble upload", app.MCDir.FilePath(fileID)))
		return nil
	}

	return NewAssembler(itemSupplier, destination, f.Finisher(uploadID, fileID))
}

// A Assembler takes a list of items and assembles them.
type Assembler struct {
	ItemSupplier
	Finisher
	destination io.Writer
}

// NewAssembler creates an Assembler.
func NewAssembler(itemSupplier ItemSupplier, destination io.Writer, finisher Finisher) *Assembler {
	return &Assembler{
		ItemSupplier: itemSupplier,
		Finisher:     finisher,
		destination:  destination,
	}
}

// Assemble will write the assembled items to destination. It writes
// the items in the order they were give. If it can't write
// any item, it will quit on that item and return it's error.
// If it is able to write all items then it will call Finisher.
// It only calls Finisher if it was able to successfully write
// all items. If it calls Finisher it will return its result.
func (a *Assembler) Assemble() error {
	if err := a.writeEach(); err != nil {
		return err
	}

	return a.Finish()
}

// writeEach attempts to write each item to destination. It
// will stop on the first item it cannot write and return
// its error.
func (a *Assembler) writeEach() error {
	switch items, err := a.Items(); {
	case err != nil:
		return err
	default:
		for _, item := range items {
			if err := a.writeItem(item); err != nil {
				return err
			}
		}
		return nil
	}
}

// writeItemTo performs the write to destination of a particular
// item. It calls copy to append the item to destination. If the
// reader returned by a item is a ReadCloser then it will call
// the close routine.
func (a *Assembler) writeItem(item Item) error {
	switch source, err := item.Reader(); {
	case err != nil:
		return err
	default:
		if closer, ok := source.(io.ReadCloser); ok {
			defer closer.Close()
		}
		_, err = io.Copy(a.destination, source)
		return err
	}
}
<commit_msg>Add comments.<commit_after>package upload

import (
	"io"
	"os"

	"github.com/materials-commons/mcstore/pkg/app"
)

// AssemblerFactory creates new instance of an Assembler
type AssemblerFactory interface {
	Assembler(uploadID, fileID string) *Assembler
}

// MCDirAssemblerFactory creates new instance of an Assembler using
// app.MCDir to determine locations, and DirItemSupplier to get the
// list of Items to assemble.
type MCDirAssemblerFactory struct {
	FinisherFactory
}

// NewMCDirAssemblerFactory creates a new instance of MCDirAssemblerFactory.
func NewMCDirAssemblerFactory(ff FinisherFactory) *MCDirAssemblerFactory {
	return &MCDirAssemblerFactory{
		FinisherFactory: ff,
	}
}

// Assembler creates the new Assembler using a DirItemSupplier, and app.MCDir
// to determine location. Assembler will also make all the paths exist by
// calling os.MkdirAll, and creating the destination file to write.
func (f *MCDirAssemblerFactory) Assembler(uploadID, fileID string) *Assembler {
	itemSupplier := newDirItemSupplier(app.MCDir.UploadDir(uploadID))
	fileDir := app.MCDir.FileDir(fileID)
	if err := os.MkdirAll(fileDir, 0700); err != nil {
		app.Log.Error(app.Logf("Cannot create path (%s) to assemble file: %s ", fileDir, fileID))
		return nil
	}

	destination, err := os.Create(app.MCDir.FilePath(fileID))
	if err != nil {
		app.Log.Error(app.Logf("Cannot create %s to assemble upload", app.MCDir.FilePath(fileID)))
		return nil
	}

	return NewAssembler(itemSupplier, destination, f.Finisher(uploadID, fileID))
}

// A Assembler takes a list of items and assembles them.
type Assembler struct {
	ItemSupplier
	Finisher
	destination io.Writer
}

// NewAssembler creates an Assembler.
func NewAssembler(itemSupplier ItemSupplier, destination io.Writer, finisher Finisher) *Assembler {
	return &Assembler{
		ItemSupplier: itemSupplier,
		Finisher:     finisher,
		destination:  destination,
	}
}

// Assemble will write the assembled items to destination. It writes
// the items in the order they were give. If it can't write
// any item, it will quit on that item and return it's error.
// If it is able to write all items then it will call Finisher.
// It only calls Finisher if it was able to successfully write
// all items. If it calls Finisher it will return its result.
func (a *Assembler) Assemble() error {
	if err := a.writeEach(); err != nil {
		return err
	}

	return a.Finish()
}

// writeEach attempts to write each item to destination. It
// will stop on the first item it cannot write and return
// its error.
func (a *Assembler) writeEach() error {
	switch items, err := a.Items(); {
	case err != nil:
		return err
	default:
		for _, item := range items {
			if err := a.writeItem(item); err != nil {
				return err
			}
		}
		return nil
	}
}

// writeItemTo performs the write to destination of a particular
// item. It calls copy to append the item to destination. If the
// reader returned by a item is a ReadCloser then it will call
// the close routine.
func (a *Assembler) writeItem(item Item) error {
	switch source, err := item.Reader(); {
	case err != nil:
		return err
	default:
		if closer, ok := source.(io.ReadCloser); ok {
			defer closer.Close()
		}
		_, err = io.Copy(a.destination, source)
		return err
	}
}
<|endoftext|>