<commit_before>package swag

import (
	"go/ast"
	goparser "go/parser"
	"go/token"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/go-openapi/spec"
)

// Parser implements a parser for Go source files.
type Parser struct {
	// swagger represents the root document object for the API specification
	swagger *spec.Swagger

	//files is a map that stores map[real_go_file_path][astFile]
	files map[string]*ast.File

	// TypeDefinitions is a map that stores [package name][type name][*ast.TypeSpec]
	TypeDefinitions map[string]map[string]*ast.TypeSpec

	//registerTypes is a map that stores [refTypeName][*ast.TypeSpec]
	registerTypes map[string]*ast.TypeSpec
}

// New creates a new Parser with default properties.
func New() *Parser {
	parser := &Parser{
		swagger: &spec.Swagger{
			SwaggerProps: spec.SwaggerProps{
				Info: &spec.Info{
					InfoProps: spec.InfoProps{
						Contact: &spec.ContactInfo{},
						License: &spec.License{},
					},
				},
				Paths: &spec.Paths{
					Paths: make(map[string]spec.PathItem),
				},
				Definitions: make(map[string]spec.Schema),
			},
		},
		files:           make(map[string]*ast.File),
		TypeDefinitions: make(map[string]map[string]*ast.TypeSpec),
		registerTypes:   make(map[string]*ast.TypeSpec),
	}
	return parser
}

// ParseApi parses general api info for gived searchDir and mainApiFile
func (parser *Parser) ParseApi(searchDir string, mainApiFile string) {
	log.Println("Generate general API Info")
	parser.getAllGoFileInfo(searchDir)
	parser.ParseGeneralApiInfo(path.Join(searchDir, mainApiFile))

	for _, astFile := range parser.files {
		parser.ParseType(astFile)
	}

	for _, astFile := range parser.files {
		parser.ParseRouterApiInfo(astFile)
	}

	parser.ParseDefinitions()
}

// ParseGeneralApiInfo parses general api info for gived mainApiFile path
func (parser *Parser) ParseGeneralApiInfo(mainApiFile string) {
	fileSet := token.NewFileSet()
	fileTree, err := goparser.ParseFile(fileSet, mainApiFile, nil, goparser.ParseComments)

	if err != nil {
		log.Panicf("ParseGeneralApiInfo occur error:%+v", err)
	}

	parser.swagger.Swagger = "2.0"

	if fileTree.Comments != nil {
		for _, comment := range fileTree.Comments {
			for _, commentLine := range strings.Split(comment.Text(), "\n") {
				attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
				switch attribute {
				case "@version":
					parser.swagger.Info.Version = strings.TrimSpace(commentLine[len(attribute):])
				case "@title":
					parser.swagger.Info.Title = strings.TrimSpace(commentLine[len(attribute):])
				case "@description":
					parser.swagger.Info.Description = strings.TrimSpace(commentLine[len(attribute):])
				case "@termsofservice":
					parser.swagger.Info.TermsOfService = strings.TrimSpace(commentLine[len(attribute):])
				case "@contact.name":
					parser.swagger.Info.Contact.Name = strings.TrimSpace(commentLine[len(attribute):])
				case "@contact.email":
					parser.swagger.Info.Contact.Email = strings.TrimSpace(commentLine[len(attribute):])
				case "@contact.url":
					parser.swagger.Info.Contact.URL = strings.TrimSpace(commentLine[len(attribute):])
				case "@license.name":
					parser.swagger.Info.License.Name = strings.TrimSpace(commentLine[len(attribute):])
				case "@license.url":
					parser.swagger.Info.License.URL = strings.TrimSpace(commentLine[len(attribute):])
				case "@host":
					parser.swagger.Host = strings.TrimSpace(commentLine[len(attribute):])
				case "@basepath":
					parser.swagger.BasePath = strings.TrimSpace(commentLine[len(attribute):])
				case "@schemes":
					parser.swagger.Schemes = GetSchemes(commentLine)
				}
			}
		}
	}
}

// GetSchemes parses swagger schemes for gived commentLine
func GetSchemes(commentLine string) []string {
	attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
	return strings.Split(strings.TrimSpace(commentLine[len(attribute):]), " ")
}

// ParseRouterApiInfo parses router api info for gived astFile
func (parser *Parser) ParseRouterApiInfo(astFile *ast.File) {
	for _, astDescription := range astFile.Decls {
		switch astDeclaration := astDescription.(type) {
		case *ast.FuncDecl:
			if astDeclaration.Doc != nil && astDeclaration.Doc.List != nil {
				operation := NewOperation() //for per 'function' comment, create a new 'Operation' object
				operation.parser = parser
				for _, comment := range astDeclaration.Doc.List {
					if err := operation.ParseComment(comment.Text); err != nil {
						log.Panicf("ParseComment panic:%+v", err)
					}
				}

				pathItem := spec.PathItem{}
				switch strings.ToUpper(operation.HttpMethod) {
				case http.MethodGet:
					pathItem.Get = &operation.Operation
				case http.MethodPost:
					pathItem.Post = &operation.Operation
				case http.MethodDelete:
					pathItem.Delete = &operation.Operation
				case http.MethodPut:
					pathItem.Put = &operation.Operation
				case http.MethodPatch:
					pathItem.Patch = &operation.Operation
				case http.MethodHead:
					pathItem.Head = &operation.Operation
				case http.MethodOptions:
					pathItem.Options = &operation.Operation
				}

				parser.swagger.Paths.Paths[operation.Path] = pathItem
			}
		}
	}
}

// ParseType parses type info for gived astFile
func (parser *Parser) ParseType(astFile *ast.File) {
	if _, ok := parser.TypeDefinitions[astFile.Name.String()]; !ok {
		parser.TypeDefinitions[astFile.Name.String()] = make(map[string]*ast.TypeSpec)
	}

	for _, astDeclaration := range astFile.Decls {
		if generalDeclaration, ok := astDeclaration.(*ast.GenDecl); ok && generalDeclaration.Tok == token.TYPE {
			for _, astSpec := range generalDeclaration.Specs {
				if typeSpec, ok := astSpec.(*ast.TypeSpec); ok {
					parser.TypeDefinitions[astFile.Name.String()][typeSpec.Name.String()] = typeSpec
				}
			}
		}
	}
}

// ParseDefinitions parses Swagger Api definitions
func (parser *Parser) ParseDefinitions() {
	for refTypeName, typeSpec := range parser.registerTypes {
		var properties map[string]spec.Schema
		properties = make(map[string]spec.Schema)

		switch typeSpec.Type.(type) {
		case *ast.StructType:
			structDecl := typeSpec.Type.(*ast.StructType)
			fields := structDecl.Fields.List

			for _, field := range fields {
				name := field.Names[0].Name
				propName := getPropertyName(field)
				properties[name] = spec.Schema{
					SchemaProps: spec.SchemaProps{Type: []string{propName}},
				}
			}

		case *ast.ArrayType:
			log.Panic("ParseDefinitions not supported 'array' yet.")
		case *ast.InterfaceType:
			log.Panic("ParseDefinitions not supported 'array' yet.")
		case *ast.MapType:
			log.Panic("ParseDefinitions not supported 'array' yet.")
		}

		parser.swagger.Definitions[refTypeName] = spec.Schema{
			SchemaProps: spec.SchemaProps{
				Type:       []string{"object"},
				Properties: properties,
			},
		}

	}
}

// GetAllGoFileInfo gets all Go source files information for gived searchDir.
func (parser *Parser) getAllGoFileInfo(searchDir string) {
	filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error {
		//exclude vendor folder
		if ext := filepath.Ext(path); ext == ".go" && !strings.Contains(path, "/vendor") {
			astFile, err := goparser.ParseFile(token.NewFileSet(), path, nil, goparser.ParseComments)
			if err != nil {
				log.Panicf("ParseFile panic:%+v", err)
			}
			parser.files[path] = astFile

		}
		return nil
	})
}

// GetSwagger returns *spec.Swagger which is the root document object for the API specification.
func (parser *Parser) GetSwagger() *spec.Swagger {
	return parser.swagger
}
<commit_msg>typo (#8)<commit_after>package swag

import (
	"go/ast"
	goparser "go/parser"
	"go/token"
	"log"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/go-openapi/spec"
)

// Parser implements a parser for Go source files.
type Parser struct {
	// swagger represents the root document object for the API specification
	swagger *spec.Swagger

	//files is a map that stores map[real_go_file_path][astFile]
	files map[string]*ast.File

	// TypeDefinitions is a map that stores [package name][type name][*ast.TypeSpec]
	TypeDefinitions map[string]map[string]*ast.TypeSpec

	//registerTypes is a map that stores [refTypeName][*ast.TypeSpec]
	registerTypes map[string]*ast.TypeSpec
}

// New creates a new Parser with default properties.
func New() *Parser {
	parser := &Parser{
		swagger: &spec.Swagger{
			SwaggerProps: spec.SwaggerProps{
				Info: &spec.Info{
					InfoProps: spec.InfoProps{
						Contact: &spec.ContactInfo{},
						License: &spec.License{},
					},
				},
				Paths: &spec.Paths{
					Paths: make(map[string]spec.PathItem),
				},
				Definitions: make(map[string]spec.Schema),
			},
		},
		files:           make(map[string]*ast.File),
		TypeDefinitions: make(map[string]map[string]*ast.TypeSpec),
		registerTypes:   make(map[string]*ast.TypeSpec),
	}
	return parser
}

// ParseApi parses general api info for gived searchDir and mainApiFile
func (parser *Parser) ParseApi(searchDir string, mainApiFile string) {
	log.Println("Generate general API Info")
	parser.getAllGoFileInfo(searchDir)
	parser.ParseGeneralApiInfo(path.Join(searchDir, mainApiFile))

	for _, astFile := range parser.files {
		parser.ParseType(astFile)
	}

	for _, astFile := range parser.files {
		parser.ParseRouterApiInfo(astFile)
	}

	parser.ParseDefinitions()
}

// ParseGeneralApiInfo parses general api info for gived mainApiFile path
func (parser *Parser) ParseGeneralApiInfo(mainApiFile string) {
	fileSet := token.NewFileSet()
	fileTree, err := goparser.ParseFile(fileSet, mainApiFile, nil, goparser.ParseComments)

	if err != nil {
		log.Panicf("ParseGeneralApiInfo occur error:%+v", err)
	}

	parser.swagger.Swagger = "2.0"

	if fileTree.Comments != nil {
		for _, comment := range fileTree.Comments {
			for _, commentLine := range strings.Split(comment.Text(), "\n") {
				attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
				switch attribute {
				case "@version":
					parser.swagger.Info.Version = strings.TrimSpace(commentLine[len(attribute):])
				case "@title":
					parser.swagger.Info.Title = strings.TrimSpace(commentLine[len(attribute):])
				case "@description":
					parser.swagger.Info.Description = strings.TrimSpace(commentLine[len(attribute):])
				case "@termsofservice":
					parser.swagger.Info.TermsOfService = strings.TrimSpace(commentLine[len(attribute):])
				case "@contact.name":
					parser.swagger.Info.Contact.Name = strings.TrimSpace(commentLine[len(attribute):])
				case "@contact.email":
					parser.swagger.Info.Contact.Email = strings.TrimSpace(commentLine[len(attribute):])
				case "@contact.url":
					parser.swagger.Info.Contact.URL = strings.TrimSpace(commentLine[len(attribute):])
				case "@license.name":
					parser.swagger.Info.License.Name = strings.TrimSpace(commentLine[len(attribute):])
				case "@license.url":
					parser.swagger.Info.License.URL = strings.TrimSpace(commentLine[len(attribute):])
				case "@host":
					parser.swagger.Host = strings.TrimSpace(commentLine[len(attribute):])
				case "@basepath":
					parser.swagger.BasePath = strings.TrimSpace(commentLine[len(attribute):])
				case "@schemes":
					parser.swagger.Schemes = GetSchemes(commentLine)
				}
			}
		}
	}
}

// GetSchemes parses swagger schemes for gived commentLine
func GetSchemes(commentLine string) []string {
	attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
	return strings.Split(strings.TrimSpace(commentLine[len(attribute):]), " ")
}

// ParseRouterApiInfo parses router api info for gived astFile
func (parser *Parser) ParseRouterApiInfo(astFile *ast.File) {
	for _, astDescription := range astFile.Decls {
		switch astDeclaration := astDescription.(type) {
		case *ast.FuncDecl:
			if astDeclaration.Doc != nil && astDeclaration.Doc.List != nil {
				operation := NewOperation() //for per 'function' comment, create a new 'Operation' object
				operation.parser = parser
				for _, comment := range astDeclaration.Doc.List {
					if err := operation.ParseComment(comment.Text); err != nil {
						log.Panicf("ParseComment panic:%+v", err)
					}
				}

				pathItem := spec.PathItem{}
				switch strings.ToUpper(operation.HttpMethod) {
				case http.MethodGet:
					pathItem.Get = &operation.Operation
				case http.MethodPost:
					pathItem.Post = &operation.Operation
				case http.MethodDelete:
					pathItem.Delete = &operation.Operation
				case http.MethodPut:
					pathItem.Put = &operation.Operation
				case http.MethodPatch:
					pathItem.Patch = &operation.Operation
				case http.MethodHead:
					pathItem.Head = &operation.Operation
				case http.MethodOptions:
					pathItem.Options = &operation.Operation
				}

				parser.swagger.Paths.Paths[operation.Path] = pathItem
			}
		}
	}
}

// ParseType parses type info for gived astFile
func (parser *Parser) ParseType(astFile *ast.File) {
	if _, ok := parser.TypeDefinitions[astFile.Name.String()]; !ok {
		parser.TypeDefinitions[astFile.Name.String()] = make(map[string]*ast.TypeSpec)
	}

	for _, astDeclaration := range astFile.Decls {
		if generalDeclaration, ok := astDeclaration.(*ast.GenDecl); ok && generalDeclaration.Tok == token.TYPE {
			for _, astSpec := range generalDeclaration.Specs {
				if typeSpec, ok := astSpec.(*ast.TypeSpec); ok {
					parser.TypeDefinitions[astFile.Name.String()][typeSpec.Name.String()] = typeSpec
				}
			}
		}
	}
}

// ParseDefinitions parses Swagger Api definitions
func (parser *Parser) ParseDefinitions() {
	for refTypeName, typeSpec := range parser.registerTypes {
		var properties map[string]spec.Schema
		properties = make(map[string]spec.Schema)

		switch typeSpec.Type.(type) {
		case *ast.StructType:
			structDecl := typeSpec.Type.(*ast.StructType)
			fields := structDecl.Fields.List

			for _, field := range fields {
				name := field.Names[0].Name
				propName := getPropertyName(field)
				properties[name] = spec.Schema{
					SchemaProps: spec.SchemaProps{Type: []string{propName}},
				}
			}

		case *ast.ArrayType:
			log.Panic("ParseDefinitions not supported 'Array' yet.")
		case *ast.InterfaceType:
			log.Panic("ParseDefinitions not supported 'Interface' yet.")
		case *ast.MapType:
			log.Panic("ParseDefinitions not supported 'Map' yet.")
		}

		parser.swagger.Definitions[refTypeName] = spec.Schema{
			SchemaProps: spec.SchemaProps{
				Type:       []string{"object"},
				Properties: properties,
			},
		}

	}
}

// GetAllGoFileInfo gets all Go source files information for gived searchDir.
func (parser *Parser) getAllGoFileInfo(searchDir string) {
	filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error {
		//exclude vendor folder
		if ext := filepath.Ext(path); ext == ".go" && !strings.Contains(path, "/vendor") {
			astFile, err := goparser.ParseFile(token.NewFileSet(), path, nil, goparser.ParseComments)
			if err != nil {
				log.Panicf("ParseFile panic:%+v", err)
			}
			parser.files[path] = astFile

		}
		return nil
	})
}

