<commit_before>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package mailer

import (
	"encoding/hex"
	"errors"
	"fmt"
	"path"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/middleware"
)

// Create New mail message use MailFrom and MailUser
func NewMailMessageFrom(To []string, from, subject, body string) Message {
	msg := NewHtmlMessage(To, from, subject, body)
	msg.User = base.MailService.User
	return msg
}

// Create New mail message use MailFrom and MailUser
func NewMailMessage(To []string, subject, body string) Message {
	return NewMailMessageFrom(To, base.MailService.User, subject, body)
}

func GetMailTmplData(user *models.User) map[interface{}]interface{} {
	data := make(map[interface{}]interface{}, 10)
	data["AppName"] = base.AppName
	data["AppVer"] = base.AppVer
	data["AppUrl"] = base.AppUrl
	data["AppLogo"] = base.AppLogo
	data["ActiveCodeLives"] = base.Service.ActiveCodeLives / 60
	data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives / 60
	if user != nil {
		data["User"] = user
	}
	return data
}

// create a time limit code for user active
func CreateUserActiveCode(user *models.User, startInf interface{}) string {
	minutes := base.Service.ActiveCodeLives
	data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands
	code := base.CreateTimeLimitCode(data, minutes, startInf)

	// add tail hex username
	code += hex.EncodeToString([]byte(user.LowerName))
	return code
}