// GetSwagger returns *spec.Swagger which is the root document object for the API specification.
func (parser *Parser) GetSwagger() *spec.Swagger {
	return parser.swagger
}
<|endoftext|>


<commit_before>package view

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"strings"

	"github.com/ywzjackal/goweb"
)

var (
	// TemplateSuffix is template suffix -.-
	TemplateSuffix = ".html"
	// TemplatePosition is template position path -.-
	TemplatePosition = "" //"./templates"
	// html.template delims attributes of left
	DelimsLeft = "{{"
	// html.template delims attributes of right
	DelimsRight = "}}"
	// views map container
	views = make(map[string]goweb.View)
	// never mind! -.-
	rootTemplate = template.New("")
	//
	TemplateFuncs = template.FuncMap{
		"httpStatusText": http.StatusText,
	}
)

// Initialize buildin view components
func init() {
	RegisterView("html", &viewHtml{})
	RegisterView("json", &viewJson{})
	RegisterView("", &view{})
	//
	//	ReloadTemplates()
}

// ReloadTemplates, you know what it will do. `.`
func ReloadTemplates() {
	if TemplatePosition == "" {
		panic("Please set `goweb.TemplatePosition to path of templates directory!`")
	}
	rootTemplate = template.New("").Funcs(TemplateFuncs)
	rootTemplate = template.Must(rootTemplate.Delims(DelimsLeft, DelimsRight).
		ParseGlob(TemplatePosition + "/*"))
}

// RegisterView should be called by custom view component in 'package file init() function'
// will panic when register with duplicate name.
//
// RegisterView 应该在用户引用的自定义视图组件的包文件的init（）函数中调用以注册新的视图组件
// 如果出现panic，说明视图组件的名字被重复注册
func RegisterView(name string, view goweb.View) {
	if _, ok := views[strings.ToLower(name)]; ok {
		panic("Register view `" + name + "` duplicate!")
	}
	views[strings.ToLower(name)] = view
}

func GetView(name string) goweb.View {
	v, exist := views[name]
	if exist {
		return v
	}
	return nil
}

type view struct {
	goweb.View
}

type ViewHtml interface {
	goweb.View
}

type ViewJson interface {
	goweb.View
}

type viewHtml struct {
	ViewHtml
	*view
	//
	req *http.Request
}

type viewJson struct {
	ViewJson
	*view
}

func (v *view) Render(c goweb.Controller, args ...interface{}) goweb.WebError {
	//	raw := []byte(fmt.Sprintf("% +v, % +v", c, args))
	//	_, err := c.Context().ResponseWriter().Write(raw)
	//	if err != nil {
	//		return goweb.NewWebError(500, err.Error())
	//	}
	return nil
}

func (v *viewHtml) Render(c goweb.Controller, args ...interface{}) (err goweb.WebError) {
	defer func() {
		if r := recover(); r != nil {
			c.Context().ResponseWriter().Write([]byte(fmt.Sprintf("%v", r)))
		}
	}()
	var (
		name = strings.ToLower(c.Context().Request().URL.Path)
	)
	if goweb.Debug {
		ReloadTemplates()
	}
	buffer := bytes.Buffer{}
	writer := bufio.NewWriter(&buffer)
	switch len(args) {
	case 0:
		e := rootTemplate.ExecuteTemplate(writer, name, c)
		if e != nil {
			return goweb.NewWebError(500, e.Error())
		}
	case 1:
		name, ok := args[0].(string)
		if !ok {
			return goweb.NewWebError(500, "invalid view template name:%+v,need string", args[0])
		}
		e := rootTemplate.ExecuteTemplate(writer, name, c)
		if e != nil {
			return goweb.NewWebError(500, e.Error())
		}
	default:
		e := rootTemplate.ExecuteTemplate(writer, name, c)
		if e != nil {
			return goweb.NewWebError(500, e.Error())
		}
	}
	writer.Flush()
	c.Context().ResponseWriter().Header().Add("Cache-Control", "no-store, must-revalidate")
	c.Context().ResponseWriter().Header().Add("Pragma", "no-cache")
	c.Context().ResponseWriter().Write(buffer.Bytes())
	return err
}

func (v *viewJson) Render(c goweb.Controller, args ...interface{}) goweb.WebError {
	if len(args) == 1 {
		b, err := json.MarshalIndent(c, "", " ")
		if err != nil {
			return goweb.NewWebError(500, err.Error())
		}
		c.Context().ResponseWriter().Header().Add("Cache-Control", "no-store, must-revalidate")
		c.Context().ResponseWriter().Header().Add("Pragma", "no-cache")
		_, err = c.Context().ResponseWriter().Write(b)
	} else {
		b, err := json.MarshalIndent(args, "", " ")
		if err != nil {
			return goweb.NewWebError(500, err.Error())
		}
		c.Context().ResponseWriter().Header().Add("Cache-Control", "no-store, must-revalidate")
		c.Context().ResponseWriter().Header().Add("Pragma", "no-cache")
		_, err = c.Context().ResponseWriter().Write(b)
	}
	return nil
}
<commit_msg>Signed-off-by: jackal <ywzjackal@163.com><commit_after>package view

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"strings"

	"github.com/ywzjackal/goweb"
)

var (
	// TemplateSuffix is template suffix -.-
	TemplateSuffix = ".html"
	// TemplatePosition is template position path -.-
	TemplatePosition = "" //"./templates"
	// html.template delims attributes of left
	DelimsLeft = "{{"
	// html.template delims attributes of right
	DelimsRight = "}}"
	// views map container
	views = make(map[string]goweb.View)
	// never mind! -.-
	rootTemplate = template.New("")
	//
	TemplateFuncs = template.FuncMap{
		"httpStatusText": http.StatusText,
	}
)

// Initialize buildin view components
func init() {
	RegisterView("html", &viewHtml{})
	RegisterView("json", &viewJson{})
	RegisterView("", &view{})
	//
	//	ReloadTemplates()
}

// ReloadTemplates, you know what it will do. `.`
func ReloadTemplates() {
	if TemplatePosition == "" {
		panic("Please set `goweb.TemplatePosition to path of templates directory!`")
	}
	rootTemplate = template.New("").Funcs(TemplateFuncs)
	rootTemplate = template.Must(rootTemplate.Delims(DelimsLeft, DelimsRight).
		ParseGlob(TemplatePosition + "/*"))
}

// RegisterView should be called by custom view component in 'package file init() function'
// will panic when register with duplicate name.
//
// RegisterView 应该在用户引用的自定义视图组件的包文件的init（）函数中调用以注册新的视图组件
// 如果出现panic，说明视图组件的名字被重复注册
func RegisterView(name string, view goweb.View) {
	if _, ok := views[strings.ToLower(name)]; ok {
		panic("Register view `" + name + "` duplicate!")
	}
	views[strings.ToLower(name)] = view
}

func GetView(name string) goweb.View {
	v, exist := views[name]
	if exist {
		return v
	}
	return nil
}

type view struct {
	goweb.View
}

type ViewHtml interface {
	goweb.View
}

type ViewJson interface {
	goweb.View
}

type viewHtml struct {
	ViewHtml
	*view
	//
	req *http.Request
}

type viewJson struct {
	ViewJson
	*view
}

func (v *view) Render(c goweb.Controller, args ...interface{}) goweb.WebError {
	//	raw := []byte(fmt.Sprintf("% +v, % +v", c, args))
	//	_, err := c.Context().ResponseWriter().Write(raw)
	//	if err != nil {
	//		return goweb.NewWebError(500, err.Error())
	//	}
	return nil
}

func (v *viewHtml) Render(c goweb.Controller, args ...interface{}) (err goweb.WebError) {
	defer func() {
		if r := recover(); r != nil {
			c.Context().ResponseWriter().Write([]byte(fmt.Sprintf("%v", r)))
		}
	}()
	var (
		name = strings.ToLower(c.Context().Request().URL.Path)
	)
	if goweb.Debug {
		ReloadTemplates()
	}
	buffer := bytes.Buffer{}
	writer := bufio.NewWriter(&buffer)
	switch len(args) {
	case 0:
		e := rootTemplate.ExecuteTemplate(writer, name, c)
		if e != nil {
			return goweb.NewWebError(500, e.Error())
		}
	case 1:
		name, ok := args[0].(string)
		if !ok {
			return goweb.NewWebError(500, "invalid view template name:%+v,need string", args[0])
		}
		e := rootTemplate.ExecuteTemplate(writer, name, c)
		if e != nil {
			return goweb.NewWebError(500, e.Error())
		}
	default:
		e := rootTemplate.ExecuteTemplate(writer, name, c)
		if e != nil {
			return goweb.NewWebError(500, e.Error())
		}
	}
	writer.Flush()
	c.Context().ResponseWriter().Header().Add("Cache-Control", "no-store, must-revalidate")
	c.Context().ResponseWriter().Header().Add("Pragma", "no-cache")
	c.Context().ResponseWriter().Write(buffer.Bytes())
	return err
}

func (v *viewJson) Render(c goweb.Controller, args ...interface{}) goweb.WebError {
	if len(args) == 0 {
		b, err := json.MarshalIndent(c, "", " ")
		if err != nil {
			return goweb.NewWebError(500, err.Error())
		}
		c.Context().ResponseWriter().Header().Add("Cache-Control", "no-store, must-revalidate")
		c.Context().ResponseWriter().Header().Add("Pragma", "no-cache")
		_, err = c.Context().ResponseWriter().Write(b)
	} else {
		b, err := json.MarshalIndent(args, "", " ")
		if err != nil {
			return goweb.NewWebError(500, err.Error())
		}
		c.Context().ResponseWriter().Header().Add("Cache-Control", "no-store, must-revalidate")
		c.Context().ResponseWriter().Header().Add("Pragma", "no-cache")
		_, err = c.Context().ResponseWriter().Write(b)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

const (
	Version         = "0.1.0"
	ExitCodeSuccess = 0
	ExitCodeError   = 1
)

type Mock struct {
	Package string
	Screens []Screen
	Launch  Launch
	Colors  []Color
	Strings []String
}

type Screen struct {
	Id        string
	Layout    Layout
	Behaviors []Behavior
}

type Layout struct {
	Views []View
}

type View struct {
	Id     string
	Type   string
	Label  string
	SizeW  string `json:"size_w"`
	SizeH  string `json:"size_h"`
	AlignH string `json:"align_h"`
	AlignV string `json:"align_v"`
}

type Behavior struct {
	Trigger Trigger
	Action  Action
}

type Trigger struct {
	Type   string
	Widget string
}

type Action struct {
	Type    string
	Transit string
}

type Launch struct {
	Screen string
}

type Color struct {
	Id    string
	Value string
}

type String struct {
	Lang string
	Defs []Def
}

type Def struct {
	Id    string
	Value string
}

func main() {
	// Parse command
	if len(os.Args) == 1 {
		printUsage()
		os.Exit(ExitCodeError)
	}
	switch os.Args[1] {
	case "gen":
	case "version":
		printVersion()
		os.Exit(ExitCodeSuccess)
	case "help":
		fallthrough
	default:
		printUsage()
		os.Exit(ExitCodeError)
	}

	if len(os.Args) < 3 {
		printUsage()
		os.Exit(ExitCodeError)
	}
	genId := os.Args[2]

	mock := parseConfigs()
	gen(&mock, genId)
}

func printUsage() {
	fmt.Println(`mocker is a mock up framework for mobile apps.

Usage:
	mocker command [options]

Command:
	gen      generate source code (see 'Generator')
	help     show this help
	version  show version of mocker

Generator:
	mocker gen ID

	ID:
		ios  Objective-C code for iOS app
`)
}

func printVersion() {
	fmt.Println("mocker version \"" + Version + "\"")
}

func parseConfigs() (mock Mock) {
	filename := "Mockerfile"
	xmlFile, err := os.Open(filename)
	if err != nil {
		fmt.Println("Error opening file", err)
		return
	}
	defer xmlFile.Close()

	b, _ := ioutil.ReadAll(xmlFile)
	err = json.Unmarshal(b, &mock)
	if err != nil {
		fmt.Println("Error unmarshaling Mockerfile", err)
		return
	}

	return
}

func gen(mock *Mock, genId string) {
	switch genId {
	case "ios":
		genIos(mock)
	case "android":
		genAndroid(mock)
	default:
		fmt.Println("Invalid gen ID")
		printUsage()
		os.Exit(ExitCodeError)
	}
}

func genAndroid(mock *Mock) {
	// TODO
	fmt.Printf("%+v\n", mock)
	outDir := "out"
	os.MkdirAll(outDir, 0777)

	genAndroidManifest(mock, outDir)

	javaDir := filepath.Join(outDir, "app", "src", "main", "java")
	os.MkdirAll(javaDir, 0777)
	packageDir := filepath.Join(javaDir, strings.Replace(mock.Package, ".", string(os.PathSeparator), -1))
	os.MkdirAll(packageDir, 0777)

	for i := range mock.Screens {
		screen := mock.Screens[i]
		genAndroidActivity(mock, packageDir, screen)
	}
}

func genIos(mock *Mock) {
	// TODO
	fmt.Printf("%+v\n", mock)
}

func genAndroidManifest(mock *Mock, outDir string) {
	filename := filepath.Join(outDir, "AndroidManifest.xml")
	f, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666)
	defer f.Close()
	f.WriteString(fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="%s" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
`, mock.Package))

	launcherId := mock.Launch.Screen
	for i := range mock.Screens {
		screen := mock.Screens[i]
		activityId := strings.Title(screen.Id)
		if screen.Id == launcherId {
			// Launcher
			f.WriteString(fmt.Sprintf(`        <activity
            android:label="@string/activity_title_%s"
            android:name=".%sActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
`, screen.Id, activityId))
		} else {
			f.WriteString(fmt.Sprintf(`        <activity
            android:label="@string/activity_title_%s"
            android:name=".%sActivity" />
`, screen.Id, activityId))
		}
	}

	f.WriteString(`    </application>

</manifest>
`)
	f.Close()
}

func genAndroidActivity(mock *Mock, packageDir string, screen Screen) {
	activityId := strings.Title(screen.Id)
	filename := filepath.Join(packageDir, activityId+"Activity.java")
	f, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666)
	defer f.Close()
	f.WriteString(fmt.Sprintf(`package %s;

import android.os.Bundle;

public class %sActivity extends Activity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_%s);
		init();
	}

	private void init() {
	}

}
`, mock.Package, activityId, screen.Id))
	f.Close()
}
<commit_msg>Fixed generation order.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
)

const (
	Version         = "0.1.0"
	ExitCodeSuccess = 0
	ExitCodeError   = 1
)

type Mock struct {
	Package string
	Screens []Screen
	Launch  Launch
	Colors  []Color
	Strings []String
}

type Screen struct {
	Id        string
	Layout    Layout
	Behaviors []Behavior
}

type Layout struct {
	Views []View
}

type View struct {
	Id     string
	Type   string
	Label  string
	SizeW  string `json:"size_w"`
	SizeH  string `json:"size_h"`
	AlignH string `json:"align_h"`
	AlignV string `json:"align_v"`
}

type Behavior struct {
	Trigger Trigger
	Action  Action
}

type Trigger struct {
	Type   string
	Widget string
}

type Action struct {
	Type    string
	Transit string
}

type Launch struct {
	Screen string
}

type Color struct {
	Id    string
	Value string
}

type String struct {
	Lang string
	Defs []Def
}

type Def struct {
	Id    string
	Value string
}

func main() {
	// Parse command
	if len(os.Args) == 1 {
		printUsage()
		os.Exit(ExitCodeError)
	}
	switch os.Args[1] {
	case "gen":
	case "version":
		printVersion()
		os.Exit(ExitCodeSuccess)
	case "help":
		fallthrough
	default:
		printUsage()
		os.Exit(ExitCodeError)
	}

	if len(os.Args) < 3 {
		printUsage()
		os.Exit(ExitCodeError)
	}
	genId := os.Args[2]

	mock := parseConfigs()
	gen(&mock, genId)
}

func printUsage() {
	fmt.Println(`mocker is a mock up framework for mobile apps.

Usage:
	mocker command [options]

Command:
	gen      generate source code (see 'Generator')
	help     show this help
	version  show version of mocker

Generator:
	mocker gen ID

	ID:
		ios  Objective-C code for iOS app
`)
}

func printVersion() {
	fmt.Println("mocker version \"" + Version + "\"")
}

func parseConfigs() (mock Mock) {
	filename := "Mockerfile"
	xmlFile, err := os.Open(filename)
	if err != nil {
		fmt.Println("Error opening file", err)
		return
	}
	defer xmlFile.Close()

	b, _ := ioutil.ReadAll(xmlFile)
	err = json.Unmarshal(b, &mock)
	if err != nil {
		fmt.Println("Error unmarshaling Mockerfile", err)
		return
	}

	return
}

func gen(mock *Mock, genId string) {
	switch genId {
	case "ios":
		genIos(mock)
	case "android":
		genAndroid(mock)
	default:
		fmt.Println("Invalid gen ID")
		printUsage()
		os.Exit(ExitCodeError)
	}
}

func genAndroid(mock *Mock) {
	// TODO
	fmt.Printf("%+v\n", mock)

	// Create standard project directory structure
	outDir := "out"
	javaDir := filepath.Join(outDir, "app", "src", "main", "java")
	os.MkdirAll(javaDir, 0777)
	packageDir := filepath.Join(javaDir, strings.Replace(mock.Package, ".", string(os.PathSeparator), -1))
	os.MkdirAll(packageDir, 0777)

	genAndroidManifest(mock, outDir)

	for i := range mock.Screens {
		screen := mock.Screens[i]
		genAndroidActivity(mock, packageDir, screen)
	}
}

func genIos(mock *Mock) {
	// TODO
	fmt.Printf("%+v\n", mock)
}

func genAndroidManifest(mock *Mock, outDir string) {
	filename := filepath.Join(outDir, "AndroidManifest.xml")
	f, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666)
	defer f.Close()
	f.WriteString(fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="%s" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
`, mock.Package))

	launcherId := mock.Launch.Screen
	for i := range mock.Screens {
		screen := mock.Screens[i]
		activityId := strings.Title(screen.Id)
		if screen.Id == launcherId {
			// Launcher
			f.WriteString(fmt.Sprintf(`        <activity
            android:label="@string/activity_title_%s"
            android:name=".%sActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
`, screen.Id, activityId))
		} else {
			f.WriteString(fmt.Sprintf(`        <activity
            android:label="@string/activity_title_%s"
            android:name=".%sActivity" />
`, screen.Id, activityId))
		}
	}

	f.WriteString(`    </application>

</manifest>
`)
	f.Close()
}

func genAndroidActivity(mock *Mock, packageDir string, screen Screen) {
	activityId := strings.Title(screen.Id)
	filename := filepath.Join(packageDir, activityId+"Activity.java")
	f, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666)
	defer f.Close()
	f.WriteString(fmt.Sprintf(`package %s;

import android.os.Bundle;

public class %sActivity extends Activity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_%s);
		init();
	}

	private void init() {
	}

}
`, mock.Package, activityId, screen.Id))
	f.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"fmt"
	"github.com/BurntSushi/toml"
	"net/http"
	"github.com/gorilla/mux"
	"github.com/codegangsta/negroni"
)

type config struct {
	Port int
	MavenDir string
}

func main() {

	// top level error handling
	defer ErrorHandler()

	// parse config
	var conf config
	if _, err := toml.DecodeFile("moddex.conf", &conf); err != nil {
		panic(err)
	}

	// get gorilla router
	router := mux.NewRouter()

	// global middleware for logging and panic recovery
	n := negroni.New(negroni.NewLogger(), negroni.NewRecovery())

	router.PathPrefix("maven").Subrouter() // TODO: maven router
	router.PathPrefix("rest/v0.1").Subrouter() // TODO: rest API

	// redirect root to web
	router.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) {
		http.Redirect(resp, req, "/web/", 301)
	})

	// angular web
	router.PathPrefix("/web/").Handler(http.StripPrefix("/web", http.FileServer(http.Dir("./angular/"))))

	// launch!
	n.UseHandler(router)
	n.Run(fmt.Sprint(":", conf.Port))
}

func ErrorHandler() {
	if err := recover(); err != nil {
		fmt.Println("Moddex died: ", err)
		os.Exit(1)
	}
}
<commit_msg>fixed go routing so angular can do its own<commit_after>package main

import (
	"os"
	"fmt"
	"github.com/BurntSushi/toml"
	"net/http"
	"github.com/gorilla/mux"
	"github.com/codegangsta/negroni"
)

type config struct {
	Port int
	MavenDir string
}

func main() {

	// top level error handling
	defer ErrorHandler()

	// parse config
	var conf config
	if _, err := toml.DecodeFile("moddex.conf", &conf); err != nil {
		panic(err)
	}

	// get gorilla router
	router := mux.NewRouter()

	// global middleware for logging and panic recovery
	n := negroni.New(negroni.NewLogger(), negroni.NewRecovery())

	router.PathPrefix("maven").Subrouter() // TODO: maven router
	router.PathPrefix("rest/v0.1").Subrouter() // TODO: rest API

	// serve angular properly
	router.PathPrefix("/").Handler(negroni.New(
		negroni.NewStatic(http.Dir("./angular/")),
		negroni.HandlerFunc(Index),
	))

	// launch!
	n.UseHandler(router)
	n.Run(fmt.Sprint(":", conf.Port))
}

func ErrorHandler() {
	if err := recover(); err != nil {
		fmt.Println("Moddex died: ", err)
		os.Exit(1)
	}
}

func Index(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	http.ServeFile(rw, r, "angular/index.html")
}
<|endoftext|>


<commit_before>package controllers