// Send user register mail with active code
func SendRegisterMail(r *middleware.Render, user *models.User) {
	code := CreateUserActiveCode(user, nil)
	subject := "Register success, Welcome"

	data := GetMailTmplData(user)
	data["Code"] = code
	body, err := r.HTMLString("mail/auth/register_success", data)
	if err != nil {
		log.Error("mail.SendRegisterMail(fail to render): %v", err)
		return
	}

	msg := NewMailMessage([]string{user.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id)

	SendAsync(&msg)
}

// Send email verify active email.
func SendActiveMail(r *middleware.Render, user *models.User) {
	code := CreateUserActiveCode(user, nil)

	subject := "Verify your e-mail address"

	data := GetMailTmplData(user)
	data["Code"] = code
	body, err := r.HTMLString("mail/auth/active_email", data)
	if err != nil {
		log.Error("mail.SendActiveMail(fail to render): %v", err)
		return
	}

	msg := NewMailMessage([]string{user.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, send active mail", user.Id)

	SendAsync(&msg)
}

// Send reset password email.
func SendResetPasswdMail(r *middleware.Render, user *models.User) {
	code := CreateUserActiveCode(user, nil)

	subject := "Reset your password"

	data := GetMailTmplData(user)
	data["Code"] = code
	body, err := r.HTMLString("mail/auth/reset_passwd", data)
	if err != nil {
		log.Error("mail.SendResetPasswdMail(fail to render): %v", err)
		return
	}

	msg := NewMailMessage([]string{user.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, send reset password email", user.Id)

	SendAsync(&msg)
}

// SendIssueNotifyMail sends mail notification of all watchers of repository.
func SendIssueNotifyMail(user, owner *models.User, repo *models.Repository, issue *models.Issue) ([]string, error) {
	watches, err := models.GetWatches(repo.Id)
	if err != nil {
		return nil, errors.New("mail.NotifyWatchers(get watches): " + err.Error())
	}

	tos := make([]string, 0, len(watches))
	for i := range watches {
		uid := watches[i].UserId
		if user.Id == uid {
			continue
		}
		u, err := models.GetUserById(uid)
		if err != nil {
			return nil, errors.New("mail.NotifyWatchers(get user): " + err.Error())
		}
		tos = append(tos, u.Email)
	}

	if len(tos) == 0 {
		return tos, nil
	}

	subject := fmt.Sprintf("[%s] %s(#%d)", repo.Name, issue.Name, issue.Index)
	content := fmt.Sprintf("%s
-
 View it on Gogs.",
		base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name),
		base.AppUrl, owner.Name, repo.Name, issue.Index)
	msg := NewMailMessageFrom(tos, user.Name, subject, content)
	msg.Info = fmt.Sprintf("Subject: %s, send issue notify emails", subject)
	SendAsync(&msg)
	return tos, nil
}

// SendIssueMentionMail sends mail notification for who are mentioned in issue.
func SendIssueMentionMail(r *middleware.Render, user, owner *models.User,
	repo *models.Repository, issue *models.Issue, tos []string) error {

	if len(tos) == 0 {
		return nil
	}

	subject := fmt.Sprintf("[%s] %s(#%d)", repo.Name, issue.Name, issue.Index)

	data := GetMailTmplData(nil)
	data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index)
	data["Subject"] = subject

	body, err := r.HTMLString("mail/notify/mention", data)
	if err != nil {
		return fmt.Errorf("mail.SendIssueMentionMail(fail to render): %v", err)
	}

	msg := NewMailMessageFrom(tos, user.Name, subject, body)
	msg.Info = fmt.Sprintf("Subject: %s, send issue mention emails", subject)
	SendAsync(&msg)
	return nil
}

// SendCollaboratorMail sends mail notification to new collaborator.
func SendCollaboratorMail(r *middleware.Render, user, owner *models.User,
	repo *models.Repository) error {

	subject := fmt.Sprintf("%s added you to %s", owner.Name, repo.Name)

	data := GetMailTmplData(nil)
	data["RepoLink"] = path.Join(owner.Name, repo.Name)
	data["Subject"] = subject

	body, err := r.HTMLString("mail/notify/collaborator", data)
	if err != nil {
		return fmt.Errorf("mail.SendCollaboratorMail(fail to render): %v", err)
	}

	msg := NewMailMessage([]string{user.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id)

	SendAsync(&msg)
	return nil
}
<commit_msg>Fix #164<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package mailer

import (
	"encoding/hex"
	"errors"
	"fmt"
	"path"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/middleware"
)

// Create New mail message use MailFrom and MailUser
func NewMailMessageFrom(To []string, from, subject, body string) Message {
	msg := NewHtmlMessage(To, from, subject, body)
	msg.User = base.MailService.User
	return msg
}

// Create New mail message use MailFrom and MailUser
func NewMailMessage(To []string, subject, body string) Message {
	return NewMailMessageFrom(To, base.MailService.User, subject, body)
}

func GetMailTmplData(user *models.User) map[interface{}]interface{} {
	data := make(map[interface{}]interface{}, 10)
	data["AppName"] = base.AppName
	data["AppVer"] = base.AppVer
	data["AppUrl"] = base.AppUrl
	data["AppLogo"] = base.AppLogo
	data["ActiveCodeLives"] = base.Service.ActiveCodeLives / 60
	data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives / 60
	if user != nil {
		data["User"] = user
	}
	return data
}

// create a time limit code for user active
func CreateUserActiveCode(user *models.User, startInf interface{}) string {
	minutes := base.Service.ActiveCodeLives
	data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands
	code := base.CreateTimeLimitCode(data, minutes, startInf)

	// add tail hex username
	code += hex.EncodeToString([]byte(user.LowerName))
	return code
}

// Send user register mail with active code
func SendRegisterMail(r *middleware.Render, user *models.User) {
	code := CreateUserActiveCode(user, nil)
	subject := "Register success, Welcome"

	data := GetMailTmplData(user)
	data["Code"] = code
	body, err := r.HTMLString("mail/auth/register_success", data)
	if err != nil {
		log.Error("mail.SendRegisterMail(fail to render): %v", err)
		return
	}

	msg := NewMailMessage([]string{user.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id)

	SendAsync(&msg)
}

// Send email verify active email.
func SendActiveMail(r *middleware.Render, user *models.User) {
	code := CreateUserActiveCode(user, nil)

	subject := "Verify your e-mail address"

	data := GetMailTmplData(user)
	data["Code"] = code
	body, err := r.HTMLString("mail/auth/active_email", data)
	if err != nil {
		log.Error("mail.SendActiveMail(fail to render): %v", err)
		return
	}

	msg := NewMailMessage([]string{user.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, send active mail", user.Id)

	SendAsync(&msg)
}

// Send reset password email.
func SendResetPasswdMail(r *middleware.Render, user *models.User) {
	code := CreateUserActiveCode(user, nil)

	subject := "Reset your password"

	data := GetMailTmplData(user)
	data["Code"] = code
	body, err := r.HTMLString("mail/auth/reset_passwd", data)
	if err != nil {
		log.Error("mail.SendResetPasswdMail(fail to render): %v", err)
		return
	}

	msg := NewMailMessage([]string{user.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, send reset password email", user.Id)

	SendAsync(&msg)
}

// SendIssueNotifyMail sends mail notification of all watchers of repository.
func SendIssueNotifyMail(user, owner *models.User, repo *models.Repository, issue *models.Issue) ([]string, error) {
	watches, err := models.GetWatches(repo.Id)
	if err != nil {
		return nil, errors.New("mail.NotifyWatchers(get watches): " + err.Error())
	}

	tos := make([]string, 0, len(watches))
	for i := range watches {
		uid := watches[i].UserId
		if user.Id == uid {
			continue
		}
		u, err := models.GetUserById(uid)
		if err != nil {
			return nil, errors.New("mail.NotifyWatchers(get user): " + err.Error())
		}
		tos = append(tos, u.Email)
	}

	if len(tos) == 0 {
		return tos, nil
	}

	subject := fmt.Sprintf("[%s] %s(#%d)", repo.Name, issue.Name, issue.Index)
	content := fmt.Sprintf("%s
-
 View it on Gogs.",
		base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name),
		base.AppUrl, owner.Name, repo.Name, issue.Index)
	msg := NewMailMessageFrom(tos, user.Email, subject, content)
	msg.Info = fmt.Sprintf("Subject: %s, send issue notify emails", subject)
	SendAsync(&msg)
	return tos, nil
}

// SendIssueMentionMail sends mail notification for who are mentioned in issue.
func SendIssueMentionMail(r *middleware.Render, user, owner *models.User,
	repo *models.Repository, issue *models.Issue, tos []string) error {

	if len(tos) == 0 {
		return nil
	}

	subject := fmt.Sprintf("[%s] %s(#%d)", repo.Name, issue.Name, issue.Index)

	data := GetMailTmplData(nil)
	data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index)
	data["Subject"] = subject

	body, err := r.HTMLString("mail/notify/mention", data)
	if err != nil {
		return fmt.Errorf("mail.SendIssueMentionMail(fail to render): %v", err)
	}

	msg := NewMailMessageFrom(tos, user.Email, subject, body)
	msg.Info = fmt.Sprintf("Subject: %s, send issue mention emails", subject)
	SendAsync(&msg)
	return nil
}

// SendCollaboratorMail sends mail notification to new collaborator.
func SendCollaboratorMail(r *middleware.Render, user, owner *models.User,
	repo *models.Repository) error {

	subject := fmt.Sprintf("%s added you to %s", owner.Name, repo.Name)

	data := GetMailTmplData(nil)
	data["RepoLink"] = path.Join(owner.Name, repo.Name)
	data["Subject"] = subject

	body, err := r.HTMLString("mail/notify/collaborator", data)
	if err != nil {
		return fmt.Errorf("mail.SendCollaboratorMail(fail to render): %v", err)
	}

	msg := NewMailMessage([]string{user.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id)

	SendAsync(&msg)
	return nil
}
<|endoftext|>


<commit_before>package svg

import (
	"github.com/gopherjs/gopherjs/js"
)

type Svg struct {
	Width   float64
	Height  float64
	Content []SvgElement
}

func jsString(a interface{}) string {
	if a != nil {
		s := js.Global.Call("String", a).String()
		if s != "undefined" && s != "null" {
			return s
		}
	}
	return ""
}

func (svg Svg) String() string {
	result := `<svg width="` + jsString(svg.Width) + `" height="` + jsString(svg.Height) + `">`
	for _, v := range svg.Content {
		result += v.String()
	}
	result += `</svg>`
	return result
}

type SvgElement interface {
	String() string
}

//SvgElement
type Rect struct {
	Width  float64 `svg:"width"`
	Height float64 `svg:"height"`
	X      float64 `svg:"x"`
	Y      float64 `svg:"y"`
	RX     float64 `svg:"rx"`
	RY     float64 `svg:"ry"`
	Strokeable
	Draggable
	Fillable
}

var unSupportMsg = "Sorry, your browser does not support inline SVG."

func (se *Rect) String() string {
	s := `<rect width="` + jsString(se.Width) + `" height="` + jsString(se.Height) + `" x="` + jsString(se.X) + `" y="` + jsString(se.Y) + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	if se.RX != 0 {
		s += ` rx="` + jsString(se.RX) + `"`
	}
	if se.RY != 0 {
		s += ` ry="` + jsString(se.RY) + `"`
	}
	s += `" >` + unSupportMsg + `</rect>`
	return s
}

type StrokeLineCap int

const (
	BUTT StrokeLineCap = iota
	ROUND
	SQUARE
)

var strokeLineCapString = []string{"butt", "round", "square"}

type Strokeable struct {
	StrokeWidth     float64       `svg:"stroke-width"`
	Stroke          string        `svg:"stroke"`
	StrokeDashArray []int         `svg:"stroke-dasharray"`
	StrokeLineCap   StrokeLineCap `svg:"stroke-linecap"`
}

func (se Strokeable) String() string {
	s := ""
	if se.Stroke != "" {
		s += ` stroke="` + jsString(se.Stroke) + `"`
	}
	if se.StrokeWidth != 0 {
		s += ` stroke-width="` + jsString(se.StrokeWidth) + `"`
	}

	if se.StrokeDashArray != nil && len(se.StrokeDashArray) > 0 {
		s += ` stroke-dasharray="`
		for k, v := range se.StrokeDashArray {
			if k != 0 {
				s += ","
			}
			s += jsString(v)
		}
		s += `"`
	}

	if se.StrokeLineCap != BUTT {
		s += ` stroke-linecap="` + strokeLineCapString[se.StrokeLineCap] + `"`
	}

	return s
}

type Draggable struct {
	Draggable bool
}

func (draggable Draggable) String() string {
	if draggable.Draggable {
		return ` class="draggable"`
	}
	return ""
}

type Fillable struct {
	Fill string `svg:"fill"`
}

func (se Fillable) String() string {
	s := ""
	if se.Fill != "" {
		s += ` fill="` + jsString(se.Fill) + `"`
	}
	return s
}

//SvgElement
type Circle struct {
	X float64 `svg:"cx"`
	Y float64 `svg:"cy"`
	R float64 `svg:"r"`
	Fillable
	Strokeable
	Draggable
}

func (se Circle) String() string {
	s := `<circle r="` + jsString(se.R) + `" cx="` + jsString(se.X) + `" cy="` + jsString(se.Y) + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</circle>`
	return s
}

//SvgElement
type Ellipse struct {
	X  float64 `svg:"cx"`
	Y  float64 `svg:"cy"`
	RX float64 `svg:"rx"`
	RY float64 `svg:"ry"`
	Fillable
	Strokeable
	Draggable
}

func (se Ellipse) String() string {
	s := `<ellipse rx="` + jsString(se.RX) + `" ry="` + jsString(se.RY) + `" cx="` + jsString(se.X) + `" cy="` + jsString(se.Y) + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</ellipse>`
	return s
}

//SvgElement
type Line struct {
	X1 float64 `svg:"x1"`
	Y1 float64 `svg:"y1"`
	X2 float64 `svg:"x2"`
	Y2 float64 `svg:"y2"`
	Strokeable
	Draggable
}

func (se Line) String() string {
	s := `<line x1="` + jsString(se.X1) + `" y1="` + jsString(se.Y1) + `" x2="` + jsString(se.X2) + `" y2="` + jsString(se.Y2) + `"`
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</line>`
	return s
}

type Point struct {
	x float64
	y float64
}

type Points []Point

func (p Points) String() string {
	s := ""
	for i := 0; i < len(p); i++ {
		s += jsString(p[i].x) + "," + jsString(p[i].y)
		if i != len(p)-1 {
			s += " "
		}
	}
	return s
}

//SvgElement
type Polygon struct {
	Points Points `svg:"points"`
	Fillable
	Strokeable
	Draggable
}

func (se Polygon) String() string {
	s := `<polygon points="` + se.Points.String() + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</polygon>`
	return s
}

//SvgElement
type Polyline struct {
	Points Points `svg:"points"`
	Fillable
	Strokeable
	Draggable
}

func (se Polyline) String() string {
	s := `<polyline points="` + se.Points.String() + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</polyline>`
	return s
}

type pathAction int

const (
	MOVETO pathAction = iota
	LINETO
	HORIZONTAL_LINETO
	VERTICAL_LINETO
	CURVETO
	SMOOTH_CURVETO
	QUADRATIC_BEZIER_CURVE
	SMOOTH_QUADRATIC_BEZIER_CURVETO
	ELLIPTICAL_ARC
	CLOSEPATH
)

var pathActionString = "MLHVCSQTAZ"

func (pa pathAction) String() string {
	return pathActionString[pa : pa+1]
}

type PathItem struct {
	Action pathAction
	Point  Point
}

type PathItems []PathItem

//SvgElement
type Path struct {
	D PathItems `svg:"d"`
	Fillable
	Strokeable
	Draggable
}

func (ps PathItems) String() string {
	s := ""
	for i := 0; i < len(ps); i++ {
		s += ps[i].Action.String()
		if ps[i].Action != CLOSEPATH {
			s += " " + jsString(ps[i].Point.x) + " " + jsString(ps[i].Point.y)
		}
		if i != len(ps)-1 {
			s += " "
		}
	}
	return s
}

func (se Path) String() string {
	s := `<path d="` + se.D.String() + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</path>`
	return s
}

type Text struct {
	Content string  `svg:"content"`
	X       float64 `svg:"x"`
	Y       float64 `svg:"y"`
	Fillable
	Strokeable
	Draggable
}

func (se Text) String() string {
	s := `<text x="` + jsString(se.X) + `" y="` + jsString(se.Y) + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + se.Content + `</text>`
	return s
}

type Group struct {
	Content []SvgElement `svg:"content"`
	Fillable
	Strokeable
	Draggable
}

func (se Group) String() string {
	s := `<g `
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >`
	for _, v := range se.Content {
		s += v.String()
	}
	s += `</g>`
	return s
}
<commit_msg>strokable type<commit_after>package svg

import (
	"github.com/gopherjs/gopherjs/js"
)

type Svg struct {
	Width   float64
	Height  float64
	Content []SvgElement
}

func jsString(a interface{}) string {
	if a != nil {
		s := js.Global.Call("String", a).String()
		if s != "undefined" && s != "null" {
			return s
		}
	}
	return ""
}

func (svg Svg) String() string {
	result := `<svg width="` + jsString(svg.Width) + `" height="` + jsString(svg.Height) + `">`
	for _, v := range svg.Content {
		result += v.String()
	}
	result += `</svg>`
	return result
}

type SvgElement interface {
	String() string
}

//SvgElement
type Rect struct {
	Width  float64 `svg:"width"`
	Height float64 `svg:"height"`
	X      float64 `svg:"x"`
	Y      float64 `svg:"y"`
	RX     float64 `svg:"rx"`
	RY     float64 `svg:"ry"`
	Strokeable
	Draggable
	Fillable
}

var unSupportMsg = "Sorry, your browser does not support inline SVG."

func (se *Rect) String() string {
	s := `<rect width="` + jsString(se.Width) + `" height="` + jsString(se.Height) + `" x="` + jsString(se.X) + `" y="` + jsString(se.Y) + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	if se.RX != 0 {
		s += ` rx="` + jsString(se.RX) + `"`
	}
	if se.RY != 0 {
		s += ` ry="` + jsString(se.RY) + `"`
	}
	s += `" >` + unSupportMsg + `</rect>`
	return s
}

type StrokeLineCap int

const (
	BUTT StrokeLineCap = iota
	ROUND
	SQUARE
)

var strokeLineCapString = []string{"butt", "round", "square"}

type Strokeable struct {
	StrokeWidth     float64       `svg:"stroke-width"`
	Stroke          string        `svg:"stroke"`
	StrokeDashArray []float64     `svg:"stroke-dasharray"`
	StrokeLineCap   StrokeLineCap `svg:"stroke-linecap"`
}

func (se Strokeable) String() string {
	s := ""
	if se.Stroke != "" {
		s += ` stroke="` + jsString(se.Stroke) + `"`
	}
	if se.StrokeWidth != 0 {
		s += ` stroke-width="` + jsString(se.StrokeWidth) + `"`
	}

	if se.StrokeDashArray != nil && len(se.StrokeDashArray) > 0 {
		s += ` stroke-dasharray="`
		for k, v := range se.StrokeDashArray {
			if k != 0 {
				s += ","
			}
			s += jsString(v)
		}
		s += `"`
	}

	if se.StrokeLineCap != BUTT {
		s += ` stroke-linecap="` + strokeLineCapString[se.StrokeLineCap] + `"`
	}

	return s
}

type Draggable struct {
	Draggable bool
}

func (draggable Draggable) String() string {
	if draggable.Draggable {
		return ` class="draggable"`
	}
	return ""
}

type Fillable struct {
	Fill string `svg:"fill"`
}

func (se Fillable) String() string {
	s := ""
	if se.Fill != "" {
		s += ` fill="` + jsString(se.Fill) + `"`
	}
	return s
}

//SvgElement
type Circle struct {
	X float64 `svg:"cx"`
	Y float64 `svg:"cy"`
	R float64 `svg:"r"`
	Fillable
	Strokeable
	Draggable
}

func (se Circle) String() string {
	s := `<circle r="` + jsString(se.R) + `" cx="` + jsString(se.X) + `" cy="` + jsString(se.Y) + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</circle>`
	return s
}

//SvgElement
type Ellipse struct {
	X  float64 `svg:"cx"`
	Y  float64 `svg:"cy"`
	RX float64 `svg:"rx"`
	RY float64 `svg:"ry"`
	Fillable
	Strokeable
	Draggable
}

func (se Ellipse) String() string {
	s := `<ellipse rx="` + jsString(se.RX) + `" ry="` + jsString(se.RY) + `" cx="` + jsString(se.X) + `" cy="` + jsString(se.Y) + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</ellipse>`
	return s
}

//SvgElement
type Line struct {
	X1 float64 `svg:"x1"`
	Y1 float64 `svg:"y1"`
	X2 float64 `svg:"x2"`
	Y2 float64 `svg:"y2"`
	Strokeable
	Draggable
}

func (se Line) String() string {
	s := `<line x1="` + jsString(se.X1) + `" y1="` + jsString(se.Y1) + `" x2="` + jsString(se.X2) + `" y2="` + jsString(se.Y2) + `"`
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</line>`
	return s
}

type Point struct {
	x float64
	y float64
}

type Points []Point

func (p Points) String() string {
	s := ""
	for i := 0; i < len(p); i++ {
		s += jsString(p[i].x) + "," + jsString(p[i].y)
		if i != len(p)-1 {
			s += " "
		}
	}
	return s
}

//SvgElement
type Polygon struct {
	Points Points `svg:"points"`
	Fillable
	Strokeable
	Draggable
}

func (se Polygon) String() string {
	s := `<polygon points="` + se.Points.String() + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</polygon>`
	return s
}

//SvgElement
type Polyline struct {
	Points Points `svg:"points"`
	Fillable
	Strokeable
	Draggable
}

func (se Polyline) String() string {
	s := `<polyline points="` + se.Points.String() + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</polyline>`
	return s
}

type pathAction int

const (
	MOVETO pathAction = iota
	LINETO
	HORIZONTAL_LINETO
	VERTICAL_LINETO
	CURVETO
	SMOOTH_CURVETO
	QUADRATIC_BEZIER_CURVE
	SMOOTH_QUADRATIC_BEZIER_CURVETO
	ELLIPTICAL_ARC
	CLOSEPATH
)

var pathActionString = "MLHVCSQTAZ"

func (pa pathAction) String() string {
	return pathActionString[pa : pa+1]
}

type PathItem struct {
	Action pathAction
	Point  Point
}

type PathItems []PathItem

//SvgElement
type Path struct {
	D PathItems `svg:"d"`
	Fillable
	Strokeable
	Draggable
}

func (ps PathItems) String() string {
	s := ""
	for i := 0; i < len(ps); i++ {
		s += ps[i].Action.String()
		if ps[i].Action != CLOSEPATH {
			s += " " + jsString(ps[i].Point.x) + " " + jsString(ps[i].Point.y)
		}
		if i != len(ps)-1 {
			s += " "
		}
	}
	return s
}

func (se Path) String() string {
	s := `<path d="` + se.D.String() + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + unSupportMsg + `</path>`
	return s
}

type Text struct {
	Content string  `svg:"content"`
	X       float64 `svg:"x"`
	Y       float64 `svg:"y"`
	Fillable
	Strokeable
	Draggable
}

func (se Text) String() string {
	s := `<text x="` + jsString(se.X) + `" y="` + jsString(se.Y) + `"`
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >` + se.Content + `</text>`
	return s
}

type Group struct {
	Content []SvgElement `svg:"content"`
	Fillable
	Strokeable
	Draggable
}

func (se Group) String() string {
	s := `<g `
	s += se.Fillable.String()
	s += se.Strokeable.String()
	s += se.Draggable.String()
	s += `" >`
	for _, v := range se.Content {
		s += v.String()
	}
	s += `</g>`
	return s
}
<|endoftext|>


<commit_before>package sms

import (
	"bufio"
	"bytes"
	"errors"
	"net"
	"strings"
)

type Sender struct {
	Id     string // Sender identifier. See Source field in smsd.cfg
	Server string // IP address:port or unix domain socket path
	Delete bool   // Will message need to be deleted after sent/reported?
	Report bool   // Is report required?
}

// Sends txt as SMS to recipients. Recipient need to be specified as
// PhoneNumber[=DstId] You can use DstId to link recipient with some other
// data in your database. Send is thread-safe.
func (s *Sender) Send(txt string, recipients ...string) error {
	if len(recipients) == 0 {
		return nil
	}
	proto := "tcp"
	if strings.IndexRune(s.Server, ':') == -1 {
		proto = "unix"
	}
	c, err := net.Dial(proto, s.Server)
	if err != nil {
		return err
	}
	w := bufio.NewWriter(c)

	if err = writeln(w, s.Id); err != nil {
		return err
	}

	if _, err = w.WriteString(recipients[0]); err != nil {
		return err
	}
	for _, num := range recipients[1:] {
		if err = w.WriteByte(' '); err != nil {
			return err
		}
		if _, err = w.WriteString(num); err != nil {
			return err
		}
	}
	if err = newLine(w); err != nil {
		return err
	}

	if s.Delete {
		if err = writeln(w, "delete"); err != nil {
			return err
		}
	}
	if s.Report {
		if err = writeln(w, "report"); err != nil {
			return err
		}
	}
	if err = newLine(w); err != nil {
		return err
	}

	if _, err = w.WriteString(strings.TrimSpace(txt)); err != nil {
		return err
	}
	if err = writeln(w, "\n."); err != nil {
		return err
	}
	if err = w.Flush(); err != nil {
		return err
	}
	// Read OK response
	buf, _, err := bufio.NewReader(c).ReadLine()
	if err != nil {
		return err
	}
	buf = bytes.TrimSpace(buf)
	if !bytes.Equal(buf, []byte{'O', 'K'}) {
		c.Close()
		return errors.New(string(buf))
	}
	return c.Close()
}

func newLine(w *bufio.Writer) error {
	return w.WriteByte('\n')
}

func writeln(w *bufio.Writer, s string) error {
	_, err := w.WriteString(s)
	if err != nil {
		return err
	}
	return newLine(w)
}

// Returns number of characters that will be used to send txt via SMS
func Len(txt string) int {
	m, n := 1, 0
	for _, r := range txt {
		if r > 0x7F {
			m = 4
		}
		n++
	}
	return m * m
}
<commit_msg>sms.Len fixed<commit_after>package sms

import (
	"bufio"
	"bytes"
	"errors"
	"net"
	"strings"
)

type Sender struct {
	Id     string // Sender identifier. See Source field in smsd.cfg
	Server string // IP address:port or unix domain socket path
	Delete bool   // Will message need to be deleted after sent/reported?
	Report bool   // Is report required?
}

// Sends txt as SMS to recipients. Recipient need to be specified as
// PhoneNumber[=DstId] You can use DstId to link recipient with some other
// data in your database. Send is thread-safe.
func (s *Sender) Send(txt string, recipients ...string) error {
	if len(recipients) == 0 {
		return nil
	}
	proto := "tcp"
	if strings.IndexRune(s.Server, ':') == -1 {
		proto = "unix"
	}
	c, err := net.Dial(proto, s.Server)
	if err != nil {
		return err
	}
	w := bufio.NewWriter(c)

	if err = writeln(w, s.Id); err != nil {
		return err
	}

	if _, err = w.WriteString(recipients[0]); err != nil {
		return err
	}
	for _, num := range recipients[1:] {
		if err = w.WriteByte(' '); err != nil {
			return err
		}
		if _, err = w.WriteString(num); err != nil {
			return err
		}
	}
	if err = newLine(w); err != nil {
		return err
	}

	if s.Delete {
		if err = writeln(w, "delete"); err != nil {
			return err
		}
	}
	if s.Report {
		if err = writeln(w, "report"); err != nil {
			return err
		}
	}
	if err = newLine(w); err != nil {
		return err
	}

	if _, err = w.WriteString(strings.TrimSpace(txt)); err != nil {
		return err
	}
	if err = writeln(w, "\n."); err != nil {
		return err
	}
	if err = w.Flush(); err != nil {
		return err
	}
	// Read OK response
	buf, _, err := bufio.NewReader(c).ReadLine()
	if err != nil {
		return err
	}
	buf = bytes.TrimSpace(buf)
	if !bytes.Equal(buf, []byte{'O', 'K'}) {
		c.Close()
		return errors.New(string(buf))
	}
	return c.Close()
}

func newLine(w *bufio.Writer) error {
	return w.WriteByte('\n')
}

func writeln(w *bufio.Writer, s string) error {
	_, err := w.WriteString(s)
	if err != nil {
		return err
	}
	return newLine(w)
}

// Returns number of characters that will be used to send txt via SMS
func Len(txt string) int {
	m, n := 1, 0
	for _, r := range txt {
		if r > 0x7F {
			m = 4
		}
		n++
	}
	return m * n
}
<|endoftext|>


<commit_before>package smtpapi

import (
	"bytes"
	"encoding/json"
	"fmt"
	"unicode/utf16"
)

// Version represents the current version of the smtpapi-go library
const Version = "0.6.3"

// SMTPAPIHeader will be used to set up X-SMTPAPI params
type SMTPAPIHeader struct {
	To          []string               `json:"to,omitempty"`
	Sub         map[string][]string    `json:"sub,omitempty"`
	Section     map[string]string      `json:"section,omitempty"`
	Category    []string               `json:"category,omitempty"`
	UniqueArgs  map[string]string      `json:"unique_args,omitempty"`
	Filters     map[string]Filter      `json:"filters,omitempty"`
	ASMGroupID  int                    `json:"asm_group_id,omitempty"`
	ASMGroups   []int                  `json:"asm_groups_to_display,omitempty"`
	SendAt      int64                  `json:"send_at,omitempty"`
	SendEachAt  []int64                `json:"send_each_at,omitempty"`
	IpPool      string                 `json:"ip_pool,omitempty"`
	BatchID     string                 `json:"batch_id,omitempty"`
	DynamicData map[string]interface{} `json:"dynamic_template_data,omitempty"`
}

// Filter represents an App/Filter and its settings
type Filter struct {
	Settings map[string]interface{} `json:"settings,omitempty"`
}

// NewSMTPAPIHeader creates a new header struct
func NewSMTPAPIHeader() *SMTPAPIHeader {
	return &SMTPAPIHeader{}
}

// AddTo appends a single email to the To header
func (h *SMTPAPIHeader) AddTo(email string) {
	h.To = append(h.To, email)
}

// AddTos appends multiple emails to the To header
func (h *SMTPAPIHeader) AddTos(emails []string) {
	for i := 0; i < len(emails); i++ {
		h.AddTo(emails[i])
	}
}

// SetTos sets the value of the To header
func (h *SMTPAPIHeader) SetTos(emails []string) {
	h.To = emails
}

// AddSubstitution adds a new substitution to a specific key
func (h *SMTPAPIHeader) AddSubstitution(key, sub string) {
	if h.Sub == nil {
		h.Sub = make(map[string][]string)
	}
	h.Sub[key] = append(h.Sub[key], sub)
}

// AddSubstitutions adds a multiple substitutions to a specific key
func (h *SMTPAPIHeader) AddSubstitutions(key string, subs []string) {
	for i := 0; i < len(subs); i++ {
		h.AddSubstitution(key, subs[i])
	}
}

// SetSubstitutions sets the value of the substitutions on the Sub header
func (h *SMTPAPIHeader) SetSubstitutions(sub map[string][]string) {
	h.Sub = sub
}

// AddSection sets the value for a specific section
func (h *SMTPAPIHeader) AddSection(section, value string) {
	if h.Section == nil {
		h.Section = make(map[string]string)
	}
	h.Section[section] = value
}

// SetSections sets the value for the Section header
func (h *SMTPAPIHeader) SetSections(sections map[string]string) {
	h.Section = sections
}

// AddCategory adds a new category to the Category header
func (h *SMTPAPIHeader) AddCategory(category string) {
	h.Category = append(h.Category, category)
}

// AddCategories adds multiple categories to the Category header
func (h *SMTPAPIHeader) AddCategories(categories []string) {
	for i := 0; i < len(categories); i++ {
		h.AddCategory(categories[i])
	}
}

// SetCategories will set the value of the Categories field
func (h *SMTPAPIHeader) SetCategories(categories []string) {
	h.Category = categories
}

// SetASMGroupID will set the value of the ASMGroupID field
func (h *SMTPAPIHeader) SetASMGroupID(groupID int) {
	h.ASMGroupID = groupID
}

// AddASMGroupToDisplay adds a new ASM group ID to be displayed
func (h *SMTPAPIHeader) AddASMGroupToDisplay(groupID int) {
	h.ASMGroups = append(h.ASMGroups, groupID)
}

// AddASMGroupsToDisplay adds multiple ASM group IDs to be displayed
func (h *SMTPAPIHeader) AddASMGroupsToDisplay(groupIDs []int) {
	for i := 0; i < len(groupIDs); i++ {
		h.AddASMGroupToDisplay(groupIDs[i])
	}
}

// SetASMGroupsToDisplay will set the value of the ASMGroups field
func (h *SMTPAPIHeader) SetASMGroupsToDisplay(groups []int) {
	h.ASMGroups = groups
}

// AddUniqueArg will set the value of a specific argument
func (h *SMTPAPIHeader) AddUniqueArg(arg, value string) {
	if h.UniqueArgs == nil {
		h.UniqueArgs = make(map[string]string)
	}
	h.UniqueArgs[arg] = value
}

// SetUniqueArgs will set the value of the Unique_args header
func (h *SMTPAPIHeader) SetUniqueArgs(args map[string]string) {
	h.UniqueArgs = args
}

// AddFilter will set the specific setting for a filter
func (h *SMTPAPIHeader) AddFilter(filter, setting string, value interface{}) {
	if h.Filters == nil {
		h.Filters = make(map[string]Filter)
	}
	if _, ok := h.Filters[filter]; !ok {
		h.Filters[filter] = Filter{
			Settings: make(map[string]interface{}),
		}
	}
	h.Filters[filter].Settings[setting] = value
}

// SetFilter takes in a Filter struct with predetermined settings and sets it for such Filter key
func (h *SMTPAPIHeader) SetFilter(filter string, value *Filter) {
	if h.Filters == nil {
		h.Filters = make(map[string]Filter)
	}
	h.Filters[filter] = *value
}

// SetSendAt takes in a timestamp which determines when the email will be sent
func (h *SMTPAPIHeader) SetSendAt(sendAt int64) {
	h.SendAt = sendAt
}

// AddSendEachAt takes in a timestamp and pushes it into a list Must match length of To emails
func (h *SMTPAPIHeader) AddSendEachAt(sendEachAt int64) {
	h.SendEachAt = append(h.SendEachAt, sendEachAt)
}

// SetSendEachAt takes an array of timestamps. Must match length of To emails
func (h *SMTPAPIHeader) SetSendEachAt(sendEachAt []int64) {
	h.SendEachAt = sendEachAt
}

// SetIpPool takes a strings and sets the IpPool field
func (h *SMTPAPIHeader) SetIpPool(ipPool string) {
	h.IpPool = ipPool
}

// Unicode escape
func escapeUnicode(input string) string {
	//var buffer bytes.Buffer
	buffer := bytes.NewBufferString("")
	for _, r := range input {
		if r > 65535 {
			// surrogate pair
			var r1, r2 = utf16.EncodeRune(r)
			var s = fmt.Sprintf("\\u%x\\u%x", r1, r2)
			// error always nil https://golang.org/pkg/bytes/#Buffer.WriteString
			buffer.WriteString(s) // nolint: gas, gosec
		} else if r > 127 {
			var s = fmt.Sprintf("\\u%04x", r)
			// error always nil https://golang.org/pkg/bytes/#Buffer.WriteString
			buffer.WriteString(s) // nolint: gas, gosec
		} else {
			var s = fmt.Sprintf("%c", r)
			// error always nil https://golang.org/pkg/bytes/#Buffer.WriteString
			buffer.WriteString(s) // nolint: gas, gosec
		}
	}
	return buffer.String()
}

// JSONString returns the representation of the Header
func (h *SMTPAPIHeader) JSONString() (string, error) {
	headers, e := json.Marshal(h)
	return escapeUnicode(string(headers)), e
}

// Load allows you to load a pre-formed x-smtpapi header
func (h *SMTPAPIHeader) Load(b []byte) error {
	return json.Unmarshal(b, h)
}
<commit_msg>Release v0.6.4<commit_after>package smtpapi

import (
	"bytes"
	"encoding/json"
	"fmt"
	"unicode/utf16"
)

// Version represents the current version of the smtpapi-go library
const Version = "0.6.4"

// SMTPAPIHeader will be used to set up X-SMTPAPI params
type SMTPAPIHeader struct {
	To          []string               `json:"to,omitempty"`
	Sub         map[string][]string    `json:"sub,omitempty"`
	Section     map[string]string      `json:"section,omitempty"`
	Category    []string               `json:"category,omitempty"`
	UniqueArgs  map[string]string      `json:"unique_args,omitempty"`
	Filters     map[string]Filter      `json:"filters,omitempty"`
	ASMGroupID  int                    `json:"asm_group_id,omitempty"`
	ASMGroups   []int                  `json:"asm_groups_to_display,omitempty"`
	SendAt      int64                  `json:"send_at,omitempty"`
	SendEachAt  []int64                `json:"send_each_at,omitempty"`
	IpPool      string                 `json:"ip_pool,omitempty"`
	BatchID     string                 `json:"batch_id,omitempty"`
	DynamicData map[string]interface{} `json:"dynamic_template_data,omitempty"`
}

// Filter represents an App/Filter and its settings
type Filter struct {
	Settings map[string]interface{} `json:"settings,omitempty"`
}

// NewSMTPAPIHeader creates a new header struct
func NewSMTPAPIHeader() *SMTPAPIHeader {
	return &SMTPAPIHeader{}
}

// AddTo appends a single email to the To header
func (h *SMTPAPIHeader) AddTo(email string) {
	h.To = append(h.To, email)
}

// AddTos appends multiple emails to the To header
func (h *SMTPAPIHeader) AddTos(emails []string) {
	for i := 0; i < len(emails); i++ {
		h.AddTo(emails[i])
	}
}

// SetTos sets the value of the To header
func (h *SMTPAPIHeader) SetTos(emails []string) {
	h.To = emails
}

// AddSubstitution adds a new substitution to a specific key
func (h *SMTPAPIHeader) AddSubstitution(key, sub string) {
	if h.Sub == nil {
		h.Sub = make(map[string][]string)
	}
	h.Sub[key] = append(h.Sub[key], sub)
}

// AddSubstitutions adds a multiple substitutions to a specific key
func (h *SMTPAPIHeader) AddSubstitutions(key string, subs []string) {
	for i := 0; i < len(subs); i++ {
		h.AddSubstitution(key, subs[i])
	}
}

// SetSubstitutions sets the value of the substitutions on the Sub header
func (h *SMTPAPIHeader) SetSubstitutions(sub map[string][]string) {
	h.Sub = sub
}

// AddSection sets the value for a specific section
func (h *SMTPAPIHeader) AddSection(section, value string) {
	if h.Section == nil {
		h.Section = make(map[string]string)
	}
	h.Section[section] = value
}

// SetSections sets the value for the Section header
func (h *SMTPAPIHeader) SetSections(sections map[string]string) {
	h.Section = sections
}

// AddCategory adds a new category to the Category header
func (h *SMTPAPIHeader) AddCategory(category string) {
	h.Category = append(h.Category, category)
}

// AddCategories adds multiple categories to the Category header
func (h *SMTPAPIHeader) AddCategories(categories []string) {
	for i := 0; i < len(categories); i++ {
		h.AddCategory(categories[i])
	}
}

// SetCategories will set the value of the Categories field
func (h *SMTPAPIHeader) SetCategories(categories []string) {
	h.Category = categories
}

// SetASMGroupID will set the value of the ASMGroupID field
func (h *SMTPAPIHeader) SetASMGroupID(groupID int) {
	h.ASMGroupID = groupID
}

// AddASMGroupToDisplay adds a new ASM group ID to be displayed
func (h *SMTPAPIHeader) AddASMGroupToDisplay(groupID int) {
	h.ASMGroups = append(h.ASMGroups, groupID)
}

// AddASMGroupsToDisplay adds multiple ASM group IDs to be displayed
func (h *SMTPAPIHeader) AddASMGroupsToDisplay(groupIDs []int) {
	for i := 0; i < len(groupIDs); i++ {
		h.AddASMGroupToDisplay(groupIDs[i])
	}
}

// SetASMGroupsToDisplay will set the value of the ASMGroups field
func (h *SMTPAPIHeader) SetASMGroupsToDisplay(groups []int) {
	h.ASMGroups = groups
}

// AddUniqueArg will set the value of a specific argument
func (h *SMTPAPIHeader) AddUniqueArg(arg, value string) {
	if h.UniqueArgs == nil {
		h.UniqueArgs = make(map[string]string)
	}
	h.UniqueArgs[arg] = value
}

// SetUniqueArgs will set the value of the Unique_args header
func (h *SMTPAPIHeader) SetUniqueArgs(args map[string]string) {
	h.UniqueArgs = args
}

// AddFilter will set the specific setting for a filter
func (h *SMTPAPIHeader) AddFilter(filter, setting string, value interface{}) {
	if h.Filters == nil {
		h.Filters = make(map[string]Filter)
	}
	if _, ok := h.Filters[filter]; !ok {
		h.Filters[filter] = Filter{
			Settings: make(map[string]interface{}),
		}
	}
	h.Filters[filter].Settings[setting] = value
}

// SetFilter takes in a Filter struct with predetermined settings and sets it for such Filter key
func (h *SMTPAPIHeader) SetFilter(filter string, value *Filter) {
	if h.Filters == nil {
		h.Filters = make(map[string]Filter)
	}
	h.Filters[filter] = *value
}

// SetSendAt takes in a timestamp which determines when the email will be sent
func (h *SMTPAPIHeader) SetSendAt(sendAt int64) {
	h.SendAt = sendAt
}

// AddSendEachAt takes in a timestamp and pushes it into a list Must match length of To emails
func (h *SMTPAPIHeader) AddSendEachAt(sendEachAt int64) {
	h.SendEachAt = append(h.SendEachAt, sendEachAt)
}

// SetSendEachAt takes an array of timestamps. Must match length of To emails
func (h *SMTPAPIHeader) SetSendEachAt(sendEachAt []int64) {
	h.SendEachAt = sendEachAt
}

// SetIpPool takes a strings and sets the IpPool field
func (h *SMTPAPIHeader) SetIpPool(ipPool string) {
	h.IpPool = ipPool
}

// Unicode escape
func escapeUnicode(input string) string {
	//var buffer bytes.Buffer
	buffer := bytes.NewBufferString("")
	for _, r := range input {
		if r > 65535 {
			// surrogate pair
			var r1, r2 = utf16.EncodeRune(r)
			var s = fmt.Sprintf("\\u%x\\u%x", r1, r2)
			// error always nil https://golang.org/pkg/bytes/#Buffer.WriteString
			buffer.WriteString(s) // nolint: gas, gosec
		} else if r > 127 {
			var s = fmt.Sprintf("\\u%04x", r)
			// error always nil https://golang.org/pkg/bytes/#Buffer.WriteString
			buffer.WriteString(s) // nolint: gas, gosec
		} else {
			var s = fmt.Sprintf("%c", r)
			// error always nil https://golang.org/pkg/bytes/#Buffer.WriteString
			buffer.WriteString(s) // nolint: gas, gosec
		}
	}
	return buffer.String()
}

// JSONString returns the representation of the Header
func (h *SMTPAPIHeader) JSONString() (string, error) {
	headers, e := json.Marshal(h)
	return escapeUnicode(string(headers)), e
}

// Load allows you to load a pre-formed x-smtpapi header
func (h *SMTPAPIHeader) Load(b []byte) error {
	return json.Unmarshal(b, h)
}
<|endoftext|>


<commit_before>package recorder

import (
	"os"
	"os/exec"
	//"strconv"
)

var FfmpegPath = "ffmpeg"

func NewFfmpeg(r *Recorder) (*Command, error) {

	// Check if custom phantomjs bin path
	if path := os.Getenv("FFMPEG_BIN"); path != "" {
		FfmpegPath = path
	}

	cmd := exec.Command(FfmpegPath, "-r", "10", "-y", "-c:v", "png", "-f", "image2pipe", "-i", "-", "-c:v", "libx264", "-pix_fmt", "yuv420p", "-movflags", "+faststart", "/tmp"+r.OutputFile)

	// Map various pipes
	inPipe, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}

	outPipe, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	errPipe, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}

	ffm := &Command{
		Cmd:    cmd,
		In:     inPipe,
		Out:    outPipe,
		Errout: errPipe,
	}

	return ffm, nil

}
<commit_msg>get temp file from tmp dir<commit_after>package recorder

import (
	"os"
	"os/exec"
	//"strconv"
)

var FfmpegPath = "ffmpeg"

func NewFfmpeg(r *Recorder) (*Command, error) {

	// Check if custom phantomjs bin path
	if path := os.Getenv("FFMPEG_BIN"); path != "" {
		FfmpegPath = path
	}

	cmd := exec.Command(FfmpegPath, "-r", "10", "-y", "-c:v", "png", "-f", "image2pipe", "-i", "-", "-c:v", "libx264", "-pix_fmt", "yuv420p", "-movflags", "+faststart", "/tmp/"+r.OutputFile)

	// Map various pipes
	inPipe, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}

	outPipe, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	errPipe, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}

	ffm := &Command{
		Cmd:    cmd,
		In:     inPipe,
		Out:    outPipe,
		Errout: errPipe,
	}

	return ffm, nil

}
<|endoftext|>


<commit_before>package model

import (
	"github.com/viant/endly"
	"github.com/viant/toolbox/data"
	"time"
)

//Activity represents pipeline or workflow activity
type Activity struct {
	*NeatlyTag
	Caller          string
	Task            string
	Service         string
	Action          string
	Description     string
	Error           string
	StartTime       time.Time
	Ineligible      bool
	Request         interface{}
	Response        map[string]interface{}
	ServiceResponse *endly.ServiceResponse
}

//FormatTag return a formatted tag
func (a *Activity) FormatTag() string {
	if a.TagIndex != "" {
		return "[" + a.Tag + a.TagIndex + "]"
	}
	return "[" + a.Tag + "]"
}

//NewActivity returns a new workflow Activity.
func NewActivity(context *endly.Context, action *Action, state data.Map) *Activity {
	var result = &Activity{
		Action:          state.ExpandAsText(action.Action),
		Service:         state.ExpandAsText(action.Service),
		NeatlyTag:       action.NeatlyTag,
		Description:     context.Expand(action.AbstractNode.Description),
		Request:         action.Request,
		Response:        make(map[string]interface{}),
		StartTime:       time.Now(),
		ServiceResponse: &endly.ServiceResponse{},
	}
	if result.NeatlyTag == nil {
		result.NeatlyTag = &NeatlyTag{}
	}
	if result.Request == nil {
		result.Request = map[string]interface{}{}
	}
	return result
}

//ActivityEndEvent represents Activity end event type.
type ActivityEndEvent struct {
	Response interface{}
}

//NewActivityEndEvent creates a new ActivityEndEvent
func NewActivityEndEvent(response interface{}) *ActivityEndEvent {
	return &ActivityEndEvent{
		Response: response,
	}
}
<commit_msg>patched service and action expansion<commit_after>package model

import (
	"github.com/viant/endly"
	"github.com/viant/toolbox/data"
	"time"
	"github.com/viant/toolbox"
)

//Activity represents pipeline or workflow activity
type Activity struct {
	*NeatlyTag
	Caller          string
	Task            string
	Service         string
	Action          string
	Description     string
	Error           string
	StartTime       time.Time
	Ineligible      bool
	Request         interface{}
	Response        map[string]interface{}
	ServiceResponse *endly.ServiceResponse
}

//FormatTag return a formatted tag
func (a *Activity) FormatTag() string {
	if a.TagIndex != "" {
		return "[" + a.Tag + a.TagIndex + "]"
	}
	return "[" + a.Tag + "]"
}

//NewActivity returns a new workflow Activity.
func NewActivity(context *endly.Context, action *Action, state data.Map) *Activity {
	var result = &Activity{
		Action:          state.ExpandAsText(action.Action),
		Service:         state.ExpandAsText(action.Service),
		NeatlyTag:       action.NeatlyTag,
		Description:     context.Expand(action.AbstractNode.Description),
		Request:         action.Request,
		Response:        make(map[string]interface{}),
		StartTime:       time.Now(),
		ServiceResponse: &endly.ServiceResponse{},
	}
	if result.NeatlyTag == nil {
		result.NeatlyTag = &NeatlyTag{}
	}
	if toolbox.IsString(result.Request) {
		result.Request = state.Expand(result.Request)
	}
	if result.Request == nil {
		result.Request = map[string]interface{}{}
	}
	return result
}

//ActivityEndEvent represents Activity end event type.
type ActivityEndEvent struct {
	Response interface{}
}

//NewActivityEndEvent creates a new ActivityEndEvent
func NewActivityEndEvent(response interface{}) *ActivityEndEvent {
	return &ActivityEndEvent{
		Response: response,
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"bufio"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"unicode/utf8"
)

const (
	_ = -iota
	EOF
	WORD
	IF
	THEN
	ELSE
	FI
	WHILE
	DO
	DONE
)

func parse(r io.Reader, name string) error {
	p := &parser{
		r:    bufio.NewReader(r),
		name: name,
		line: 1,
		col:  0,
	}
	p.next()
	p.program()
	return p.err
}

type parser struct {
	r   *bufio.Reader
	err error

	tok int32
	val string

	name string
	line int
	col  int
}

var reserved = map[rune]bool{
	'\n': true,
	'#':  true,
	'=':  true,
	'&':  true,
	'>':  true,
	'<':  true,
	'|':  true,
	';':  true,
	'(':  true,
	')':  true,
	'{':  true,
	'}':  true,
	'$':  true,
}

var quote = map[rune]bool{
	'"':  true,
	'\'': true,
}

var space = map[rune]bool{
	' ':  true,
	'\t': true,
}

var (
	ident = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
	num   = regexp.MustCompile(`^[1-9][0-9]*$`)
)

func (p *parser) next() {
	if p.tok == EOF {
		return
	}
	r := ' '
	var err error
	for space[r] {
		r, _, err = p.r.ReadRune()
		if err == io.EOF {
			p.tok = EOF
			p.col++
			return
		}
		if err != nil {
			p.errPass(err)
			return
		}
		p.col++
	}
	if r == '\\' {
		p.next()
		if p.got('\n') {
			return
		}
		if err := p.r.UnreadRune(); err != nil {
			p.errPass(err)
			return
		}
		p.col--
	}
	if reserved[r] {
		switch r {
		case '#':
			p.discardUpTo('\n')
			p.next()
			return
		case '$':
			p.next()
			if p.got('{') {
				p.discardUpTo('}')
				p.tok = WORD
				return
			}
		case '\n':
			p.line++
			p.col = 0
			p.tok = '\n'
			return
		default:
			p.tok = r
			return
		}
	}
	if quote[r] {
		p.strContent(byte(r))
		if p.tok == EOF {
			return
		}
		p.col--
		p.tok = WORD
		return
	}
	rs := []rune{r}
	for !reserved[r] && !quote[r] && !space[r] {
		r, _, err = p.r.ReadRune()
		if err == io.EOF {
			p.col++
			break
		}
		if err != nil {
			p.errPass(err)
			return
		}
		p.col++
		rs = append(rs, r)
	}
	if err != io.EOF && len(rs) > 1 {
		if err := p.r.UnreadRune(); err != nil {
			p.errPass(err)
			return
		}
		rs = rs[:len(rs)-1]
	}
	p.col--
	p.tok = WORD
	p.val = string(rs)
	return
}

func (p *parser) strContent(delim byte) {
	v := []string{string(delim)}
	p.col++
	for {
		s, err := p.r.ReadString(delim)
		for _, r := range s {
			if r == '\n' {
				p.line++
				p.col = 0
			} else {
				p.col++
			}
		}
		if err == io.EOF {
			p.tok = EOF
			p.errWanted(rune(delim))
		} else if err != nil {
			p.errPass(err)
		}
		v = append(v, s)
		if delim == '\'' {
			break
		}
		if len(s) > 1 && s[len(s)-2] == '\\' && s[len(s)-1] == delim {
			continue
		}
		break
	}
	p.val = strings.Join(v, "")
}

func (p *parser) discardUpTo(delim byte) {
	b, err := p.r.ReadBytes(delim)
	if err == io.EOF {
		p.tok = EOF
		p.col++
	} else if err != nil {
		p.errPass(err)
	}
	p.col += utf8.RuneCount(b)
}

func (p *parser) peek(tok int32) bool {
	return p.tok == tok || (p.tok == WORD && p.val == tokStrs[tok])
}

func (p *parser) got(tok int32) bool {
	if p.peek(tok) {
		p.next()
		return true
	}
	return false
}

func (p *parser) want(tok int32) {
	if !p.peek(tok) {
		p.errWanted(tok)
		return
	}
	p.next()
}

var tokStrs = map[int32]string{
	IF:    "if",
	THEN:  "then",
	ELSE:  "else",
	FI:    "fi",
	WHILE: "while",
	DO:    "do",
	DONE:  "done",
}

var tokNames = map[int32]string{
	EOF:  `EOF`,
	WORD: `word`,

	IF:    `"if"`,
	THEN:  `"then"`,
	ELSE:  `"else"`,
	FI:    `"fi"`,
	WHILE: `"while"`,
	DO:    `"do"`,
	DONE:  `"done"`,
}

func tokName(tok int32) string {
	if s, e := tokNames[tok]; e {
		return s
	}
	return strconv.QuoteRune(tok)
}

func (p *parser) errPass(err error) {
	if p.err == nil {
		p.err = err
	}
	p.tok = EOF
}

func (p *parser) lineErr(format string, v ...interface{}) {
	pos := fmt.Sprintf("%s:%d:%d: ", p.name, p.line, p.col)
	p.errPass(fmt.Errorf(pos+format, v...))
}

func (p *parser) errWantedStr(s string) {
	p.lineErr("unexpected token %s, wanted %s", tokName(p.tok), s)
}

func (p *parser) errWanted(tok int32) {
	p.errWantedStr(tokName(tok))
}

func (p *parser) errAfterStr(s string) {
	p.lineErr("unexpected token %s after %s", tokName(p.tok), s)
}

func (p *parser) program() {
	for p.tok != EOF {
		if p.got('\n') {
			continue
		}
		p.command()
	}
}

func (p *parser) command() {
	switch {
	case p.got('('):
		count := 0
		for p.tok != EOF && !p.peek(')') {
			if p.got('\n') {
				continue
			}
			p.command()
			count++
		}
		if count == 0 {
			p.errWantedStr("command")
		}
		p.want(')')
	case p.got(IF):
		p.command()
		p.want(THEN)
		for p.tok != EOF && !p.peek(FI) && !p.peek(ELSE) {
			if p.got('\n') {
				continue
			}
			p.command()
		}
		if p.got(ELSE) {
			for p.tok != EOF && !p.peek(FI) {
				if p.got('\n') {
					continue
				}
				p.command()
			}
		}
		p.want(FI)
	case p.got(WHILE):
		p.command()
		p.want(DO)
		for p.tok != EOF && !p.peek(DONE) {
			if p.got('\n') {
				continue
			}
			p.command()
		}
		p.want(DONE)
	case p.got(WORD):
		for p.tok != EOF {
			lval := p.val
			switch {
			case p.got(WORD):
			case p.got('='):
				if !ident.MatchString(lval) {
					p.col -= utf8.RuneCountInString(lval)
					p.col -= utf8.RuneCountInString(p.val)
					p.lineErr("invalid var name %q", lval)
					return
				}
				p.got(WORD)
			case p.got('&'):
				if p.got('&') {
					p.command()
				}
				return
			case p.got('|'):
				p.got('|')
				p.command()
				return
			case p.got('('):
				if !ident.MatchString(lval) {
					p.col -= utf8.RuneCountInString(lval)
					p.col--
					p.lineErr("invalid func name %q", lval)
					return
				}
				p.want(')')
				p.command()
				return
			case p.got('>'):
				p.redirectDest()
			case p.got('<'):
				p.want(WORD)
			case p.got(';'):
				return
			case p.got('\n'):
				return
			default:
				p.errAfterStr("command")
			}
		}
	case p.got('{'):
		for p.tok != EOF && !p.peek('}') {
			if p.got('\n') {
				continue
			}
			p.command()
		}
		p.want('}')
		switch {
		case p.got('&'):
			if p.got('&') {
				p.command()
			}
			return
		case p.got('|'):
			p.got('|')
			p.command()
			return
		case p.got('>'):
			p.redirectDest()
		case p.got('<'):
			p.want(WORD)
		case p.got(';'):
			return
		case p.got('\n'):
			return
		default:
			p.errAfterStr("block")
		}
	default:
		p.errWantedStr("command")
	}
}

func (p *parser) redirectDest() {
	switch {
	case p.got('&'):
		p.want(WORD)
		if !num.MatchString(p.val) {
			p.col -= utf8.RuneCountInString(p.val)
			p.col++
			p.lineErr("invalid fd %q", p.val)
		}
		return
	case p.got('>'):
	}
	p.want(WORD)
}
<commit_msg>Simplify some return/break logic<commit_after>// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package sh

import (
	"bufio"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"unicode/utf8"
)

const (
	_ = -iota
	EOF
	WORD
	IF
	THEN
	ELSE
	FI
	WHILE
	DO
	DONE
)

func parse(r io.Reader, name string) error {
	p := &parser{
		r:    bufio.NewReader(r),
		name: name,
		line: 1,
		col:  0,
	}
	p.next()
	p.program()
	return p.err
}

type parser struct {
	r   *bufio.Reader
	err error

	tok int32
	val string

	name string
	line int
	col  int
}

var reserved = map[rune]bool{
	'\n': true,
	'#':  true,
	'=':  true,
	'&':  true,
	'>':  true,
	'<':  true,
	'|':  true,
	';':  true,
	'(':  true,
	')':  true,
	'{':  true,
	'}':  true,
	'$':  true,
}

var quote = map[rune]bool{
	'"':  true,
	'\'': true,
}

var space = map[rune]bool{
	' ':  true,
	'\t': true,
}

var (
	ident = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
	num   = regexp.MustCompile(`^[1-9][0-9]*$`)
)

func (p *parser) next() {
	if p.tok == EOF {
		return
	}
	r := ' '
	var err error
	for space[r] {
		r, _, err = p.r.ReadRune()
		if err == io.EOF {
			p.tok = EOF
			p.col++
			return
		}
		if err != nil {
			p.errPass(err)
			return
		}
		p.col++
	}
	if r == '\\' {
		p.next()
		if p.got('\n') {
			return
		}
		if err := p.r.UnreadRune(); err != nil {
			p.errPass(err)
			return
		}
		p.col--
	}
	if reserved[r] {
		switch r {
		case '#':
			p.discardUpTo('\n')
			p.next()
			return
		case '$':
			p.next()
			if p.got('{') {
				p.discardUpTo('}')
				p.tok = WORD
				return
			}
		case '\n':
			p.line++
			p.col = 0
			p.tok = '\n'
			return
		default:
			p.tok = r
			return
		}
	}
	if quote[r] {
		p.strContent(byte(r))
		if p.tok == EOF {
			return
		}
		p.col--
		p.tok = WORD
		return
	}
	rs := []rune{r}
	for !reserved[r] && !quote[r] && !space[r] {
		r, _, err = p.r.ReadRune()
		if err == io.EOF {
			p.col++
			break
		}
		if err != nil {
			p.errPass(err)
			return
		}
		p.col++
		rs = append(rs, r)
	}
	if err != io.EOF && len(rs) > 1 {
		if err := p.r.UnreadRune(); err != nil {
			p.errPass(err)
			return
		}
		rs = rs[:len(rs)-1]
	}
	p.col--
	p.tok = WORD
	p.val = string(rs)
	return
}

func (p *parser) strContent(delim byte) {
	v := []string{string(delim)}
	p.col++
	for {
		s, err := p.r.ReadString(delim)
		for _, r := range s {
			if r == '\n' {
				p.line++
				p.col = 0
			} else {
				p.col++
			}
		}
		if err == io.EOF {
			p.tok = EOF
			p.errWanted(rune(delim))
		} else if err != nil {
			p.errPass(err)
		}
		v = append(v, s)
		if delim == '\'' {
			break
		}
		if len(s) > 1 && s[len(s)-2] == '\\' && s[len(s)-1] == delim {
			continue
		}
		break
	}
	p.val = strings.Join(v, "")
}

func (p *parser) discardUpTo(delim byte) {
	b, err := p.r.ReadBytes(delim)
	if err == io.EOF {
		p.tok = EOF
		p.col++
	} else if err != nil {
		p.errPass(err)
	}
	p.col += utf8.RuneCount(b)
}

func (p *parser) peek(tok int32) bool {
	return p.tok == tok || (p.tok == WORD && p.val == tokStrs[tok])
}

func (p *parser) got(tok int32) bool {
	if p.peek(tok) {
		p.next()
		return true
	}
	return false
}

func (p *parser) want(tok int32) {
	if !p.peek(tok) {
		p.errWanted(tok)
		return
	}
	p.next()
}

var tokStrs = map[int32]string{
	IF:    "if",
	THEN:  "then",
	ELSE:  "else",
	FI:    "fi",
	WHILE: "while",
	DO:    "do",
	DONE:  "done",
}

var tokNames = map[int32]string{
	EOF:  `EOF`,
	WORD: `word`,

	IF:    `"if"`,
	THEN:  `"then"`,
	ELSE:  `"else"`,
	FI:    `"fi"`,
	WHILE: `"while"`,
	DO:    `"do"`,
	DONE:  `"done"`,
}

func tokName(tok int32) string {
	if s, e := tokNames[tok]; e {
		return s
	}
	return strconv.QuoteRune(tok)
}

func (p *parser) errPass(err error) {
	if p.err == nil {
		p.err = err
	}
	p.tok = EOF
}

func (p *parser) lineErr(format string, v ...interface{}) {
	pos := fmt.Sprintf("%s:%d:%d: ", p.name, p.line, p.col)
	p.errPass(fmt.Errorf(pos+format, v...))
}

func (p *parser) errWantedStr(s string) {
	p.lineErr("unexpected token %s, wanted %s", tokName(p.tok), s)
}

func (p *parser) errWanted(tok int32) {
	p.errWantedStr(tokName(tok))
}

func (p *parser) errAfterStr(s string) {
	p.lineErr("unexpected token %s after %s", tokName(p.tok), s)
}

func (p *parser) program() {
	for p.tok != EOF {
		if p.got('\n') {
			continue
		}
		p.command()
	}
}

func (p *parser) command() {
	switch {
	case p.got('('):
		count := 0
		for p.tok != EOF && !p.peek(')') {
			if p.got('\n') {
				continue
			}
			p.command()
			count++
		}
		if count == 0 {
			p.errWantedStr("command")
		}
		p.want(')')
	case p.got(IF):
		p.command()
		p.want(THEN)
		for p.tok != EOF && !p.peek(FI) && !p.peek(ELSE) {
			if p.got('\n') {
				continue
			}
			p.command()
		}
		if p.got(ELSE) {
			for p.tok != EOF && !p.peek(FI) {
				if p.got('\n') {
					continue
				}
				p.command()
			}
		}
		p.want(FI)
	case p.got(WHILE):
		p.command()
		p.want(DO)
		for p.tok != EOF && !p.peek(DONE) {
			if p.got('\n') {
				continue
			}
			p.command()
		}
		p.want(DONE)
	case p.got(WORD):
	args:
		for p.tok != EOF {
			lval := p.val
			switch {
			case p.got(WORD):
			case p.got('='):
				if !ident.MatchString(lval) {
					p.col -= utf8.RuneCountInString(lval)
					p.col -= utf8.RuneCountInString(p.val)
					p.lineErr("invalid var name %q", lval)
					return
				}
				p.got(WORD)
			case p.got('&'):
				if p.got('&') {
					p.command()
				}
				break args
			case p.got('|'):
				p.got('|')
				p.command()
				break args
			case p.got('('):
				if !ident.MatchString(lval) {
					p.col -= utf8.RuneCountInString(lval)
					p.col--
					p.lineErr("invalid func name %q", lval)
					break args
				}
				p.want(')')
				p.command()
				break args
			case p.got('>'):
				p.redirectDest()
			case p.got('<'):
				p.want(WORD)
			case p.got(';'):
				break args
			case p.got('\n'):
				break args
			default:
				p.errAfterStr("command")
			}
		}
	case p.got('{'):
		for p.tok != EOF && !p.peek('}') {
			if p.got('\n') {
				continue
			}
			p.command()
		}
		p.want('}')
		switch {
		case p.got('&'):
			if p.got('&') {
				p.command()
			}
		case p.got('|'):
			p.got('|')
			p.command()
		case p.got('>'):
			p.redirectDest()
		case p.got('<'):
			p.want(WORD)
		case p.got(';'):
		case p.got('\n'):
		default:
			p.errAfterStr("block")
		}
	default:
		p.errWantedStr("command")
	}
}

func (p *parser) redirectDest() {
	switch {
	case p.got('&'):
		p.want(WORD)
		if !num.MatchString(p.val) {
			p.col -= utf8.RuneCountInString(p.val)
			p.col++
			p.lineErr("invalid fd %q", p.val)
		}
		return
	case p.got('>'):
	}
	p.want(WORD)
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"fmt"
	"strings"
)

type templateParams map[string]interface{}

type fileParams struct {
	filename string
	params   templateParams
}

type array struct {
	paramName    string
	paramValues  []string
	continuation verbatim
}

type verbatim struct {
	text string
	next *array
}

func (self *verbatim) subst(paramName string, paramValue interface{}) {
	textValue, ok := paramValue.(string)
	if ok {
		self.text = strings.Replace(self.text, "{"+paramName+"}", textValue, -1)
	} else {
		arrayValue, ok := paramValue.([]string)
		if ok {
			pos := strings.Index(self.text, "{"+paramName+"}")
			if pos >= 0 {
				self.next = &array{paramName, arrayValue,
					verbatim{self.text[len(paramName)+2:], self.next}}
				self.text = self.text[:pos]
			}
		} else {
			self.text = strings.Replace(self.text, "{"+paramName+"}",
				fmt.Sprint(paramValue), -1)
		}
	}
}

func expandPathnameTemplate(pathname string, params templateParams) []fileParams {
	root := &verbatim{pathname, nil}

	for paramName, paramValue := range params {
		root.subst(paramName, paramValue)

		for node := root.next; node != nil; node = node.continuation.next {
			node.continuation.subst(paramName, paramValue)
		}

		fmt.Println(paramName, paramValue)
		strings.Index(pathname, "{"+paramName+"}")
	}

	fmt.Println(root)

	return []fileParams{{pathname, params}}
}
<commit_msg>Advance pathname template implementation<commit_after>// Copyright (C) 2017 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.

package main

import (
	"fmt"
	"strings"
)

type templateParams map[string]interface{}

type fileParams struct {
	filename string
	params   templateParams
}

type array struct {
	paramName    string
	paramValues  []string
	continuation verbatim
}

type verbatim struct {
	text string
	next *array
}

// Subst updates the 'verbatim' receiver by replacing all instances
// of paramName surrounded by braces with paramValue, which can be
// either a string or a slice of strings. In the latter case, the
// text in the receiver structure gets truncated by the substitution
// and the receiver structure gets extended by a new array structure.
func (v *verbatim) subst(paramName string, paramValue interface{}) {
	textValue, ok := paramValue.(string)

	if ok {
		v.text = strings.Replace(v.text, "{"+paramName+"}", textValue, -1)
	} else {
		arrayValue, ok := paramValue.([]string)

		if ok {
			pos := strings.Index(v.text, "{"+paramName+"}")

			if pos >= 0 {
				v.next = &array{paramName, arrayValue,
					verbatim{v.text[len(paramName)+2:], v.next}}
				v.text = v.text[:pos]
			}
		} else {
			v.text = strings.Replace(v.text, "{"+paramName+"}",
				fmt.Sprint(paramValue), -1)
		}
	}
}

// ExpandPathnameTemplate takes a pathname template and subsitutes
// template parameter names with their values. Parameter values can be
// either strings or slices of strings. Each template value that is a
// slice of strings multiplies the number of output strings by the number
// of strings in the slice.
func expandPathnameTemplate(pathname string, params templateParams) []fileParams {
	root := verbatim{pathname, nil}

	for paramName, paramValue := range params {
		root.subst(paramName, paramValue)

		for node := root.next; node != nil; node = node.continuation.next {
			node.continuation.subst(paramName, paramValue)
		}

		fmt.Println(paramName, paramValue)
		strings.Index(pathname, "{"+paramName+"}")
	}

	if root.next == nil {
		return []fileParams{{root.text, params}}
	}

	fmt.Println(root)

	return []fileParams{{pathname, params}}
}
<|endoftext|>


<commit_before>package kkok

import (
	"encoding/json"
	"fmt"
	"os/exec"
	"regexp"
	"time"

	"github.com/cybozu-go/log"
	"github.com/pkg/errors"
	"github.com/robertkrimen/otto"
)

var (
	reFilterID = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
)

// Filter is the interface that filter plugins must implement.
//
// Methods other than Params and Process are implemented in BaseFilter
// so that a filter implementation can embed BaseFilter to provide them.
type Filter interface {

	// Params returns PluginParams that can be used to construct
	// this filter.
	Params() PluginParams

	// Process applies the filter for all alerts and returns
	// the filtered alerts.
	Process(alerts []*Alert) ([]*Alert, error)

	// ID returns the ID of the filter.
	ID() string

	// Label returns the string label of the filter.
	Label() string

	// Dynamic returns true if the filter is added dynamically.
	Dynamic() bool

	// SetDynamic sets the filter dynamic.
	// This should be used privately inside kkok.
	SetDynamic()

	// Disabled returns true iff the filter is disabled.
	Disabled() bool

	// Enable enables the filter if e == true, otherwise disables the filter.
	Enable(e bool)

	// Expired returns true iff the filter has already been expired.
	Expired() bool
}

// FilterConstructor is a function signature for filter construction.
//
// id should be passed to BaseFilter.Init.
// params may be used to initialize the filter.
type FilterConstructor func(id string, params map[string]interface{}) (Filter, error)

var filterTypes = make(map[string]FilterConstructor)

// RegisterFilter registers a construction function of a Filter type.
func RegisterFilter(typ string, ctor FilterConstructor) {
	filterTypes[typ] = ctor
}

// NewFilter constructs a Filter.
func NewFilter(typ string, params map[string]interface{}) (Filter, error) {
	ctor, ok := filterTypes[typ]
	if !ok {
		return nil, errors.New("no such filter type: " + typ)
	}
	i, ok := params["id"]
	if !ok {
		return nil, errors.New("no filter id")
	}
	id, ok := i.(string)
	if !ok {
		return nil, errors.New("filter id must be a string")
	}
	delete(params, "id")
	return ctor(id, params)
}

// BaseFilter provides the common implementation for all filters.
//
// Filter plugins MUST embed BaseFilter anonymously.
type BaseFilter struct {
	id        string
	label     string
	dynamic   bool
	disabled  bool
	all       bool
	origIf    interface{}
	ifScript  *otto.Script
	ifCommand *exec.Cmd
	expire    time.Time
}

// Init initializes BaseFilter with parameters.
//
// Significant keys in params are:
//
//    label    string  Arbitrary string label of the filter.
//    disabled bool    If true, this filter is disabled.
//    expire   string  RFC3339 format time at which this filter expires.
//    all      bool    If true, the filter process all alerts at once.
//    if       string|array of strings
//                     string must be a JavaScript expression.
//                     array of strings must be a command and arguments
//                     to be invoked.
func (b *BaseFilter) Init(id string, params map[string]interface{}) error {
	if !reFilterID.MatchString(id) {
		return errors.New("invalid filter id: " + id)
	}

	b.id = id

	if i, ok := params["label"]; ok {
		label, ok := i.(string)
		if !ok {
			return errors.New("label must be a string")
		}
		b.label = label
	}

	if i, ok := params["disabled"]; ok {
		disabled, ok := i.(bool)
		if !ok {
			return errors.New("disabled must be a boolean")
		}
		b.disabled = disabled
	}

	if i, ok := params["expire"]; ok {
		s, ok := i.(string)
		if !ok {
			return errors.New("expire must be a string for RFC3339 time format")
		}
		err := b.expire.UnmarshalText([]byte(s))
		if err != nil {
			return errors.Wrap(err, "expire")
		}
	}

	if i, ok := params["all"]; ok {
		all, ok := i.(bool)
		if !ok {
			return errors.New("all must be a boolean")
		}
		b.all = all
	}

	if _, ok := params["if"]; !ok {
		return nil
	}

	b.origIf = params["if"]

	switch i := b.origIf.(type) {
	case string:
		script, err := CompileJS(i)
		if err != nil {
			return errors.Wrap(err, "expr: "+i)
		}
		b.ifScript = script

	case []interface{}:
		command, err := parseCommand(i)
		if err != nil {
			return err
		}
		b.ifCommand = command

	default:
		return errors.New("if must be string or []string")
	}

	return nil
}

func parseCommand(a []interface{}) (*exec.Cmd, error) {
	if len(a) == 0 {
		return nil, errors.New("empty command")
	}

	b := make([]string, len(a))
	for i, e := range a {
		s, ok := e.(string)
		if !ok {
			return nil, fmt.Errorf("not a string: %#v", e)
		}
		b[i] = s
	}

	return exec.Command(b[0], b[1:]...), nil
}

// AddParams adds basic parameters for Filter.Params method.
// The map must not be nil.
func (b *BaseFilter) AddParams(m map[string]interface{}) {
	if len(b.label) > 0 {
		m["label"] = b.label
	}
	if b.disabled {
		m["disabled"] = b.disabled
	}
	if b.all {
		m["all"] = b.all
	}
	if b.origIf != nil {
		m["if"] = b.origIf
	}
	if b.dynamic && (!b.expire.IsZero()) {
		m["expire"] = b.expire
	}
}

// ID returns the ID of the filter.
func (b *BaseFilter) ID() string {
	return b.id
}

// Label returns the string label of the filter.
func (b *BaseFilter) Label() string {
	return b.label
}

// Dynamic returns true if the filter is added dynamically.
func (b *BaseFilter) Dynamic() bool {
	return b.dynamic
}

// SetDynamic sets the filter dynamic.
// This should be used privately inside kkok.
func (b *BaseFilter) SetDynamic() {
	b.dynamic = true
}

// Disabled returns true iff the filter is disabled.
func (b *BaseFilter) Disabled() bool {
	return b.disabled
}

// Enable enables the filter if e == true, otherwise disables the filter.
func (b *BaseFilter) Enable(e bool) {
	b.disabled = !e
}

// All returns true iff the filter processes all alerts at once.
// If false, the filter processes alerts one by one.
func (b *BaseFilter) All() bool {
	return b.all
}

// Expired returns true iff the filter has already been expired.
func (b *BaseFilter) Expired() bool {
	if !b.dynamic {
		return false
	}

	if b.expire.IsZero() {
		return false
	}

	return time.Now().After(b.expire)
}

// EvalAlert evaluates an alert with "if" condition.
func (b *BaseFilter) EvalAlert(a *Alert) (bool, error) {
	if b.ifScript != nil {
		value, err := a.Eval(b.ifScript)
		if err != nil {
			return false, err
		}
		if !value.IsBoolean() {
			log.Warn("kkok: not a boolean expression", map[string]interface{}{
				"expression": b.origIf,
			})
		}
		bvalue, _ := value.ToBoolean()
		return bvalue, nil
	}

	if b.ifCommand != nil {
		command := &exec.Cmd{
			Path: b.ifCommand.Path,
			Args: b.ifCommand.Args,
		}
		w, err := command.StdinPipe()
		if err != nil {
			return false, err
		}
		err = command.Start()
		if err != nil {
			return false, err
		}

		enc := json.NewEncoder(w)
		err = enc.Encode(a)
		w.Close()
		if err != nil {
			command.Wait()
			return false, err
		}

		return command.Wait() == nil, nil
	}

	return true, nil
}

// EvalAllAlerts evaluates all alerts with "if" condition.
func (b *BaseFilter) EvalAllAlerts(alerts []*Alert) (bool, error) {
	if b.ifScript != nil {
		vm := baseOtto.Copy()
		err := vm.Set("alerts", alerts)
		if err != nil {
			return false, err
		}
		value, err := vm.Run(b.ifScript)
		if err != nil {
			return false, err
		}
		bvalue, _ := value.ToBoolean()
		return bvalue, nil
	}

	if b.ifCommand != nil {
		command := &exec.Cmd{
			Path: b.ifCommand.Path,
			Args: b.ifCommand.Args,
		}
		return command.Run() == nil, nil
	}

	return true, nil
}
<commit_msg>[kkok] enhance log.<commit_after>package kkok

import (
	"encoding/json"
	"fmt"
	"os/exec"
	"regexp"
	"time"

	"github.com/cybozu-go/log"
	"github.com/pkg/errors"
	"github.com/robertkrimen/otto"
)

var (
	reFilterID = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
)

// Filter is the interface that filter plugins must implement.
//
// Methods other than Params and Process are implemented in BaseFilter
// so that a filter implementation can embed BaseFilter to provide them.
type Filter interface {

	// Params returns PluginParams that can be used to construct
	// this filter.
	Params() PluginParams

	// Process applies the filter for all alerts and returns
	// the filtered alerts.
	Process(alerts []*Alert) ([]*Alert, error)

	// ID returns the ID of the filter.
	ID() string

	// Label returns the string label of the filter.
	Label() string

	// Dynamic returns true if the filter is added dynamically.
	Dynamic() bool

	// SetDynamic sets the filter dynamic.
	// This should be used privately inside kkok.
	SetDynamic()

	// Disabled returns true iff the filter is disabled.
	Disabled() bool

	// Enable enables the filter if e == true, otherwise disables the filter.
	Enable(e bool)

	// Expired returns true iff the filter has already been expired.
	Expired() bool
}

// FilterConstructor is a function signature for filter construction.
//
// id should be passed to BaseFilter.Init.
// params may be used to initialize the filter.
type FilterConstructor func(id string, params map[string]interface{}) (Filter, error)

var filterTypes = make(map[string]FilterConstructor)

// RegisterFilter registers a construction function of a Filter type.
func RegisterFilter(typ string, ctor FilterConstructor) {
	filterTypes[typ] = ctor
}

// NewFilter constructs a Filter.
func NewFilter(typ string, params map[string]interface{}) (Filter, error) {
	ctor, ok := filterTypes[typ]
	if !ok {
		return nil, errors.New("no such filter type: " + typ)
	}
	i, ok := params["id"]
	if !ok {
		return nil, errors.New("no filter id")
	}
	id, ok := i.(string)
	if !ok {
		return nil, errors.New("filter id must be a string")
	}
	delete(params, "id")
	return ctor(id, params)
}

// BaseFilter provides the common implementation for all filters.
//
// Filter plugins MUST embed BaseFilter anonymously.
type BaseFilter struct {
	id        string
	label     string
	dynamic   bool
	disabled  bool
	all       bool
	origIf    interface{}
	ifScript  *otto.Script
	ifCommand *exec.Cmd
	expire    time.Time
}

// Init initializes BaseFilter with parameters.
//
// Significant keys in params are:
//
//    label    string  Arbitrary string label of the filter.
//    disabled bool    If true, this filter is disabled.
//    expire   string  RFC3339 format time at which this filter expires.
//    all      bool    If true, the filter process all alerts at once.
//    if       string|array of strings
//                     string must be a JavaScript expression.
//                     array of strings must be a command and arguments
//                     to be invoked.
func (b *BaseFilter) Init(id string, params map[string]interface{}) error {
	if !reFilterID.MatchString(id) {
		return errors.New("invalid filter id: " + id)
	}

	b.id = id

	if i, ok := params["label"]; ok {
		label, ok := i.(string)
		if !ok {
			return errors.New("label must be a string")
		}
		b.label = label
	}

	if i, ok := params["disabled"]; ok {
		disabled, ok := i.(bool)
		if !ok {
			return errors.New("disabled must be a boolean")
		}
		b.disabled = disabled
	}

	if i, ok := params["expire"]; ok {
		s, ok := i.(string)
		if !ok {
			return errors.New("expire must be a string for RFC3339 time format")
		}
		err := b.expire.UnmarshalText([]byte(s))
		if err != nil {
			return errors.Wrap(err, "expire")
		}
	}

	if i, ok := params["all"]; ok {
		all, ok := i.(bool)
		if !ok {
			return errors.New("all must be a boolean")
		}
		b.all = all
	}

	if _, ok := params["if"]; !ok {
		return nil
	}

	b.origIf = params["if"]

	switch i := b.origIf.(type) {
	case string:
		script, err := CompileJS(i)
		if err != nil {
			return errors.Wrap(err, "expr: "+i)
		}
		b.ifScript = script

	case []interface{}:
		command, err := parseCommand(i)
		if err != nil {
			return err
		}
		b.ifCommand = command

	default:
		return errors.New("if must be string or []string")
	}

	return nil
}

func parseCommand(a []interface{}) (*exec.Cmd, error) {
	if len(a) == 0 {
		return nil, errors.New("empty command")
	}

	b := make([]string, len(a))
	for i, e := range a {
		s, ok := e.(string)
		if !ok {
			return nil, fmt.Errorf("not a string: %#v", e)
		}
		b[i] = s
	}

	return exec.Command(b[0], b[1:]...), nil
}

// AddParams adds basic parameters for Filter.Params method.
// The map must not be nil.
func (b *BaseFilter) AddParams(m map[string]interface{}) {
	if len(b.label) > 0 {
		m["label"] = b.label
	}
	if b.disabled {
		m["disabled"] = b.disabled
	}
	if b.all {
		m["all"] = b.all
	}
	if b.origIf != nil {
		m["if"] = b.origIf
	}
	if b.dynamic && (!b.expire.IsZero()) {
		m["expire"] = b.expire
	}
}

// ID returns the ID of the filter.
func (b *BaseFilter) ID() string {
	return b.id
}

// Label returns the string label of the filter.
func (b *BaseFilter) Label() string {
	return b.label
}

// Dynamic returns true if the filter is added dynamically.
func (b *BaseFilter) Dynamic() bool {
	return b.dynamic
}

// SetDynamic sets the filter dynamic.
// This should be used privately inside kkok.
func (b *BaseFilter) SetDynamic() {
	b.dynamic = true
}

// Disabled returns true iff the filter is disabled.
func (b *BaseFilter) Disabled() bool {
	return b.disabled
}

// Enable enables the filter if e == true, otherwise disables the filter.
func (b *BaseFilter) Enable(e bool) {
	b.disabled = !e
}

// All returns true iff the filter processes all alerts at once.
// If false, the filter processes alerts one by one.
func (b *BaseFilter) All() bool {
	return b.all
}

// Expired returns true iff the filter has already been expired.
func (b *BaseFilter) Expired() bool {
	if !b.dynamic {
		return false
	}

	if b.expire.IsZero() {
		return false
	}

	return time.Now().After(b.expire)
}

// EvalAlert evaluates an alert with "if" condition.
func (b *BaseFilter) EvalAlert(a *Alert) (bool, error) {
	if b.ifScript != nil {
		value, err := a.Eval(b.ifScript)
		if err != nil {
			return false, err
		}
		if !value.IsBoolean() {
			log.Warn("kkok: not a boolean expression", map[string]interface{}{
				"id":         b.id,
				"expression": b.origIf,
			})
		}
		bvalue, _ := value.ToBoolean()
		return bvalue, nil
	}

	if b.ifCommand != nil {
		command := &exec.Cmd{
			Path: b.ifCommand.Path,
			Args: b.ifCommand.Args,
		}
		w, err := command.StdinPipe()
		if err != nil {
			return false, err
		}
		err = command.Start()
		if err != nil {
			return false, err
		}

		enc := json.NewEncoder(w)
		err = enc.Encode(a)
		w.Close()
		if err != nil {
			command.Wait()
			return false, err
		}

		return command.Wait() == nil, nil
	}

	return true, nil
}

// EvalAllAlerts evaluates all alerts with "if" condition.
func (b *BaseFilter) EvalAllAlerts(alerts []*Alert) (bool, error) {
	if b.ifScript != nil {
		vm := baseOtto.Copy()
		err := vm.Set("alerts", alerts)
		if err != nil {
			return false, err
		}
		value, err := vm.Run(b.ifScript)
		if err != nil {
			return false, err
		}
		bvalue, _ := value.ToBoolean()
		return bvalue, nil
	}

	if b.ifCommand != nil {
		command := &exec.Cmd{
			Path: b.ifCommand.Path,
			Args: b.ifCommand.Args,
		}
		return command.Run() == nil, nil
	}

	return true, nil
}
<|endoftext|>


<commit_before>package flickr

import (
	"os"
	"fmt"
	"http"
	"sort"
	"io/ioutil"
	"crypto/md5"
)

const (
	endpoint = "http://api.flickr.com/services/rest/?"
)

type Request struct {
	ApiKey string
	Method string
	Args   map[string]string
}

// So we can return custom errors
type Error string

func (e Error) String() string {
	return string(e)
}

func (request *Request) Sign(secret string) {
	args := request.Args

	sorted_keys := make([]string, len(args)+2)

	args["api_key"] = request.ApiKey
	args["method"] = request.Method

	// Sort array keys
	i := 0
	for k, _ := range args {
		sorted_keys[i] = k
		i++
	}
	sort.SortStrings(sorted_keys)

	// Build out ordered key-value string prefixed by secret
	s := secret
	for _, key := range sorted_keys {
		s += fmt.Sprintf("%s%s", key, args[key])
	}

	// Since we're only adding two keys, it's easier
	// and more space-efficient to just delete them
	// them copy the whole map
	args["api_key"] = "", false
	args["method"] = "", false

	// Have the full string, now hash
	hash := md5.New()
	hash.Write([]byte(s))

	// Add api_sig as one of the args
	args["api_sig"] = fmt.Sprintf("%x", hash.Sum())
}

func (request *Request) Execute() (response string, ret os.Error) {
	args := request.Args

	if request.ApiKey == "" || request.Method == "" {
		return "", Error("Need both API key and method")
	}

	args["api_key"] = request.ApiKey
	args["method"] = request.Method

	s := endpoint
	i := 0
	for k, v := range args {
		if i != 0 {
			s += "&"
		}
		i++
		s += fmt.Sprintf("%s=%s", k, v)
	}

	res, _, err := http.Get(s)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	body, _ := ioutil.ReadAll(res.Body)
	return string(body), nil
}
<commit_msg>Make sure to escape values<commit_after>package flickr

import (
	"os"
	"fmt"
	"http"
	"sort"
	"io/ioutil"
	"crypto/md5"
)

const (
	endpoint = "http://api.flickr.com/services/rest/?"
)

type Request struct {
	ApiKey string
	Method string
	Args   map[string]string
}

// So we can return custom errors
type Error string

func (e Error) String() string {
	return string(e)
}

func (request *Request) Sign(secret string) {
	args := request.Args

	sorted_keys := make([]string, len(args)+2)

	args["api_key"] = request.ApiKey
	args["method"] = request.Method

	// Sort array keys
	i := 0
	for k, _ := range args {
		sorted_keys[i] = k
		i++
	}
	sort.SortStrings(sorted_keys)

	// Build out ordered key-value string prefixed by secret
	s := secret
	for _, key := range sorted_keys {
		s += fmt.Sprintf("%s%s", key, args[key])
	}

	// Since we're only adding two keys, it's easier
	// and more space-efficient to just delete them
	// them copy the whole map
	args["api_key"] = "", false
	args["method"] = "", false

	// Have the full string, now hash
	hash := md5.New()
	hash.Write([]byte(s))

	// Add api_sig as one of the args
	args["api_sig"] = fmt.Sprintf("%x", hash.Sum())
}

func (request *Request) Execute() (response string, ret os.Error) {
	args := request.Args

	if request.ApiKey == "" || request.Method == "" {
		return "", Error("Need both API key and method")
	}

	args["api_key"] = request.ApiKey
	args["method"] = request.Method

	s := endpoint
	i := 0
	for k, v := range args {
		if i != 0 {
			s += "&"
		}
		i++
		s += fmt.Sprintf("%s=%s", k, http.URLEscape(v))
	}

	res, _, err := http.Get(s)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	body, _ := ioutil.ReadAll(res.Body)
	return string(body), nil
}
<|endoftext|>