import (
    "database/sql"
    "errors"
    "github.com/klenin/orc/db"
    "github.com/klenin/orc/mvc/models"
    "github.com/klenin/orc/sessions"
    "github.com/klenin/orc/utils"
    "html/template"
    "net/http"
    "strings"
)

const HASH_SIZE = 32

var err error

type BaseController struct{}

type Controller struct {
    Request  *http.Request
    Response http.ResponseWriter
}

type Model struct {
    Id           string
    TableName    string
    Caption      string
    Table        []interface{}
    Columns      []string
    ColNames     []string
    ColModel     []map[string]interface{}
    Sub          bool
    SubTableName string
    SubCaption   string
    SubColNames  []string
    SubColModel  []map[string]interface{}
}

func (*Controller) GetModel(tableName string) models.EntityInterface {
    return new(models.ModelManager).GetModel(tableName)
}

func (this *Controller) Render(filenames []string, tmpname string, data interface{}) {
    filenames = append(filenames, "mvc/views/header.html")
    filenames = append(filenames, "mvc/views/footer.html")
    tmpl, err := template.ParseFiles(filenames...)
    if err != nil {
        http.Error(this.Response, err.Error(), http.StatusInternalServerError)
    }
    if err := tmpl.ExecuteTemplate(this.Response, tmpname, data); err != nil {
        http.Error(this.Response, err.Error(), http.StatusInternalServerError)
    }
}

func (this *Controller) CheckSid() (id int, result error)  {
    userSid := sessions.GetValue("sid", this.Request)
    if !sessions.CheckSession(this.Response, this.Request) || userSid == nil {
        return -1, errors.New("Данные в куках отсутствуют.")
    }

    if err := this.GetModel("users").
        LoadWherePart(map[string]interface{}{"sid": userSid}).
        SelectRow([]string{"id"}).
        Scan(&id);
        err != nil {
        return -1, errors.New("Данные в куках отсутствуют.")
    }

    return id, nil
}

func (this *Controller) isAdmin() bool {
    userId, err := this.CheckSid()
    if err != nil {
        return false
    }

    var role string
    err = this.GetModel("users").
        LoadWherePart(map[string]interface{}{"id": userId}).
        SelectRow([]string{"role"}).
        Scan(&role)
    if err != nil || role == "user" {
        return false
    }

    return role == "admin"
}

func (*Controller) regExists(userId, eventId int) int {
    var regId int
    query := `SELECT registrations.id
        FROM registrations
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN users ON users.id = faces.user_id
        WHERE users.id = $1 AND events.id = $2;`
    err := db.QueryRow(query, []interface{}{userId, eventId}).Scan(&regId)
    if err != sql.ErrNoRows {
        return regId
    } else {
        return -1
    }
}

func WellcomeToProfile(w http.ResponseWriter, r *http.Request) {
    newContreoller := new(BaseController).Handler()

    parts := strings.Split(r.URL.Path, "/")
    token := parts[len(parts)-1]

    var id int
    err := newContreoller.GetModel("users").
        LoadWherePart(map[string]interface{}{"token": token}).
        SelectRow([]string{"id"}).
        Scan(&id)
    if utils.HandleErr("[WellcomeToProfile]: ", err, w) || id == 0 {
        return
    }

    sid := utils.GetRandSeq(HASH_SIZE)
    params := map[string]interface{}{"sid": sid, "enabled": true}
    where := map[string]interface{}{"id": id}
    newContreoller.GetModel("users").Update(false, -1, params, where)
    sessions.SetSession(w, map[string]interface{}{"sid": sid})
    http.Redirect(w, r, "/usercontroller/showcabinet", 200)
}

type VirtController interface {
    GetModel(tableName string) models.EntityInterface
    Render(filename string, data interface{})
    CheckSid() (id int, result bool)
    isAdmin() bool
    regExists(userId, eventId int) bool
}
<commit_msg>Remove unused variable<commit_after>package controllers

import (
    "database/sql"
    "errors"
    "github.com/klenin/orc/db"
    "github.com/klenin/orc/mvc/models"
    "github.com/klenin/orc/sessions"
    "github.com/klenin/orc/utils"
    "html/template"
    "net/http"
    "strings"
)

const HASH_SIZE = 32

type BaseController struct{}

type Controller struct {
    Request  *http.Request
    Response http.ResponseWriter
}

type Model struct {
    Id           string
    TableName    string
    Caption      string
    Table        []interface{}
    Columns      []string
    ColNames     []string
    ColModel     []map[string]interface{}
    Sub          bool
    SubTableName string
    SubCaption   string
    SubColNames  []string
    SubColModel  []map[string]interface{}
}

func (*Controller) GetModel(tableName string) models.EntityInterface {
    return new(models.ModelManager).GetModel(tableName)
}

func (this *Controller) Render(filenames []string, tmpname string, data interface{}) {
    filenames = append(filenames, "mvc/views/header.html")
    filenames = append(filenames, "mvc/views/footer.html")
    tmpl, err := template.ParseFiles(filenames...)
    if err != nil {
        http.Error(this.Response, err.Error(), http.StatusInternalServerError)
    }
    if err := tmpl.ExecuteTemplate(this.Response, tmpname, data); err != nil {
        http.Error(this.Response, err.Error(), http.StatusInternalServerError)
    }
}

func (this *Controller) CheckSid() (id int, result error)  {
    userSid := sessions.GetValue("sid", this.Request)
    if !sessions.CheckSession(this.Response, this.Request) || userSid == nil {
        return -1, errors.New("Данные в куках отсутствуют.")
    }

    if err := this.GetModel("users").
        LoadWherePart(map[string]interface{}{"sid": userSid}).
        SelectRow([]string{"id"}).
        Scan(&id);
        err != nil {
        return -1, errors.New("Данные в куках отсутствуют.")
    }

    return id, nil
}

func (this *Controller) isAdmin() bool {
    userId, err := this.CheckSid()
    if err != nil {
        return false
    }

    var role string
    err = this.GetModel("users").
        LoadWherePart(map[string]interface{}{"id": userId}).
        SelectRow([]string{"role"}).
        Scan(&role)
    if err != nil || role == "user" {
        return false
    }

    return role == "admin"
}

func (*Controller) regExists(userId, eventId int) int {
    var regId int
    query := `SELECT registrations.id
        FROM registrations
        INNER JOIN events ON events.id = registrations.event_id
        INNER JOIN faces ON faces.id = registrations.face_id
        INNER JOIN users ON users.id = faces.user_id
        WHERE users.id = $1 AND events.id = $2;`
    err := db.QueryRow(query, []interface{}{userId, eventId}).Scan(&regId)
    if err != sql.ErrNoRows {
        return regId
    } else {
        return -1
    }
}

func WellcomeToProfile(w http.ResponseWriter, r *http.Request) {
    newContreoller := new(BaseController).Handler()

    parts := strings.Split(r.URL.Path, "/")
    token := parts[len(parts)-1]

    var id int
    err := newContreoller.GetModel("users").
        LoadWherePart(map[string]interface{}{"token": token}).
        SelectRow([]string{"id"}).
        Scan(&id)
    if utils.HandleErr("[WellcomeToProfile]: ", err, w) || id == 0 {
        return
    }

    sid := utils.GetRandSeq(HASH_SIZE)
    params := map[string]interface{}{"sid": sid, "enabled": true}
    where := map[string]interface{}{"id": id}
    newContreoller.GetModel("users").Update(false, -1, params, where)
    sessions.SetSession(w, map[string]interface{}{"sid": sid})
    http.Redirect(w, r, "/usercontroller/showcabinet", 200)
}

type VirtController interface {
    GetModel(tableName string) models.EntityInterface
    Render(filename string, data interface{})
    CheckSid() (id int, result bool)
    isAdmin() bool
    regExists(userId, eventId int) bool
}
<|endoftext|>


<commit_before>// Copyright 2011 Arne Roomann-Kurrik
//
// 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 twittergo

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

const (
	H_LIMIT              = "X-Rate-Limit-Limit"
	H_LIMIT_REMAIN       = "X-Rate-Limit-Remaining"
	H_LIMIT_RESET        = "X-Rate-Limit-Reset"
	H_MEDIA_LIMIT        = "X-MediaRateLimit-Limit"
	H_MEDIA_LIMIT_REMAIN = "X-MediaRateLimit-Remaining"
	H_MEDIA_LIMIT_RESET  = "X-MediaRateLimit-Reset"
)

const (
	STATUS_INVALID  = 400
	STATUS_NOTFOUND = 404
	STATUS_LIMIT    = 429
	STATUS_GATEWAY  = 502
)

type Error struct {
	Code    int
	Message string
}

func (e Error) Error() string {
	msg := "Error %v: %v"
	return fmt.Sprintf(msg, e.Code, e.Message)
}

type Errors struct {
	Errors []Error
}

func (e Errors) Error() string {
	msg := ""
	for _, err := range e.Errors {
		msg += err.Error() + ". "
	}
	return msg
}

type RateLimitError struct {
	Limit     uint32
	Remaining uint32
	Reset     time.Time
}

func (e RateLimitError) Error() string {
	msg := "Rate limit: %v, Remaining: %v, Reset: %v"
	return fmt.Sprintf(msg, e.Limit, e.Remaining, e.Reset)
}

type APIResponse http.Response

func (r APIResponse) HasRateLimit() bool {
	return r.Header.Get(H_LIMIT) != ""
}

func (r APIResponse) RateLimit() uint32 {
	h := r.Header.Get(H_LIMIT)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) RateLimitRemaining() uint32 {
	h := r.Header.Get(H_LIMIT_REMAIN)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) RateLimitReset() time.Time {
	h := r.Header.Get(H_LIMIT_RESET)
	i, _ := strconv.ParseUint(h, 10, 32)
	t := time.Unix(int64(i), 0)
	return t
}

func (r APIResponse) HasMediaRateLimit() bool {
	return r.Header.Get(H_MEDIA_LIMIT) != ""
}

func (r APIResponse) MediaRateLimit() uint32 {
	h := r.Header.Get(H_MEDIA_LIMIT)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) MediaRateLimitRemaining() uint32 {
	h := r.Header.Get(H_MEDIA_LIMIT_REMAIN)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) MediaRateLimitReset() time.Time {
	h := r.Header.Get(H_MEDIA_LIMIT_RESET)
	i, _ := strconv.ParseUint(h, 10, 32)
	t := time.Unix(int64(i), 0)
	return t
}

// Parses a JSON encoded HTTP response into the supplied interface.
func (r APIResponse) Parse(out interface{}) (err error) {
	switch r.StatusCode {
	case STATUS_NOTFOUND:
		fallthrough
	case STATUS_GATEWAY:
		fallthrough
	case STATUS_INVALID:
		e := &Errors{}
		defer r.Body.Close()
		json.NewDecoder(r.Body).Decode(e)
		err = *e
		return
	case STATUS_LIMIT:
		err = RateLimitError{
			Limit:     r.RateLimit(),
			Remaining: r.RateLimitRemaining(),
			Reset:     r.RateLimitReset(),
		}
		return
	}
	defer r.Body.Close()
	err = json.NewDecoder(r.Body).Decode(out)
	if err == io.EOF {
		err = nil
	}
	return
}

// It's a user!
type User map[string]interface{}

func (u User) Id() uint64 {
	id, _ := strconv.ParseUint(u["id_str"].(string), 10, 64)
	return id
}

func (u User) IdStr() string {
	return u["id_str"].(string)
}

func (u User) Name() string {
	return u["name"].(string)
}

func (u User) ScreenName() string {
	return u["screen_name"].(string)
}

// It's a Tweet! (Adorably referred to by the API as a "status").
type Tweet map[string]interface{}

func (t Tweet) Id() (id uint64) {
	var (
		err error
		src = t["id_str"].(string)
	)
	if id, err = strconv.ParseUint(src, 10, 64); err != nil {
		panic(fmt.Sprintf("Could not parse ID: %v", err))
	}
	return
}

func (t Tweet) IdStr() string {
	return t["id_str"].(string)
}

func (t Tweet) Text() string {
	return t["text"].(string)
}

func (t Tweet) User() User {
	return User(t["user"].(map[string]interface{}))
}

func (t Tweet) CreatedAt() (out time.Time) {
	var (
		err error
		src = t["created_at"].(string)
	)
	if out, err = time.Parse(time.RubyDate, src); err != nil {
		panic(fmt.Sprintf("Could not parse time: %v", err))
	}
	return
}

func (t Tweet) JSON() (out []byte) {
	var err error
	if out, err = json.MarshalIndent(t, "", "  "); err != nil {
		panic(fmt.Sprintf("Problem converting to JSON: %v", err))
	}
	return
}

// It's a structured list of Tweets!
type SearchResults struct {
	Statuses []Tweet
}

// It's a less structured list of Tweets!
type Timeline []Tweet
<commit_msg>Make Tweets store their JSON representation.<commit_after>// Copyright 2011 Arne Roomann-Kurrik
//
// 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 twittergo

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

const (
	H_LIMIT              = "X-Rate-Limit-Limit"
	H_LIMIT_REMAIN       = "X-Rate-Limit-Remaining"
	H_LIMIT_RESET        = "X-Rate-Limit-Reset"
	H_MEDIA_LIMIT        = "X-MediaRateLimit-Limit"
	H_MEDIA_LIMIT_REMAIN = "X-MediaRateLimit-Remaining"
	H_MEDIA_LIMIT_RESET  = "X-MediaRateLimit-Reset"
)

const (
	STATUS_INVALID  = 400
	STATUS_NOTFOUND = 404
	STATUS_LIMIT    = 429
	STATUS_GATEWAY  = 502
)

type Error struct {
	Code    int
	Message string
}

func (e Error) Error() string {
	msg := "Error %v: %v"
	return fmt.Sprintf(msg, e.Code, e.Message)
}

type Errors struct {
	Errors []Error
}

func (e Errors) Error() string {
	msg := ""
	for _, err := range e.Errors {
		msg += err.Error() + ". "
	}
	return msg
}

type RateLimitError struct {
	Limit     uint32
	Remaining uint32
	Reset     time.Time
}

func (e RateLimitError) Error() string {
	msg := "Rate limit: %v, Remaining: %v, Reset: %v"
	return fmt.Sprintf(msg, e.Limit, e.Remaining, e.Reset)
}

type APIResponse http.Response

func (r APIResponse) HasRateLimit() bool {
	return r.Header.Get(H_LIMIT) != ""
}

func (r APIResponse) RateLimit() uint32 {
	h := r.Header.Get(H_LIMIT)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) RateLimitRemaining() uint32 {
	h := r.Header.Get(H_LIMIT_REMAIN)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) RateLimitReset() time.Time {
	h := r.Header.Get(H_LIMIT_RESET)
	i, _ := strconv.ParseUint(h, 10, 32)
	t := time.Unix(int64(i), 0)
	return t
}

func (r APIResponse) HasMediaRateLimit() bool {
	return r.Header.Get(H_MEDIA_LIMIT) != ""
}

func (r APIResponse) MediaRateLimit() uint32 {
	h := r.Header.Get(H_MEDIA_LIMIT)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) MediaRateLimitRemaining() uint32 {
	h := r.Header.Get(H_MEDIA_LIMIT_REMAIN)
	i, _ := strconv.ParseUint(h, 10, 32)
	return uint32(i)
}

func (r APIResponse) MediaRateLimitReset() time.Time {
	h := r.Header.Get(H_MEDIA_LIMIT_RESET)
	i, _ := strconv.ParseUint(h, 10, 32)
	t := time.Unix(int64(i), 0)
	return t
}

// Parses a JSON encoded HTTP response into the supplied interface.
func (r APIResponse) Parse(out interface{}) (err error) {
	switch r.StatusCode {
	case STATUS_NOTFOUND:
		fallthrough
	case STATUS_GATEWAY:
		fallthrough
	case STATUS_INVALID:
		e := &Errors{}
		defer r.Body.Close()
		json.NewDecoder(r.Body).Decode(e)
		err = *e
		return
	case STATUS_LIMIT:
		err = RateLimitError{
			Limit:     r.RateLimit(),
			Remaining: r.RateLimitRemaining(),
			Reset:     r.RateLimitReset(),
		}
		return
	}
	defer r.Body.Close()
	err = json.NewDecoder(r.Body).Decode(out)
	if err == io.EOF {
		err = nil
	}
	return
}

// It's a user!
type User map[string]interface{}

func (u User) Id() uint64 {
	id, _ := strconv.ParseUint(u["id_str"].(string), 10, 64)
	return id
}

func (u User) IdStr() string {
	return u["id_str"].(string)
}

func (u User) Name() string {
	return u["name"].(string)
}

func (u User) ScreenName() string {
	return u["screen_name"].(string)
}

// It's a Tweet! (Adorably referred to by the API as a "status").
type Tweet map[string]interface{}

func (t *Tweet) UnmarshalJSON(b []byte) (err error) {
	out := (*map[string]interface{})(t)
	if err = json.Unmarshal(b, out); err == nil {
		t = (*Tweet)(out)
		c := make([]byte, len(b))
		copy(c, b)
		(*t)["json"] = c
	}
	return
}

func (t Tweet) Id() (id uint64) {
	var (
		err error
		src = t["id_str"].(string)
	)
	if id, err = strconv.ParseUint(src, 10, 64); err != nil {
		panic(fmt.Sprintf("Could not parse ID: %v", err))
	}
	return
}

func (t Tweet) IdStr() string {
	return t["id_str"].(string)
}

func (t Tweet) Text() string {
	return t["text"].(string)
}

func (t Tweet) User() User {
	return User(t["user"].(map[string]interface{}))
}

func (t Tweet) CreatedAt() (out time.Time) {
	var (
		err error
		src = t["created_at"].(string)
	)
	if out, err = time.Parse(time.RubyDate, src); err != nil {
		panic(fmt.Sprintf("Could not parse time: %v", err))
	}
	return
}

func (t Tweet) JSON() []byte {
	return t["json"].([]byte)
}

// It's a structured list of Tweets!
type SearchResults struct {
	Statuses []Tweet
}

// It's a less structured list of Tweets!
type Timeline []Tweet
<|endoftext|>


<commit_before>package geocodio

// Geocodio is the base struct
type Geocodio struct {
	APIKey string
}

type Address struct {
	Components   Components `json:"address_components"`
	Formatted    string     `json:"formatted_address"`
	Location     Location   `json:"location"`
	Accuracy     float64    `json:"accuracy"`
	AccuracyType string     `json:"accuracy_type"`
	Source       string     `json:"source"`
	Fields       Fields     `json:"fields,omitempty"`
}

type Components struct {
	Number          string `json:"number"`
	Street          string `json:"street"`
	Suffix          string `json:"suffix"`
	PostDirectional string `json:"postdirectional"`
	FormattedStreet string `json:"formatted_street"`
	City            string `json:"city"`
	County          string `json:"county"`
	State           string `json:"state"`
	Zip             string `json:"zip"`
	Country         string `json:"country"`
}

type Location struct {
	Latitude  float64 `json:"lat"`
	Longitude float64 `json:"lng"`
}

// Fields
type Fields struct {
	Timezone                  Timezone                  `json:"timezone,omitempty"`
	CongressionalDistrict     CongressionalDistrict     `json:"congressional_district,omitempty"`
	StateLegislativeDistricts StateLegislativeDistricts `json:"state_legislative_districts,omitempty"`
}

type Timezone struct {
	Name        string `json:"name"`
	UTCOffset   int    `json:"utc_offset"`
	ObservesDST bool   `json:"observes_dst"`
}

type CongressionalDistrict struct {
	Name           string `json:"name"`
	DistrictNumber int    `json:"district_number"`
	CongressNumber string `json:"congress_number"`
	CongressYears  string `json:"congress_years"`
}

type StateLegislativeDistricts struct {
	House  StateLegislativeDistrict `json:"house"`
	Senate StateLegislativeDistrict `json:"senate"`
}

type StateLegislativeDistrict struct {
	Name           string `json:"name"`
	DistrictNumber string `json:"district_number"`
}

type GeocodeResult struct {
	Input   Components `json:"input,omitempty"`
	Results []Address  `json:"results"`
	Debug   struct {
		RequestedURL string `json:"requested_url"`
		Status       string `json:"status"`
		StatusCode   int    `json:"status_code"`
	} `json:"-"`
}
<commit_msg>clean up model, fix geocode result struct<commit_after>package geocodio

// Geocodio is the base struct
type Geocodio struct {
	APIKey string
}

type Address struct {
	Components   Components `json:"address_components"`
	Formatted    string     `json:"formatted_address"`
	Location     Location   `json:"location"`
	Accuracy     float64    `json:"accuracy"`
	AccuracyType string     `json:"accuracy_type"`
	Source       string     `json:"source"`
	Fields       Fields     `json:"fields,omitempty"`
}

type Components struct {
	Number          string `json:"number"`
	Street          string `json:"street"`
	Suffix          string `json:"suffix"`
	SecondaryNumber string `json:"secondarynumber"`
	SecondaryUnit   string `json:"secondaryunit"`
	PostDirectional string `json:"postdirectional"`
	FormattedStreet string `json:"formatted_street"`
	City            string `json:"city"`
	State           string `json:"state"`
	Zip             string `json:"zip"`
	County          string `json:"county"`
	Country         string `json:"country"`
}

type Location struct {
	Latitude  float64 `json:"lat"`
	Longitude float64 `json:"lng"`
}

// Fields
type Fields struct {
	Timezone                  Timezone                  `json:"timezone,omitempty"`
	CongressionalDistrict     CongressionalDistrict     `json:"congressional_district,omitempty"`
	StateLegislativeDistricts StateLegislativeDistricts `json:"state_legislative_districts,omitempty"`
}

type Timezone struct {
	Name        string `json:"name"`
	UTCOffset   int    `json:"utc_offset"`
	ObservesDST bool   `json:"observes_dst"`
}

type CongressionalDistrict struct {
	Name           string `json:"name"`
	DistrictNumber int    `json:"district_number"`
	CongressNumber string `json:"congress_number"`
	CongressYears  string `json:"congress_years"`
}

type StateLegislativeDistricts struct {
	House  StateLegislativeDistrict `json:"house"`
	Senate StateLegislativeDistrict `json:"senate"`
}

type StateLegislativeDistrict struct {
	Name           string `json:"name"`
	DistrictNumber string `json:"district_number"`
}

// GeocodeResponse
type GeocodeResult struct {
	Input struct {
		AddressComponents Components `json:"address_components"`
	} `json:"input,omitempty"`
	Results []Address `json:"results"`
	Debug   struct {
		RawResponse  []byte `json:"-"`
		RequestedURL string `json:"requested_url"`
		Status       string `json:"status"`
		StatusCode   int    `json:"status_code"`
	} `json:"-"`
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type MongoDatabase struct {
	s *mgo.Session
}

// Constants representing collection names
const user_collection string = "users"
const review_collection string = "reviews"
const booking_collection string = "bookings"
const places_collection string = "places"

type Review struct {
	Id          bson.ObjectId `json:"id" bson:"_id,omitempty"`
	Rating      float64       `json:"rating"`
	Description string        `json:"description"`
	Author      bson.ObjectId `json:"author,omitempty"`
}

type User struct {
	Id            bson.ObjectId  `json:"id" bson:"_id,omitempty"`                                // user id
	HostingPlaces []HostingPlace `json:"hostingPlaces,omitempty" bson:"hostingPlaces,omitempty"` // hosting places that this user has registered
	BikeLocation  HostingPlace   `json:"bikeLocation,omitempty" bson:"bikeLocation,omitempty"`   // where my bike is now (empty if you haven't put your bike)
	Reviews       []Review       `json:"reviews,omitempty" bson:"reviews,omitempty"`
	UserID        string         `json:"userID"` // user email
	ProfilePicUrl string         `json:"profilePic"`
	FirstName     string         `json:"firstName"`
	LastName      string         `json:"lastName"`
	Gender        string         `json:"gender,omitempty"`
}

type Booking struct {
	Id   bson.ObjectId `json:"id" bson:"_id,omitempty"`
	Date time.Time     `json:"time"`           // when this booking happened
	User string        `json:"user,omitempty"` // who did the booking
	Host string        `json:"host,omitempty"` // who's owner
	Long float64       `json:"long"`           // longitude
	Lat  float64       `json:"lat"`            // latitude
}

type HostingPlace struct {
	Id       bson.ObjectId `json:"id" bson:"_id,omitempty"`
	Host     string        `json:"host"`     // who is hosting this place (email address)
	Spaces   int           `json:"spaces"`   // how many bikes can you put here
	Active   bool          `json:"active"`   // is it active or not
	Long     float64       `json:"long"`     // longitude
	Lat      float64       `json:"lat"`      // latitude
	Address  string        `json:"address"`  // address
	Bookings []Booking     `json:"bookings"` // current bookings
}

// filterCategories function searches in categories collection based on supplied keywords
func (db *MongoDatabase) getUsers() (results []User, err error) {
	c := db.s.DB(AppConfig.databaseName).C(user_collection)

	err = c.Find(nil).All(&results)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warn("Category search failed.")
		return nil, err
	} else {
		return results, nil
	}

}

func (db *MongoDatabase) getUser(userID string) (User, error) {
	c := db.s.DB(AppConfig.databaseName).C(user_collection)

	var result User

	err := c.Find(bson.M{"userid": userID}).One(&result)

	// getting places
	pc := db.s.DB(AppConfig.databaseName).C(places_collection)
	var places []HostingPlace
	err = pc.Find(bson.M{"host": userID}).All(&places)

	if len(places) > 0 {
		place := places[0]
		// bookings
		bc := db.s.DB(AppConfig.databaseName).C(booking_collection)
		var bookings []Booking

		err = bc.Find(bson.M{
			"host": userID,
			"lat":  place.Lat,
			"long": place.Long,
		}).All(&bookings)

		result.HostingPlaces = places

		place.Bookings = bookings
		result.HostingPlaces[0] = place
		return result, err
	} else {
		return result, err

	}

}

func (db *MongoDatabase) addUser(user User) error {
	c := db.s.DB(AppConfig.databaseName).C(user_collection)
	id := bson.NewObjectId()

	log.WithFields(log.Fields{
		"bsonID": id,
	}).Info("ID for user document")

	user.Id = id

	err := c.Insert(user)

	return err
}

func (db *MongoDatabase) addHostingPlace(place HostingPlace) error {

	// adding place
	c := db.s.DB(AppConfig.databaseName).C(places_collection)
	id := bson.NewObjectId()
	log.WithFields(log.Fields{
		"bsonID": id,
	}).Info("ID for hosting place document")
	place.Id = id

	err := c.Insert(place)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error while adding new place.")
	}

	return err
}

func (db *MongoDatabase) addBooking(booking Booking) error {
	// adding place
	c := db.s.DB(AppConfig.databaseName).C(booking_collection)
	id := bson.NewObjectId()
	log.WithFields(log.Fields{
		"bsonID": id,
	}).Info("ID for booking document")
	booking.Id = id
	booking.Date = time.Now()

	err := c.Insert(booking)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error while adding new booking.")
	}

	return err
}
<commit_msg>get booking model<commit_after>package main

import (
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type MongoDatabase struct {
	s *mgo.Session
}

// Constants representing collection names
const user_collection string = "users"
const review_collection string = "reviews"
const booking_collection string = "bookings"
const places_collection string = "places"

type Review struct {
	Id          bson.ObjectId `json:"id" bson:"_id,omitempty"`
	Rating      float64       `json:"rating"`
	Description string        `json:"description"`
	Author      bson.ObjectId `json:"author,omitempty"`
}

type User struct {
	Id            bson.ObjectId  `json:"id" bson:"_id,omitempty"`                                // user id
	HostingPlaces []HostingPlace `json:"hostingPlaces,omitempty" bson:"hostingPlaces,omitempty"` // hosting places that this user has registered
	BikeLocation  HostingPlace   `json:"bikeLocation,omitempty" bson:"bikeLocation,omitempty"`   // where my bike is now (empty if you haven't put your bike)
	Reviews       []Review       `json:"reviews,omitempty" bson:"reviews,omitempty"`
	UserID        string         `json:"userID"` // user email
	ProfilePicUrl string         `json:"profilePic"`
	FirstName     string         `json:"firstName"`
	LastName      string         `json:"lastName"`
	Gender        string         `json:"gender,omitempty"`
}

type Booking struct {
	Id   bson.ObjectId `json:"id" bson:"_id,omitempty"`
	Date time.Time     `json:"time"`           // when this booking happened
	User string        `json:"user,omitempty"` // who did the booking
	Host string        `json:"host,omitempty"` // who's owner
	Long float64       `json:"long"`           // longitude
	Lat  float64       `json:"lat"`            // latitude
}

type HostingPlace struct {
	Id       bson.ObjectId `json:"id" bson:"_id,omitempty"`
	Host     string        `json:"host"`     // who is hosting this place (email address)
	Spaces   int           `json:"spaces"`   // how many bikes can you put here
	Active   bool          `json:"active"`   // is it active or not
	Long     float64       `json:"long"`     // longitude
	Lat      float64       `json:"lat"`      // latitude
	Address  string        `json:"address"`  // address
	Bookings []Booking     `json:"bookings"` // current bookings
}

// filterCategories function searches in categories collection based on supplied keywords
func (db *MongoDatabase) getUsers() (results []User, err error) {
	c := db.s.DB(AppConfig.databaseName).C(user_collection)

	err = c.Find(nil).All(&results)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Warn("Category search failed.")
		return nil, err
	} else {
		return results, nil
	}

}

func (db *MongoDatabase) getUser(userID string) (User, error) {
	c := db.s.DB(AppConfig.databaseName).C(user_collection)

	var result User

	err := c.Find(bson.M{"userid": userID}).One(&result)

	// getting places
	pc := db.s.DB(AppConfig.databaseName).C(places_collection)
	var places []HostingPlace
	err = pc.Find(bson.M{"host": userID}).All(&places)

	if len(places) > 0 {
		place := places[0]
		// bookings
		bc := db.s.DB(AppConfig.databaseName).C(booking_collection)
		var bookings []Booking

		err = bc.Find(bson.M{
			"host": userID,
			"lat":  place.Lat,
			"long": place.Long,
		}).All(&bookings)

		result.HostingPlaces = places

		place.Bookings = bookings
		result.HostingPlaces[0] = place
		return result, err
	} else {
		return result, err

	}

}

func (db *MongoDatabase) addUser(user User) error {
	c := db.s.DB(AppConfig.databaseName).C(user_collection)
	id := bson.NewObjectId()

	log.WithFields(log.Fields{
		"bsonID": id,
	}).Info("ID for user document")

	user.Id = id

	err := c.Insert(user)

	return err
}

func (db *MongoDatabase) addHostingPlace(place HostingPlace) error {

	// adding place
	c := db.s.DB(AppConfig.databaseName).C(places_collection)
	id := bson.NewObjectId()
	log.WithFields(log.Fields{
		"bsonID": id,
	}).Info("ID for hosting place document")
	place.Id = id

	err := c.Insert(place)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error while adding new place.")
	}

	return err
}

func (db *MongoDatabase) addBooking(booking Booking) error {
	// adding place
	c := db.s.DB(AppConfig.databaseName).C(booking_collection)
	id := bson.NewObjectId()
	log.WithFields(log.Fields{
		"bsonID": id,
	}).Info("ID for booking document")
	booking.Id = id
	booking.Date = time.Now()

	err := c.Insert(booking)

	if err != nil {
		log.WithFields(log.Fields{
			"error": err.Error(),
		}).Error("Got error while adding new booking.")
	}

	return err
}

func (db *MongoDatabase) getBooking(userID string) (Booking, error) {
	c := db.s.DB(AppConfig.databaseName).C(booking_collection)

	var result Booking

	// who cares for errors? all or nothing #24hoursCoding
	err := c.Find(bson.M{"user": userID}).Sort("-time").One(&result)

	return result, err

}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"
	"strings"
	"time"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxc/utils"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/i18n"
)

type cmdExport struct {
	global *cmdGlobal

	flagContainerOnly    bool
	flagOptimizedStorage bool
}

func (c *cmdExport) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("export [<remote>:]<container> [target] [--container-only] [--optimized-storage]")
	cmd.Short = i18n.G("Export container backups")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Export containers as backup tarballs.`))
	cmd.Example = cli.FormatSection("", i18n.G(
		`lxc export u1 backup0.tar.gz
    Download a backup tarball of the u1 container.`))

	cmd.RunE = c.Run
	cmd.Flags().BoolVar(&c.flagContainerOnly, "container-only", false,
		i18n.G("Whether or not to only backup the container (without snapshots)"))
	cmd.Flags().BoolVar(&c.flagOptimizedStorage, "optimized-storage", false,
		i18n.G("Use storage driver optimized format (can only be restored on a similar pool)"))

	return cmd
}

func (c *cmdExport) Run(cmd *cobra.Command, args []string) error {
	conf := c.global.conf

	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 1, 2)
	if exit {
		return err
	}

	// Connect to LXD
	remote, name, err := conf.ParseRemote(args[0])
	if err != nil {
		return err
	}

	d, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	req := api.ContainerBackupsPost{
		Name:             "",
		ExpiryDate:       time.Now().Add(24 * time.Hour),
		ContainerOnly:    c.flagContainerOnly,
		OptimizedStorage: c.flagOptimizedStorage,
	}

	op, err := d.CreateContainerBackup(name, req)
	if err != nil {
		return errors.Wrap(err, "Create container backup")
	}

	// Wait until backup is done
	err = op.Wait()
	if err != nil {
		return err
	}

	// Get name of backup
	backupName := strings.TrimPrefix(op.Get().Resources["backups"][0],
		"/1.0/backups/")

	defer func() {
		// Delete backup after we're done
		op, err = d.DeleteContainerBackup(name, backupName)
		if err == nil {
			op.Wait()
		}
	}()

	var targetName string
	if len(args) > 1 {
		targetName = args[1]
	} else {
		targetName = "backup.tar.gz"
	}

	target, err := os.Create(shared.HostPath(targetName))
	if err != nil {
		return err
	}
	defer target.Close()

	// Prepare the download request
	progress := utils.ProgressRenderer{
		Format: i18n.G("Exporting the backup: %s"),
		Quiet:  c.global.flagQuiet,
	}
	backupFileRequest := lxd.BackupFileRequest{
		BackupFile:      io.WriteSeeker(target),
		ProgressHandler: progress.UpdateProgress,
	}

	// Export tarball
	_, err = d.GetContainerBackupFile(name, backupName, &backupFileRequest)
	if err != nil {
		os.Remove(targetName)
		progress.Done("")
		return errors.Wrap(err, "Fetch container backup file")
	}

	progress.Done(i18n.G("Backup exported successfully!"))
	return nil
}
<commit_msg>lxc: Update for fixed backup fields<commit_after>package main

import (
	"io"
	"os"
	"strings"
	"time"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxc/utils"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	cli "github.com/lxc/lxd/shared/cmd"
	"github.com/lxc/lxd/shared/i18n"
)

type cmdExport struct {
	global *cmdGlobal

	flagContainerOnly    bool
	flagOptimizedStorage bool
}

func (c *cmdExport) Command() *cobra.Command {
	cmd := &cobra.Command{}
	cmd.Use = i18n.G("export [<remote>:]<container> [target] [--container-only] [--optimized-storage]")
	cmd.Short = i18n.G("Export container backups")
	cmd.Long = cli.FormatSection(i18n.G("Description"), i18n.G(
		`Export containers as backup tarballs.`))
	cmd.Example = cli.FormatSection("", i18n.G(
		`lxc export u1 backup0.tar.gz
    Download a backup tarball of the u1 container.`))

	cmd.RunE = c.Run
	cmd.Flags().BoolVar(&c.flagContainerOnly, "container-only", false,
		i18n.G("Whether or not to only backup the container (without snapshots)"))
	cmd.Flags().BoolVar(&c.flagOptimizedStorage, "optimized-storage", false,
		i18n.G("Use storage driver optimized format (can only be restored on a similar pool)"))

	return cmd
}

func (c *cmdExport) Run(cmd *cobra.Command, args []string) error {
	conf := c.global.conf

	// Sanity checks
	exit, err := c.global.CheckArgs(cmd, args, 1, 2)
	if exit {
		return err
	}

	// Connect to LXD
	remote, name, err := conf.ParseRemote(args[0])
	if err != nil {
		return err
	}

	d, err := conf.GetContainerServer(remote)
	if err != nil {
		return err
	}

	req := api.ContainerBackupsPost{
		Name:             "",
		ExpiresAt:        time.Now().Add(24 * time.Hour),
		ContainerOnly:    c.flagContainerOnly,
		OptimizedStorage: c.flagOptimizedStorage,
	}

	op, err := d.CreateContainerBackup(name, req)
	if err != nil {
		return errors.Wrap(err, "Create container backup")
	}

	// Wait until backup is done
	err = op.Wait()
	if err != nil {
		return err
	}

	// Get name of backup
	backupName := strings.TrimPrefix(op.Get().Resources["backups"][0],
		"/1.0/backups/")

	defer func() {
		// Delete backup after we're done
		op, err = d.DeleteContainerBackup(name, backupName)
		if err == nil {
			op.Wait()
		}
	}()

	var targetName string
	if len(args) > 1 {
		targetName = args[1]
	} else {
		targetName = "backup.tar.gz"
	}

	target, err := os.Create(shared.HostPath(targetName))
	if err != nil {
		return err
	}
	defer target.Close()

	// Prepare the download request
	progress := utils.ProgressRenderer{
		Format: i18n.G("Exporting the backup: %s"),
		Quiet:  c.global.flagQuiet,
	}
	backupFileRequest := lxd.BackupFileRequest{
		BackupFile:      io.WriteSeeker(target),
		ProgressHandler: progress.UpdateProgress,
	}

	// Export tarball
	_, err = d.GetContainerBackupFile(name, backupName, &backupFileRequest)
	if err != nil {
		os.Remove(targetName)
		progress.Done("")
		return errors.Wrap(err, "Fetch container backup file")
	}

	progress.Done(i18n.G("Backup exported successfully!"))
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
)

type VoicePipe struct {
	RootDir   string
	Directive *Directive
}

func NewVoicePipe(path string) (*VoicePipe, error) {
	d, err := NewDirective(path)
	if err != nil {
		return nil, err
	}
	return &VoicePipe{RootDir: path, Directive: d}, nil
}

func (vp *VoicePipe) Resources() ([]os.FileInfo, error) {
	rs := make([]os.FileInfo, 0)
	fis, err := ioutil.ReadDir(vp.RootDir)
	if err != nil {
		return rs, err
	}
	for _, fi := range fis {
		n := fi.Name()
		if n != ".voicepipe" && n != "voicepipe.yml" && n != "Dockerfile" {
			rs = append(rs, fi)
		}
	}
	return rs, nil
}

func (vp *VoicePipe) SetupWorkingDir() error {
	rs, err := vp.Resources()
	if err != nil {
		return err
	}
	for _, id := range vp.Directive.ImageDirectives {
		dir := vp.RootDir + "/.voicepipe/" + id.Tag
		err = os.RemoveAll(dir)
		if err != nil {
			return err
		}
		err = os.MkdirAll(dir, 0775)
		if err != nil {
			return err
		}
		for _, fi := range rs {
			if fi.Name() != "Dockerfile" {
				err = os.Link(vp.RootDir+"/"+fi.Name(), dir+"/"+fi.Name())
				if err != nil {
					return err
				}
			}
			buf, err := ioutil.ReadFile(vp.RootDir + "/Dockerfile")
			if err != nil {
				return err
			}
			df, err := Unmarshal(buf)
			if err != nil {
				return err
			}
			// TODO: copying structures costs a lot
			for k, v := range id.Parameters {
				df = ReplaceEnv(*df, k, v)
			}
			err = ioutil.WriteFile(dir+"/Dockerfile", df.Marshal(), 775)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func (vp *VoicePipe) BuildImages(stdout, stderr io.Writer) error {
	for _, id := range vp.Directive.ImageDirectives {
		dir := vp.RootDir + "/.voicepipe/" + id.Tag
		tag := vp.Directive.Repository + ":" + id.Tag
		cmd := exec.Command("docker", "build", "--rm", "-t", tag, dir)
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		err := cmd.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func (vp *VoicePipe) Run() error {
	err := vp.SetupWorkingDir()
	if err != nil {
		return err
	}
	stdout := bufio.NewWriter(os.Stdout)
	stderr := bufio.NewWriter(os.Stderr)
	err = vp.BuildImages(stdout, stderr)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Extract a setup function<commit_after>package main

import (
	"bufio"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
)

type VoicePipe struct {
	RootDir   string
	Directive *Directive
}

func NewVoicePipe(path string) (*VoicePipe, error) {
	d, err := NewDirective(path)
	if err != nil {
		return nil, err
	}
	return &VoicePipe{RootDir: path, Directive: d}, nil
}

func (vp *VoicePipe) Resources() ([]os.FileInfo, error) {
	rs := make([]os.FileInfo, 0)
	fis, err := ioutil.ReadDir(vp.RootDir)
	if err != nil {
		return rs, err
	}
	for _, fi := range fis {
		n := fi.Name()
		if n != ".voicepipe" && n != "voicepipe.yml" && n != "Dockerfile" {
			rs = append(rs, fi)
		}
	}
	return rs, nil
}

func (vp *VoicePipe) SetupWorkDirFor(id ImageDirective, rs []os.FileInfo) error {
	dir := vp.RootDir + "/.voicepipe/" + id.Tag
	if err := os.RemoveAll(dir); err != nil {
		return err
	}
	if err := os.MkdirAll(dir, 0775); err != nil {
		return err
	}
	for _, fi := range rs {
		if fi.Name() != "Dockerfile" {
			if err := os.Link(vp.RootDir+"/"+fi.Name(), dir+"/"+fi.Name()); err != nil {
				return err
			}
			continue
		}
		buf, err := ioutil.ReadFile(vp.RootDir + "/" + fi.Name())
		if err != nil {
			return err
		}
		df, err := Unmarshal(buf)
		if err != nil {
			return err
		}
		// TODO: copying structures costs a lot
		for k, v := range id.Parameters {
			df = ReplaceEnv(*df, k, v)
		}
		if err := ioutil.WriteFile(fi.Name(), df.Marshal(), 775); err != nil {
			return err
		}
	}
	return nil
}

func (vp *VoicePipe) SetupWorkingDir() error {
	rs, err := vp.Resources()
	if err != nil {
		return err
	}
	for _, id := range vp.Directive.ImageDirectives {
		if err := vp.SetupWorkDirFor(*id, rs); err != nil {
			return err
		}
	}
	return nil
}

func (vp *VoicePipe) BuildImages(stdout, stderr io.Writer) error {
	for _, id := range vp.Directive.ImageDirectives {
		dir := vp.RootDir + "/.voicepipe/" + id.Tag
		tag := vp.Directive.Repository + ":" + id.Tag
		cmd := exec.Command("docker", "build", "--rm", "-t", tag, dir)
		cmd.Stdout = stdout
		cmd.Stderr = stderr
		err := cmd.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func (vp *VoicePipe) Run() error {
	err := vp.SetupWorkingDir()
	if err != nil {
		return err
	}
	stdout := bufio.NewWriter(os.Stdout)
	stderr := bufio.NewWriter(os.Stderr)
	err = vp.BuildImages(stdout, stderr)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"html/template"
	"log"
	"mime"
	"net"
	"net/http"
	"net/mail"
	"net/smtp"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/mhale/smtpd"
	"github.com/streadway/simpleuuid"
)

var (
	config Config  // Filters & routes
	stats  Stats   // Statistics of sent and dropped mail
	logs   LogList // Recent mail log for Dashboard
)

var httpAddr *string = flag.String("http", ":8080", "Address & port for HTTP server")
var smtpAddr *string = flag.String("smtp", ":2525", "Address & port for SMTP server")
var confFile *string = flag.String("conf", "/etc/mailrouter.conf", "Full path to configuration file")

// Handler for handling incoming mail messages.
func mailHandler(origin net.Addr, from string, to []string, data []byte) {
	originIPStr, _, _ := net.SplitHostPort(origin.String())
	originIP := net.ParseIP(originIPStr)

	// Parse the message to get the Subject header.
	msg, err := mail.ReadMessage(bytes.NewReader(data))
	if err != nil {
		log.Printf("Failed to parse message: %s\n", err)
		log.Printf("Aborting processing of message.")
		return
	}
	subject := msg.Header.Get("Subject")

	// Check each filter in order.
	var filterName string
	var routeId string
	for _, filter := range SortedFilters() {
		if filter.Match(from, to, subject, originIP) {
			filterName = filter.Name
			routeId = filter.RouteId
			break
		}
	}

	// Use the default route if no filters were matched.
	if routeId == "" {
		for _, route := range config.Routes {
			if route.IsDefault {
				routeId = route.Id
			}
		}
	}

	// If the message is to be dropped, record the drop and return.
	if routeId == "DROP" {
		stats.Dropped(len(data))
		logs.Add(originIP, from, to, subject, filterName, "Drop")
		return
	}

	// Otherwise, deliver the mail to the selected route and record the delivery.
	route := config.Routes[routeId]
	addr := route.Hostname + ":" + strconv.Itoa(route.Port)

	// Override the recipient if To field is set.
	if route.To != "" {
		to = []string{route.To}
	}

	// Deliver the mail.
	err = smtp.SendMail(addr, nil, from, to, data)
	if err != nil {
		log.Printf("Failed to deliver mail to route %s (%s): %s", route.Name, addr, err)
		return
	}

	// Record the successful delivery.
	stats.Sent(len(data))
	logs.Add(originIP, from, to, subject, filterName, route.Name)
}

// Utility function for parsing URLs like /route/:id/:action
func ParsePath(path string) (base string, id string, action string) {
	path = strings.Trim(path, "/")
	parts := strings.Split(path, "/")
	if len(parts) > 0 {
		base = parts[0]
	}
	if len(parts) > 1 {
		id = parts[1]
	}
	if len(parts) > 2 {
		action = parts[2]
	}
	return base, id, action
}

func routeHandler(w http.ResponseWriter, req *http.Request) {
	var msg string
	_, id, action := ParsePath(req.URL.Path)
	method := req.FormValue("_method")

	if req.Method == "GET" {
		data := make(map[string]interface{})
		data["list"] = SortedRoutes()

		// Populate the form if requested.
		if id != "" && action == "edit" {
			data["id"] = id
			data["edit"] = config.Routes[id]
		}

		// Render the page. Reparsing the template every time eases development at the expense of performance.
		html, _ := Asset("views/routes.html")
		tmpl, err := template.New("routes").Parse(string(html))
		if err != nil {
			log.Println(err)
		}
		err = tmpl.Execute(w, data)
		if err != nil {
			log.Println(err)
		}
	}

	if req.Method == "POST" {
		config.Lock()
		defer config.Unlock()

		if method == "delete" {
			msg = fmt.Sprintf("Deleted route %s.", config.Routes[id].Name)
			// If a default route was deleted, make Drop the default.
			if config.Routes[id].IsDefault == true {
				route := config.Routes["DROP"]
				route.IsDefault = true
				config.Routes["DROP"] = route
				msg = fmt.Sprintf("%s The Drop route is now the default route.", msg)
			}
			delete(config.Routes, id)
		}

		if method == "default" {
			msg = fmt.Sprintf("Set %s as the default route.", config.Routes[id].Name)
			for id, route := range config.Routes {
				route.IsDefault = false
				config.Routes[id] = route
			}
			route := config.Routes[id]
			route.IsDefault = true
			config.Routes[id] = route
		}

		if method == "save" {
			// Unset id means a new route is being added
			if id == "" {
				msg = fmt.Sprintf("Added route %s to host %s.", req.FormValue("routename"), req.FormValue("hostname"))
				uuid, _ := simpleuuid.NewTime(time.Now())
				id = uuid.String()
			} else {
				msg = fmt.Sprintf("Updated route %s.", req.FormValue("routename"))
			}

			// Create a new Route from the form submission.
			port, _ := strconv.Atoi(req.FormValue("port"))
			route := Route{
				Id:       id,
				Name:     req.FormValue("routename"),
				To:       req.FormValue("to"),
				Hostname: req.FormValue("hostname"),
				Port:     port,
			}
			config.Routes[id] = route
		}

		if msg != "" {
			log.Printf(msg)
			SaveConfig()
		}

		http.Redirect(w, req, "/routes/", http.StatusFound)
	}
}

func filterHandler(w http.ResponseWriter, req *http.Request) {
	var msg string
	_, id, action := ParsePath(req.URL.Path)
	method := req.FormValue("_method")

	if req.Method == "GET" {
		data := make(map[string]interface{})
		data["list"] = SortedFilters()
		data["routes"] = SortedRoutes()

		if len(config.Routes) == 1 {
			data["info"] = "No routes are defined. It is recommended to define routes before filters to populate the route drop-down menu below."
		}

		// Populate the form if requested.
		if id != "" && action == "edit" {
			data["id"] = id
			data["edit"] = config.Filters[id]
		}

		// Render the page. Reparsing the template every time eases development at the expense of performance.
		html, _ := Asset("views/filters.html")
		tmpl, err := template.New("filters").Parse(string(html))
		if err != nil {
			log.Println(err)
		}
		err = tmpl.Execute(w, data)
		if err != nil {
			log.Println(err)
		}
	}

	if req.Method == "POST" {
		config.Lock()
		defer config.Unlock()

		if method == "delete" {
			msg = fmt.Sprintf("Deleted filter %s.", config.Filters[id].Name)
			delete(config.Filters, id)
		}

		if method == "save" {
			// Unset id means a new filter is being added
			if id == "" {
				msg = fmt.Sprintf("Added filter %s.", req.FormValue("filtername"), req.FormValue("hostname"))
				uuid, _ := simpleuuid.NewTime(time.Now())
				id = uuid.String()
			} else {
				msg = fmt.Sprintf("Updated filter %s.", req.FormValue("filtername"))
			}

			// Create a new Filter from the form submission.
			order, _ := strconv.Atoi(req.FormValue("order"))
			filter := Filter{
				Id:      id,
				Order:   order,
				Name:    req.FormValue("filtername"),
				To:      req.FormValue("to"),
				From:    req.FormValue("from"),
				Origin:  req.FormValue("origin"),
				Subject: req.FormValue("subject"),
				RouteId: req.FormValue("route-id"),
			}
			filter.Summary = filter.Summarise()
			filter.RouteName = config.Routes[filter.RouteId].Name
			config.Filters[id] = filter
		}

		if msg != "" {
			log.Printf(msg)
			SaveConfig()
		}

		http.Redirect(w, req, "/filters/", http.StatusFound)
	}
}

// Handler for serving the Dashboard.
func indexHandler(w http.ResponseWriter, req *http.Request) {
	// Catch bad URLs.
	if req.URL.Path != "/" {
		http.NotFound(w, req)
		return
	}

	data := make(map[string]interface{})
	data["stats"] = stats
	data["logs"] = logs.Logs
	data["maxLogs"] = MaxLogs

	html, _ := Asset("views/index.html")
	tmpl, err := template.New("index").Parse(string(html))
	if err != nil {
		log.Println(err)
	}
	err = tmpl.Execute(w, data)
	if err != nil {
		log.Println(err)
	}
}

// Handler for serving static assets (CSS/JS).
func assetHandler(w http.ResponseWriter, req *http.Request) {
	path := string(req.URL.Path[1:]) // Strip leading slash
	data, err := Asset(path)
	if err != nil {
		log.Printf("Asset not found: %s", path)
		http.NotFound(w, req)
		return
	}

	ext := filepath.Ext(path)
	contentType := mime.TypeByExtension(ext)
	w.Header().Set("Content-Type", contentType)
	w.Write(data)
}

func main() {
	flag.Parse()

	// Load filters & routes from configuration file.
	err := LoadConfig()
	if err != nil {
		log.Printf("Could not load configuration file: %s", err)
		log.Printf("No routes or filters are defined. All incoming mail will be dropped.")
	} else {
		// Subtract 1 from config.Routes to account for Drop route.
		log.Printf("Loaded %d routes and %d filters.", len(config.Routes)-1, len(config.Filters))
	}

	// Run HTTP server in the background.
	log.Printf("Mailrouter serving HTTP on %s", *httpAddr)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/assets/", assetHandler)
	http.HandleFunc("/routes/", routeHandler)
	http.HandleFunc("/filters/", filterHandler)
	go http.ListenAndServe(*httpAddr, nil)

	// Run SMTP server in the foreground to force an exit if it fails.
	log.Printf("Mailrouter serving SMTP on %s", *smtpAddr)
	err = smtpd.ListenAndServe(*smtpAddr, mailHandler, "Mailrouter", "")
	if err != nil {
		log.Fatalf("smtpd.ListenAndServe error: %v", err)
	}
}
<commit_msg>Remove unused parameter<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"html/template"
	"log"
	"mime"
	"net"
	"net/http"
	"net/mail"
	"net/smtp"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/mhale/smtpd"
	"github.com/streadway/simpleuuid"
)

var (
	config Config  // Filters & routes
	stats  Stats   // Statistics of sent and dropped mail
	logs   LogList // Recent mail log for Dashboard
)

var httpAddr *string = flag.String("http", ":8080", "Address & port for HTTP server")
var smtpAddr *string = flag.String("smtp", ":2525", "Address & port for SMTP server")
var confFile *string = flag.String("conf", "/etc/mailrouter.conf", "Full path to configuration file")

// Handler for handling incoming mail messages.
func mailHandler(origin net.Addr, from string, to []string, data []byte) {
	originIPStr, _, _ := net.SplitHostPort(origin.String())
	originIP := net.ParseIP(originIPStr)

	// Parse the message to get the Subject header.
	msg, err := mail.ReadMessage(bytes.NewReader(data))
	if err != nil {
		log.Printf("Failed to parse message: %s\n", err)
		log.Printf("Aborting processing of message.")
		return
	}
	subject := msg.Header.Get("Subject")

	// Check each filter in order.
	var filterName string
	var routeId string
	for _, filter := range SortedFilters() {
		if filter.Match(from, to, subject, originIP) {
			filterName = filter.Name
			routeId = filter.RouteId
			break
		}
	}

	// Use the default route if no filters were matched.
	if routeId == "" {
		for _, route := range config.Routes {
			if route.IsDefault {
				routeId = route.Id
			}
		}
	}

	// If the message is to be dropped, record the drop and return.
	if routeId == "DROP" {
		stats.Dropped(len(data))
		logs.Add(originIP, from, to, subject, filterName, "Drop")
		return
	}

	// Otherwise, deliver the mail to the selected route and record the delivery.
	route := config.Routes[routeId]
	addr := route.Hostname + ":" + strconv.Itoa(route.Port)

	// Override the recipient if To field is set.
	if route.To != "" {
		to = []string{route.To}
	}

	// Deliver the mail.
	err = smtp.SendMail(addr, nil, from, to, data)
	if err != nil {
		log.Printf("Failed to deliver mail to route %s (%s): %s", route.Name, addr, err)
		return
	}

	// Record the successful delivery.
	stats.Sent(len(data))
	logs.Add(originIP, from, to, subject, filterName, route.Name)
}

// Utility function for parsing URLs like /route/:id/:action
func ParsePath(path string) (base string, id string, action string) {
	path = strings.Trim(path, "/")
	parts := strings.Split(path, "/")
	if len(parts) > 0 {
		base = parts[0]
	}
	if len(parts) > 1 {
		id = parts[1]
	}
	if len(parts) > 2 {
		action = parts[2]
	}
	return base, id, action
}

func routeHandler(w http.ResponseWriter, req *http.Request) {
	var msg string
	_, id, action := ParsePath(req.URL.Path)
	method := req.FormValue("_method")

	if req.Method == "GET" {
		data := make(map[string]interface{})
		data["list"] = SortedRoutes()

		// Populate the form if requested.
		if id != "" && action == "edit" {
			data["id"] = id
			data["edit"] = config.Routes[id]
		}

		// Render the page. Reparsing the template every time eases development at the expense of performance.
		html, _ := Asset("views/routes.html")
		tmpl, err := template.New("routes").Parse(string(html))
		if err != nil {
			log.Println(err)
		}
		err = tmpl.Execute(w, data)
		if err != nil {
			log.Println(err)
		}
	}

	if req.Method == "POST" {
		config.Lock()
		defer config.Unlock()

		if method == "delete" {
			msg = fmt.Sprintf("Deleted route %s.", config.Routes[id].Name)
			// If a default route was deleted, make Drop the default.
			if config.Routes[id].IsDefault == true {
				route := config.Routes["DROP"]
				route.IsDefault = true
				config.Routes["DROP"] = route
				msg = fmt.Sprintf("%s The Drop route is now the default route.", msg)
			}
			delete(config.Routes, id)
		}

		if method == "default" {
			msg = fmt.Sprintf("Set %s as the default route.", config.Routes[id].Name)
			for id, route := range config.Routes {
				route.IsDefault = false
				config.Routes[id] = route
			}
			route := config.Routes[id]
			route.IsDefault = true
			config.Routes[id] = route
		}

		if method == "save" {
			// Unset id means a new route is being added
			if id == "" {
				msg = fmt.Sprintf("Added route %s to host %s.", req.FormValue("routename"), req.FormValue("hostname"))
				uuid, _ := simpleuuid.NewTime(time.Now())
				id = uuid.String()
			} else {
				msg = fmt.Sprintf("Updated route %s.", req.FormValue("routename"))
			}

			// Create a new Route from the form submission.
			port, _ := strconv.Atoi(req.FormValue("port"))
			route := Route{
				Id:       id,
				Name:     req.FormValue("routename"),
				To:       req.FormValue("to"),
				Hostname: req.FormValue("hostname"),
				Port:     port,
			}
			config.Routes[id] = route
		}

		if msg != "" {
			log.Printf(msg)
			SaveConfig()
		}

		http.Redirect(w, req, "/routes/", http.StatusFound)
	}
}

func filterHandler(w http.ResponseWriter, req *http.Request) {
	var msg string
	_, id, action := ParsePath(req.URL.Path)
	method := req.FormValue("_method")

	if req.Method == "GET" {
		data := make(map[string]interface{})
		data["list"] = SortedFilters()
		data["routes"] = SortedRoutes()

		if len(config.Routes) == 1 {
			data["info"] = "No routes are defined. It is recommended to define routes before filters to populate the route drop-down menu below."
		}

		// Populate the form if requested.
		if id != "" && action == "edit" {
			data["id"] = id
			data["edit"] = config.Filters[id]
		}

		// Render the page. Reparsing the template every time eases development at the expense of performance.
		html, _ := Asset("views/filters.html")
		tmpl, err := template.New("filters").Parse(string(html))
		if err != nil {
			log.Println(err)
		}
		err = tmpl.Execute(w, data)
		if err != nil {
			log.Println(err)
		}
	}

	if req.Method == "POST" {
		config.Lock()
		defer config.Unlock()

		if method == "delete" {
			msg = fmt.Sprintf("Deleted filter %s.", config.Filters[id].Name)
			delete(config.Filters, id)
		}

		if method == "save" {
			// Unset id means a new filter is being added
			if id == "" {
				msg = fmt.Sprintf("Added filter %s.", req.FormValue("filtername"))
				uuid, _ := simpleuuid.NewTime(time.Now())
				id = uuid.String()
			} else {
				msg = fmt.Sprintf("Updated filter %s.", req.FormValue("filtername"))
			}

			// Create a new Filter from the form submission.
			order, _ := strconv.Atoi(req.FormValue("order"))
			filter := Filter{
				Id:      id,
				Order:   order,
				Name:    req.FormValue("filtername"),
				To:      req.FormValue("to"),
				From:    req.FormValue("from"),
				Origin:  req.FormValue("origin"),
				Subject: req.FormValue("subject"),
				RouteId: req.FormValue("route-id"),
			}
			filter.Summary = filter.Summarise()
			filter.RouteName = config.Routes[filter.RouteId].Name
			config.Filters[id] = filter
		}

		if msg != "" {
			log.Printf(msg)
			SaveConfig()
		}

		http.Redirect(w, req, "/filters/", http.StatusFound)
	}
}

// Handler for serving the Dashboard.
func indexHandler(w http.ResponseWriter, req *http.Request) {
	// Catch bad URLs.
	if req.URL.Path != "/" {
		http.NotFound(w, req)
		return
	}

	data := make(map[string]interface{})
	data["stats"] = stats
	data["logs"] = logs.Logs
	data["maxLogs"] = MaxLogs

	html, _ := Asset("views/index.html")
	tmpl, err := template.New("index").Parse(string(html))
	if err != nil {
		log.Println(err)
	}
	err = tmpl.Execute(w, data)
	if err != nil {
		log.Println(err)
	}
}

// Handler for serving static assets (CSS/JS).
func assetHandler(w http.ResponseWriter, req *http.Request) {
	path := string(req.URL.Path[1:]) // Strip leading slash
	data, err := Asset(path)
	if err != nil {
		log.Printf("Asset not found: %s", path)
		http.NotFound(w, req)
		return
	}

	ext := filepath.Ext(path)
	contentType := mime.TypeByExtension(ext)
	w.Header().Set("Content-Type", contentType)
	w.Write(data)
}

func main() {
	flag.Parse()

	// Load filters & routes from configuration file.
	err := LoadConfig()
	if err != nil {
		log.Printf("Could not load configuration file: %s", err)
		log.Printf("No routes or filters are defined. All incoming mail will be dropped.")
	} else {
		// Subtract 1 from config.Routes to account for Drop route.
		log.Printf("Loaded %d routes and %d filters.", len(config.Routes)-1, len(config.Filters))
	}

	// Run HTTP server in the background.
	log.Printf("Mailrouter serving HTTP on %s", *httpAddr)
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/assets/", assetHandler)
	http.HandleFunc("/routes/", routeHandler)
	http.HandleFunc("/filters/", filterHandler)
	go http.ListenAndServe(*httpAddr, nil)

	// Run SMTP server in the foreground to force an exit if it fails.
	log.Printf("Mailrouter serving SMTP on %s", *smtpAddr)
	err = smtpd.ListenAndServe(*smtpAddr, mailHandler, "Mailrouter", "")
	if err != nil {
		log.Fatalf("smtpd.ListenAndServe error: %v", err)
	}
}
<|endoftext|>