<commit_before>package nginx

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path"
	"text/template"

	"github.com/golang/glog"
)

const dhparamFilename = "dhparam.pem"

// NginxController Updates NGINX configuration, starts and reloads NGINX
type NginxController struct {
	nginxConfdPath          string
	nginxCertsPath          string
	local                   bool
	healthStatus            bool
	nginxConfTemplatePath   string
	nginxIngressTempatePath string
}

// IngressNginxConfig describes an NGINX configuration
type IngressNginxConfig struct {
	Upstreams []Upstream
	Servers   []Server
}

// Upstream describes an NGINX upstream
type Upstream struct {
	Name            string
	UpstreamServers []UpstreamServer
	StickyCookie    string
}

// UpstreamServer describes a server in an NGINX upstream
type UpstreamServer struct {
	Address string
	Port    string
}

// Server describes an NGINX server
type Server struct {
	ServerSnippets        []string
	Name                  string
	ServerTokens          string
	Locations             []Location
	SSL                   bool
	SSLCertificate        string
	SSLCertificateKey     string
	StatusZone            string
	HTTP2                 bool
	RedirectToHTTPS       bool
	ProxyProtocol         bool
	HSTS                  bool
	HSTSMaxAge            int64
	HSTSIncludeSubdomains bool
	ProxyHideHeaders      []string
	ProxyPassHeaders      []string

	// http://nginx.org/en/docs/http/ngx_http_realip_module.html
	RealIPHeader    string
	SetRealIPFrom   []string
	RealIPRecursive bool
}

// Location describes an NGINX location
type Location struct {
	LocationSnippets     []string
	Path                 string
	Upstream             Upstream
	ProxyConnectTimeout  string
	ProxyReadTimeout     string
	ClientMaxBodySize    string
	Websocket            bool
	Rewrite              string
	SSL                  bool
	ProxyBuffering       bool
	ProxyBuffers         string
	ProxyBufferSize      string
	ProxyMaxTempFileSize string
}

// NginxMainConfig describe the main NGINX configuration file
type NginxMainConfig struct {
	ServerNamesHashBucketSize string
	ServerNamesHashMaxSize    string
	LogFormat                 string
	HealthStatus              bool
	HTTPSnippets              []string
	// http://nginx.org/en/docs/http/ngx_http_ssl_module.html
	SSLProtocols           string
	SSLPreferServerCiphers bool
	SSLCiphers             string
	SSLDHParam             string
}

// NewUpstreamWithDefaultServer creates an upstream with the default server.
// proxy_pass to an upstream with the default server returns 502.
// We use it for services that have no endpoints
func NewUpstreamWithDefaultServer(name string) Upstream {
	return Upstream{
		Name:            name,
		UpstreamServers: []UpstreamServer{UpstreamServer{Address: "127.0.0.1", Port: "8181"}},
	}
}

// NewNginxController creates a NGINX controller
func NewNginxController(nginxConfPath string, local bool, healthStatus bool, nginxConfTemplatePath string, nginxIngressTemplatePath string) (*NginxController, error) {
	ngxc := NginxController{
		nginxConfdPath:          path.Join(nginxConfPath, "conf.d"),
		nginxCertsPath:          path.Join(nginxConfPath, "ssl"),
		local:                   local,
		healthStatus:            healthStatus,
		nginxConfTemplatePath:   nginxConfTemplatePath,
		nginxIngressTempatePath: nginxIngressTemplatePath,
	}

	if !local {
		createDir(ngxc.nginxCertsPath)
	}

	cfg := &NginxMainConfig{ServerNamesHashMaxSize: NewDefaultConfig().MainServerNamesHashMaxSize}
	ngxc.UpdateMainConfigFile(cfg)

	return &ngxc, nil
}

// DeleteIngress deletes the configuration file, which corresponds for the
// specified ingress from NGINX conf directory
func (nginx *NginxController) DeleteIngress(name string) {
	filename := nginx.getIngressNginxConfigFileName(name)
	glog.V(3).Infof("deleting %v", filename)

	if !nginx.local {
		if err := os.Remove(filename); err != nil {
			glog.Warningf("Failed to delete %v: %v", filename, err)
		}
	}
}

// AddOrUpdateIngress creates or updates a file with
// the specified configuration for the specified ingress
func (nginx *NginxController) AddOrUpdateIngress(name string, config IngressNginxConfig) {
	glog.V(3).Infof("Updating NGINX configuration")
	filename := nginx.getIngressNginxConfigFileName(name)
	nginx.templateIt(config, filename)
}

// AddOrUpdateDHParam creates the servers dhparam.pem file
func (nginx *NginxController) AddOrUpdateDHParam(dhparam string) (string, error) {
	fileName := nginx.nginxCertsPath + "/" + dhparamFilename
	if !nginx.local {
		pem, err := os.Create(fileName)
		if err != nil {
			return fileName, fmt.Errorf("Couldn't create file %v: %v", fileName, err)
		}
		defer pem.Close()

		_, err = pem.WriteString(dhparam)
		if err != nil {
			return fileName, fmt.Errorf("Couldn't write to pem file %v: %v", fileName, err)
		}
	}
	return fileName, nil
}

// AddOrUpdateCertAndKey creates a .pem file wth the cert and the key with the
// specified name
func (nginx *NginxController) AddOrUpdateCertAndKey(name string, cert string, key string) string {
	pemFileName := nginx.nginxCertsPath + "/" + name + ".pem"

	if !nginx.local {
		pem, err := os.Create(pemFileName)
		if err != nil {
			glog.Fatalf("Couldn't create pem file %v: %v", pemFileName, err)
		}
		defer pem.Close()

		_, err = pem.WriteString(key)
		if err != nil {
			glog.Fatalf("Couldn't write to pem file %v: %v", pemFileName, err)
		}

		_, err = pem.WriteString("\n")
		if err != nil {
			glog.Fatalf("Couldn't write to pem file %v: %v", pemFileName, err)
		}

		_, err = pem.WriteString(cert)
		if err != nil {
			glog.Fatalf("Couldn't write to pem file %v: %v", pemFileName, err)
		}
	}

	return pemFileName
}

func (nginx *NginxController) getIngressNginxConfigFileName(name string) string {
	return path.Join(nginx.nginxConfdPath, name+".conf")
}

func (nginx *NginxController) templateIt(config IngressNginxConfig, filename string) {
	tmpl, err := template.New(nginx.nginxIngressTempatePath).ParseFiles(nginx.nginxIngressTempatePath)
	if err != nil {
		glog.Fatalf("Failed to parse template file: %v", err)
	}

	glog.V(3).Infof("Writing NGINX conf to %v", filename)

	if glog.V(3) {
		tmpl.Execute(os.Stdout, config)
	}

	if !nginx.local {
		w, err := os.Create(filename)
		if err != nil {
			glog.Fatalf("Failed to open %v: %v", filename, err)
		}
		defer w.Close()

		if err := tmpl.Execute(w, config); err != nil {
			glog.Fatalf("Failed to write template %v", err)
		}
	} else {
		// print conf to stdout here
	}

	glog.V(3).Infof("NGINX configuration file had been updated")
}

// Reload reloads NGINX
func (nginx *NginxController) Reload() error {
	if !nginx.local {
		if err := shellOut("nginx -t"); err != nil {
			return fmt.Errorf("Invalid nginx configuration detected, not reloading: %s", err)
		}
		if err := shellOut("nginx -s reload"); err != nil {
			return fmt.Errorf("Reloading NGINX failed: %s", err)
		}
	} else {
		glog.V(3).Info("Reloading nginx")
	}
	return nil
}

// Start starts NGINX
func (nginx *NginxController) Start() {
	if !nginx.local {
		if err := shellOut("nginx"); err != nil {
			glog.Fatalf("Failed to start nginx: %v", err)
		}
	} else {
		glog.V(3).Info("Starting nginx")
	}
}

func createDir(path string) {
	if err := os.Mkdir(path, os.ModeDir); err != nil {
		glog.Fatalf("Couldn't create directory %v: %v", path, err)
	}
}

func shellOut(cmd string) (err error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	glog.V(3).Infof("executing %s", cmd)

	command := exec.Command("sh", "-c", cmd)
	command.Stdout = &stdout
	command.Stderr = &stderr

	err = command.Start()
	if err != nil {
		return fmt.Errorf("Failed to execute %v, err: %v", cmd, err)
	}

	err = command.Wait()
	if err != nil {
		return fmt.Errorf("Command %v stdout: %q\nstderr: %q\nfinished with error: %v", cmd,
			stdout.String(), stderr.String(), err)
	}
	return nil
}

// UpdateMainConfigFile update the main NGINX configuration file
func (nginx *NginxController) UpdateMainConfigFile(cfg *NginxMainConfig) {
	cfg.HealthStatus = nginx.healthStatus

	tmpl, err := template.New(nginx.nginxConfTemplatePath).ParseFiles(nginx.nginxConfTemplatePath)
	if err != nil {
		glog.Fatalf("Failed to parse the main config template file: %v", err)
	}

	filename := "/etc/nginx/nginx.conf"
	glog.V(3).Infof("Writing NGINX conf to %v", filename)

	if glog.V(3) {
		tmpl.Execute(os.Stdout, cfg)
	}

	if !nginx.local {
		w, err := os.Create(filename)
		if err != nil {
			glog.Fatalf("Failed to open %v: %v", filename, err)
		}
		defer w.Close()

		if err := tmpl.Execute(w, cfg); err != nil {
			glog.Fatalf("Failed to write template %v", err)
		}
	}

	glog.V(3).Infof("The main NGINX configuration file had been updated")
}
<commit_msg>Write a pem file atomically<commit_after>package nginx

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"text/template"

	"github.com/golang/glog"
)

const dhparamFilename = "dhparam.pem"

// NginxController Updates NGINX configuration, starts and reloads NGINX
type NginxController struct {
	nginxConfdPath          string
	nginxCertsPath          string
	local                   bool
	healthStatus            bool
	nginxConfTemplatePath   string
	nginxIngressTempatePath string
}

// IngressNginxConfig describes an NGINX configuration
type IngressNginxConfig struct {
	Upstreams []Upstream
	Servers   []Server
}

// Upstream describes an NGINX upstream
type Upstream struct {
	Name            string
	UpstreamServers []UpstreamServer
	StickyCookie    string
}

// UpstreamServer describes a server in an NGINX upstream
type UpstreamServer struct {
	Address string
	Port    string
}

// Server describes an NGINX server
type Server struct {
	ServerSnippets        []string
	Name                  string
	ServerTokens          string
	Locations             []Location
	SSL                   bool
	SSLCertificate        string
	SSLCertificateKey     string
	StatusZone            string
	HTTP2                 bool
	RedirectToHTTPS       bool
	ProxyProtocol         bool
	HSTS                  bool
	HSTSMaxAge            int64
	HSTSIncludeSubdomains bool
	ProxyHideHeaders      []string
	ProxyPassHeaders      []string

	// http://nginx.org/en/docs/http/ngx_http_realip_module.html
	RealIPHeader    string
	SetRealIPFrom   []string
	RealIPRecursive bool
}

// Location describes an NGINX location
type Location struct {
	LocationSnippets     []string
	Path                 string
	Upstream             Upstream
	ProxyConnectTimeout  string
	ProxyReadTimeout     string
	ClientMaxBodySize    string
	Websocket            bool
	Rewrite              string
	SSL                  bool
	ProxyBuffering       bool
	ProxyBuffers         string
	ProxyBufferSize      string
	ProxyMaxTempFileSize string
}

// NginxMainConfig describe the main NGINX configuration file
type NginxMainConfig struct {
	ServerNamesHashBucketSize string
	ServerNamesHashMaxSize    string
	LogFormat                 string
	HealthStatus              bool
	HTTPSnippets              []string
	// http://nginx.org/en/docs/http/ngx_http_ssl_module.html
	SSLProtocols           string
	SSLPreferServerCiphers bool
	SSLCiphers             string
	SSLDHParam             string
}

// NewUpstreamWithDefaultServer creates an upstream with the default server.
// proxy_pass to an upstream with the default server returns 502.
// We use it for services that have no endpoints
func NewUpstreamWithDefaultServer(name string) Upstream {
	return Upstream{
		Name:            name,
		UpstreamServers: []UpstreamServer{UpstreamServer{Address: "127.0.0.1", Port: "8181"}},
	}
}

// NewNginxController creates a NGINX controller
func NewNginxController(nginxConfPath string, local bool, healthStatus bool, nginxConfTemplatePath string, nginxIngressTemplatePath string) (*NginxController, error) {
	ngxc := NginxController{
		nginxConfdPath:          path.Join(nginxConfPath, "conf.d"),
		nginxCertsPath:          path.Join(nginxConfPath, "ssl"),
		local:                   local,
		healthStatus:            healthStatus,
		nginxConfTemplatePath:   nginxConfTemplatePath,
		nginxIngressTempatePath: nginxIngressTemplatePath,
	}

	if !local {
		createDir(ngxc.nginxCertsPath)
	}

	cfg := &NginxMainConfig{ServerNamesHashMaxSize: NewDefaultConfig().MainServerNamesHashMaxSize}
	ngxc.UpdateMainConfigFile(cfg)

	return &ngxc, nil
}

// DeleteIngress deletes the configuration file, which corresponds for the
// specified ingress from NGINX conf directory
func (nginx *NginxController) DeleteIngress(name string) {
	filename := nginx.getIngressNginxConfigFileName(name)
	glog.V(3).Infof("deleting %v", filename)

	if !nginx.local {
		if err := os.Remove(filename); err != nil {
			glog.Warningf("Failed to delete %v: %v", filename, err)
		}
	}
}

// AddOrUpdateIngress creates or updates a file with
// the specified configuration for the specified ingress
func (nginx *NginxController) AddOrUpdateIngress(name string, config IngressNginxConfig) {
	glog.V(3).Infof("Updating NGINX configuration")
	filename := nginx.getIngressNginxConfigFileName(name)
	nginx.templateIt(config, filename)
}

// AddOrUpdateDHParam creates the servers dhparam.pem file
func (nginx *NginxController) AddOrUpdateDHParam(dhparam string) (string, error) {
	fileName := nginx.nginxCertsPath + "/" + dhparamFilename
	if !nginx.local {
		pem, err := os.Create(fileName)
		if err != nil {
			return fileName, fmt.Errorf("Couldn't create file %v: %v", fileName, err)
		}
		defer pem.Close()

		_, err = pem.WriteString(dhparam)
		if err != nil {
			return fileName, fmt.Errorf("Couldn't write to pem file %v: %v", fileName, err)
		}
	}
	return fileName, nil
}

// AddOrUpdateCertAndKey creates a .pem file wth the cert and the key with the
// specified name
func (nginx *NginxController) AddOrUpdateCertAndKey(name string, cert string, key string) string {
	pemFileName := nginx.nginxCertsPath + "/" + name + ".pem"

	if !nginx.local {
		pem, err := ioutil.TempFile(nginx.nginxCertsPath, name)
		if err != nil {
			glog.Fatalf("Couldn't create a temp file for the pem file %v: %v", name, err)
		}

		_, err = pem.WriteString(key)
		if err != nil {
			glog.Fatalf("Couldn't write to the temp pem file %v: %v", pem.Name(), err)
		}

		_, err = pem.WriteString("\n")
		if err != nil {
			glog.Fatalf("Couldn't write to the temp pem file %v: %v", pem.Name(), err)
		}

		_, err = pem.WriteString(cert)
		if err != nil {
			glog.Fatalf("Couldn't write to the temp pem file %v: %v", pem.Name(), err)
		}

		err = pem.Close()
		if err != nil {
			glog.Fatalf("Couldn't close the temp pem file %v: %v", pem.Name(), err)
		}

		err = os.Rename(pem.Name(), pemFileName)
		if err != nil {
			glog.Fatalf("Fail to rename the temp pem file %v to %v: %v", pem.Name(), pemFileName, err)
		}
	}

	return pemFileName
}

func (nginx *NginxController) getIngressNginxConfigFileName(name string) string {
	return path.Join(nginx.nginxConfdPath, name+".conf")
}

func (nginx *NginxController) templateIt(config IngressNginxConfig, filename string) {
	tmpl, err := template.New(nginx.nginxIngressTempatePath).ParseFiles(nginx.nginxIngressTempatePath)
	if err != nil {
		glog.Fatalf("Failed to parse template file: %v", err)
	}

	glog.V(3).Infof("Writing NGINX conf to %v", filename)

	if glog.V(3) {
		tmpl.Execute(os.Stdout, config)
	}

	if !nginx.local {
		w, err := os.Create(filename)
		if err != nil {
			glog.Fatalf("Failed to open %v: %v", filename, err)
		}
		defer w.Close()

		if err := tmpl.Execute(w, config); err != nil {
			glog.Fatalf("Failed to write template %v", err)
		}
	} else {
		// print conf to stdout here
	}

	glog.V(3).Infof("NGINX configuration file had been updated")
}

// Reload reloads NGINX
func (nginx *NginxController) Reload() error {
	if !nginx.local {
		if err := shellOut("nginx -t"); err != nil {
			return fmt.Errorf("Invalid nginx configuration detected, not reloading: %s", err)
		}
		if err := shellOut("nginx -s reload"); err != nil {
			return fmt.Errorf("Reloading NGINX failed: %s", err)
		}
	} else {
		glog.V(3).Info("Reloading nginx")
	}
	return nil
}

// Start starts NGINX
func (nginx *NginxController) Start() {
	if !nginx.local {
		if err := shellOut("nginx"); err != nil {
			glog.Fatalf("Failed to start nginx: %v", err)
		}
	} else {
		glog.V(3).Info("Starting nginx")
	}
}

func createDir(path string) {
	if err := os.Mkdir(path, os.ModeDir); err != nil {
		glog.Fatalf("Couldn't create directory %v: %v", path, err)
	}
}

func shellOut(cmd string) (err error) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	glog.V(3).Infof("executing %s", cmd)

	command := exec.Command("sh", "-c", cmd)
	command.Stdout = &stdout
	command.Stderr = &stderr

	err = command.Start()
	if err != nil {
		return fmt.Errorf("Failed to execute %v, err: %v", cmd, err)
	}

	err = command.Wait()
	if err != nil {
		return fmt.Errorf("Command %v stdout: %q\nstderr: %q\nfinished with error: %v", cmd,
			stdout.String(), stderr.String(), err)
	}
	return nil
}

// UpdateMainConfigFile update the main NGINX configuration file
func (nginx *NginxController) UpdateMainConfigFile(cfg *NginxMainConfig) {
	cfg.HealthStatus = nginx.healthStatus

	tmpl, err := template.New(nginx.nginxConfTemplatePath).ParseFiles(nginx.nginxConfTemplatePath)
	if err != nil {
		glog.Fatalf("Failed to parse the main config template file: %v", err)
	}

	filename := "/etc/nginx/nginx.conf"
	glog.V(3).Infof("Writing NGINX conf to %v", filename)

	if glog.V(3) {
		tmpl.Execute(os.Stdout, cfg)
	}

	if !nginx.local {
		w, err := os.Create(filename)
		if err != nil {
			glog.Fatalf("Failed to open %v: %v", filename, err)
		}
		defer w.Close()

		if err := tmpl.Execute(w, cfg); err != nil {
			glog.Fatalf("Failed to write template %v", err)
		}
	}

	glog.V(3).Infof("The main NGINX configuration file had been updated")
}
<|endoftext|>


<commit_before>// Package conf parses conf files and offers functions for reading.
// Configuration file format:
// 	#comment
// 	;comment
// 	[section]
// 	value=key
package conf

import (
	"errors"
	"io"
	"os"
)

type Conf struct {
	filename string
	data     map[string]map[string]string
}

const (
	stateStart = iota
	stateMid
	stateComment
	stateSection
	stateKey
	stateValue
	stateError
	stateEOF
)

type lexer struct {
	file *os.File

	bufferSection string
	bufferKey     string
	bufferValue   string
	bufferError   string
	buffer        string

	data map[string]map[string]string
}

// Read returns the value to a given section and key.
// An error will be returned if a key or section does not exist.
func (conf *Conf) Read(section, key string) (string, error) {
	value, exists := conf.data[section][key]
	if !exists {
		return "", errors.New("key or section does not exist")
	}
	return value, nil
}

// Open opens and parses a conf file.
func Open(filename string) (*Conf, error) {
	conf := &Conf{filename: filename}
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	state := stateStart
	lex := &lexer{file, "", "", "", "", "", make(map[string]map[string]string)}
	for {
		switch state {
		case stateStart:
			state = lex.doStart()
		case stateMid:
			state = lex.doMid()
		case stateComment:
			state = lex.doComment()
		case stateSection:
			state = lex.doSection()
		case stateKey:
			state = lex.doKey()
		case stateValue:
			state = lex.doValue()
		case stateError:
			return nil, lex.doError()
		case stateEOF:
			conf.data = lex.data
			return conf, nil
		}
	}

}

func (lex *lexer) doStart() int {
	switch lex.add() {
	case "":
		return stateEOF
	case " ", "	", "\n":
		return stateStart
	case "[":
		lex.flush()
		return stateSection
	case "#", ";":
		return stateComment
	}
	lex.bufferError = "key not in section: " + lex.buffer
	return stateError
}

func (lex *lexer) doMid() int {
	switch lex.look() {
	case "":
		return stateEOF
	case " ", "	", "\n":
		lex.add()
		return stateMid
	case "[":
		lex.add()
		lex.flush()
		return stateSection
	case "#", ";":
		lex.add()
		return stateComment
	}
	lex.flush()
	return stateKey
}

func (lex *lexer) doComment() int {
	switch lex.add() {
	case "":
		return stateEOF
	case "\n":
		if lex.bufferSection == "" {
			return stateStart
		}
		return stateMid
	}
	return stateComment
}

func (lex *lexer) doSection() int {
	switch lex.look() {
	case "\n", "":
		lex.add()
		lex.bufferError = "broken section name: " + lex.buffer
		return stateError
	case "]":
		lex.bufferSection = lex.flush()

		if _, ok := lex.data[lex.bufferSection]; ok {
			lex.bufferError = "duplicate section: " + lex.bufferSection
			return stateError
		}
		lex.data[lex.bufferSection] = make(map[string]string)
		lex.add()
		return stateMid
	}
	lex.add()
	return stateSection
}

func (lex *lexer) doKey() int {
	switch lex.look() {
	case "\n", "":
		lex.add()
		lex.bufferError = "broken key name: " + lex.buffer
		return stateError
	case "=":
		lex.bufferKey = lex.flush()
		if _, ok := lex.data[lex.bufferSection][lex.bufferKey]; ok {
			lex.bufferError = "duplicate key in section: " + lex.bufferKey
			return stateError
		}
		lex.add()
		lex.flush()
		return stateValue
	}
	lex.add()
	return stateKey
}

func (lex *lexer) doValue() int {
	switch lex.look() {
	case "\n", "":
		lex.bufferValue = lex.flush()
		lex.add()
		lex.data[lex.bufferSection][lex.bufferKey] = lex.bufferValue
		return stateMid
	}
	lex.add()
	return stateValue
}

func (lex *lexer) doError() error {
	return errors.New(lex.bufferError)
}

func (lex *lexer) get() string {
	chr := make([]byte, 1)
	_, err := io.ReadFull(lex.file, chr)
	if err != nil {
		return ""
	}
	if string(chr[0]) == "\r" && lex.look() == "\n"  {	//\r\n to \n for easier parsing
		return lex.get()
	}
	return string(chr[0])
}

func (lex *lexer) add() string {
	chr := lex.get()
	lex.buffer += chr
	return chr
}

func (lex *lexer) look() string {
	chr := lex.get()
	lex.file.Seek(-1, 1)
	return chr
}

func (lex *lexer) flush() string {
	save := lex.buffer
	lex.buffer = ""
	return save
}
<commit_msg>better error messages<commit_after>// Package conf parses conf files and offers functions for reading.
// Configuration file format:
// 	#comment
// 	;comment
// 	[section]
// 	value=key
package conf

import (
	"errors"
	"io"
	"os"
)

type Conf struct {
	filename string
	data     map[string]map[string]string
}

const (
	stateStart = iota
	stateMid
	stateComment
	stateSection
	stateKey
	stateValue
	stateError
	stateEOF
)

type lexer struct {
	file *os.File

	bufferSection string
	bufferKey     string
	bufferValue   string
	bufferError   string
	buffer        string

	data map[string]map[string]string
}

// Read returns the value to a given section and key.
// An error will be returned if a key or section does not exist.
func (conf *Conf) Read(section, key string) (string, error) {
	value, exists := conf.data[section][key]
	if !exists {
		return "", errors.New("read: " + conf.filename + " key \"" + key + "\" does not exist in section \"" + section + "\"")
	}
	return value, nil
}

// Open opens and parses a conf file.
func Open(filename string) (*Conf, error) {
	conf := &Conf{filename: filename}
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	state := stateStart
	lex := &lexer{file, "", "", "", "", "", make(map[string]map[string]string)}
	for {
		switch state {
		case stateStart:
			state = lex.doStart()
		case stateMid:
			state = lex.doMid()
		case stateComment:
			state = lex.doComment()
		case stateSection:
			state = lex.doSection()
		case stateKey:
			state = lex.doKey()
		case stateValue:
			state = lex.doValue()
		case stateError:
			return nil, lex.doError()
		case stateEOF:
			conf.data = lex.data
			return conf, nil
		}
	}

}

func (lex *lexer) doStart() int {
	switch lex.add() {
	case "":
		return stateEOF
	case " ", "	", "\n":
		return stateStart
	case "[":
		lex.flush()
		return stateSection
	case "#", ";":
		return stateComment
	}
	lex.bufferError = "key not in section: " + lex.buffer
	return stateError
}

func (lex *lexer) doMid() int {
	switch lex.look() {
	case "":
		return stateEOF
	case " ", "	", "\n":
		lex.add()
		return stateMid
	case "[":
		lex.add()
		lex.flush()
		return stateSection
	case "#", ";":
		lex.add()
		return stateComment
	}
	lex.flush()
	return stateKey
}

func (lex *lexer) doComment() int {
	switch lex.add() {
	case "":
		return stateEOF
	case "\n":
		if lex.bufferSection == "" {
			return stateStart
		}
		return stateMid
	}
	return stateComment
}

func (lex *lexer) doSection() int {
	switch lex.look() {
	case "\n", "":
		lex.add()
		lex.bufferError = "broken section name: " + lex.buffer
		return stateError
	case "]":
		lex.bufferSection = lex.flush()

		if _, ok := lex.data[lex.bufferSection]; ok {
			lex.bufferError = "duplicate section: " + lex.bufferSection
			return stateError
		}
		lex.data[lex.bufferSection] = make(map[string]string)
		lex.add()
		return stateMid
	}
	lex.add()
	return stateSection
}

func (lex *lexer) doKey() int {
	switch lex.look() {
	case "\n", "":
		lex.add()
		lex.bufferError = "broken key name: " + lex.buffer
		return stateError
	case "=":
		lex.bufferKey = lex.flush()
		if _, ok := lex.data[lex.bufferSection][lex.bufferKey]; ok {
			lex.bufferError = "duplicate key in section: " + lex.bufferKey
			return stateError
		}
		lex.add()
		lex.flush()
		return stateValue
	}
	lex.add()
	return stateKey
}

func (lex *lexer) doValue() int {
	switch lex.look() {
	case "\n", "":
		lex.bufferValue = lex.flush()
		lex.add()
		lex.data[lex.bufferSection][lex.bufferKey] = lex.bufferValue
		return stateMid
	}
	lex.add()
	return stateValue
}

func (lex *lexer) doError() error {
	return errors.New(lex.bufferError)
}

func (lex *lexer) get() string {
	chr := make([]byte, 1)
	_, err := io.ReadFull(lex.file, chr)
	if err != nil {
		return ""
	}
	if string(chr[0]) == "\r" && lex.look() == "\n"  {	//\r\n to \n for easier parsing
		return lex.get()
	}
	return string(chr[0])
}

func (lex *lexer) add() string {
	chr := lex.get()
	lex.buffer += chr
	return chr
}

func (lex *lexer) look() string {
	chr := lex.get()
	lex.file.Seek(-1, 1)
	return chr
}

func (lex *lexer) flush() string {
	save := lex.buffer
	lex.buffer = ""
	return save
}
<|endoftext|>


<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 repo

import (
	"path"
	"path/filepath"
	"strings"

	"github.com/codegangsta/martini"

	"github.com/gogits/webdav"

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

func Create(ctx *middleware.Context, form auth.CreateRepoForm) {
	ctx.Data["Title"] = "Create repository"
	ctx.Data["PageIsNewRepo"] = true // For navbar arrow.
	ctx.Data["LanguageIgns"] = models.LanguageIgns
	ctx.Data["Licenses"] = models.Licenses

	if ctx.Req.Method == "GET" {
		ctx.HTML(200, "repo/create")
		return
	}

	if ctx.HasError() {
		ctx.HTML(200, "repo/create")
		return
	}

	_, err := models.CreateRepository(ctx.User, form.RepoName, form.Description,
		form.Language, form.License, form.Visibility == "private", form.InitReadme == "on")
	if err == nil {
		log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName)
		ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName)
		return
	} else if err == models.ErrRepoAlreadyExist {
		ctx.RenderWithErr("Repository name has already been used", "repo/create", &form)
		return
	} else if err == models.ErrRepoNameIllegal {
		ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/create", &form)
		return
	}
	ctx.Handle(200, "repo.Create", err)
}

func Single(ctx *middleware.Context, params martini.Params) {
	if !ctx.Repo.IsValid {
		return
	}

	branchName := params["branchname"]
	userName := params["username"]
	repoName := params["reponame"]

	// Get tree path
	treename := params["_1"]

	if len(treename) > 0 && treename[len(treename)-1] == '/' {
		ctx.Redirect("/" + ctx.Repo.Owner.LowerName + "/" +
			ctx.Repo.Repository.Name + "/src/" + branchName + "/" + treename[:len(treename)-1])
		return
	}

	ctx.Data["IsRepoToolbarSource"] = true

	// Branches.
	brs, err := models.GetBranches(userName, repoName)
	if err != nil {
		ctx.Handle(404, "repo.Single(GetBranches)", err)
		return
	} else if ctx.Repo.Repository.IsBare {
		ctx.Data["IsBareRepo"] = true
		ctx.HTML(200, "repo/single")
		return
	}
	ctx.Data["Branches"] = brs

	var commitId string
	if !models.IsBranchExist(userName, repoName, branchName) {
		commitId = branchName
	}

	repoFile, err := models.GetTargetFile(userName, repoName,
		branchName, commitId, treename)
	if err != nil && err != models.ErrRepoFileNotExist {
		ctx.Handle(404, "repo.Single(GetTargetFile)", err)
		return
	}

	branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + branchName
	rawLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/raw/" + branchName

	if len(treename) != 0 && repoFile == nil {
		ctx.Handle(404, "repo.Single", nil)
		return
	}

	if repoFile != nil && repoFile.IsFile() {
		if blob, err := repoFile.LookupBlob(); err != nil {
			ctx.Handle(404, "repo.Single(repoFile.LookupBlob)", err)
		} else {
			ctx.Data["FileSize"] = repoFile.Size
			ctx.Data["IsFile"] = true
			ctx.Data["FileName"] = repoFile.Name
			ext := path.Ext(repoFile.Name)
			if len(ext) > 0 {
				ext = ext[1:]
			}
			ctx.Data["FileExt"] = ext
			ctx.Data["FileLink"] = rawLink + "/" + treename

			data := blob.Contents()
			_, isTextFile := base.IsTextFile(data)
			_, isImageFile := base.IsImageFile(data)
			ctx.Data["FileIsText"] = isTextFile

			if isImageFile {
				ctx.Data["IsImageFile"] = true
			} else {
				readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name)
				ctx.Data["ReadmeExist"] = readmeExist
				if readmeExist {
					ctx.Data["FileContent"] = string(base.RenderMarkdown(data, ""))
				} else {
					if isTextFile {
						ctx.Data["FileContent"] = string(data)
					}
				}
			}
		}

	} else {
		// Directory and file list.
		files, err := models.GetReposFiles(userName, repoName,
			branchName, commitId, treename)
		if err != nil {
			ctx.Handle(404, "repo.Single(GetReposFiles)", err)
			return
		}

		ctx.Data["Files"] = files

		var readmeFile *models.RepoFile

		for _, f := range files {
			if !f.IsFile() || !base.IsReadmeFile(f.Name) {
				continue
			} else {
				readmeFile = f
				break
			}
		}

		if readmeFile != nil {
			ctx.Data["ReadmeInSingle"] = true
			ctx.Data["ReadmeExist"] = true
			if blob, err := readmeFile.LookupBlob(); err != nil {
				ctx.Handle(404, "repo.Single(readmeFile.LookupBlob)", err)
				return
			} else {
				ctx.Data["FileSize"] = readmeFile.Size
				ctx.Data["FileLink"] = rawLink + "/" + treename
				data := blob.Contents()
				_, isTextFile := base.IsTextFile(data)
				ctx.Data["FileIsText"] = isTextFile
				ctx.Data["FileName"] = readmeFile.Name
				if isTextFile {
					ctx.Data["FileContent"] = string(base.RenderMarkdown(data, branchLink))
				}
			}
		}
	}

	ctx.Data["Username"] = userName
	ctx.Data["Reponame"] = repoName

	var treenames []string
	Paths := make([]string, 0)

	if len(treename) > 0 {
		treenames = strings.Split(treename, "/")
		for i, _ := range treenames {
			Paths = append(Paths, strings.Join(treenames[0:i+1], "/"))
		}

		ctx.Data["HasParentPath"] = true
		if len(Paths)-2 >= 0 {
			ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2]
		}
	}

	// Get latest commit according username and repo name.
	commit, err := models.GetCommit(userName, repoName,
		branchName, commitId)
	if err != nil {
		log.Error("repo.Single(GetCommit): %v", err)
		ctx.Handle(404, "repo.Single(GetCommit)", err)
		return
	}
	ctx.Data["LastCommit"] = commit

	ctx.Data["CommitId"] = commitId

	ctx.Data["Paths"] = Paths
	ctx.Data["Treenames"] = treenames
	ctx.Data["BranchLink"] = branchLink
	ctx.HTML(200, "repo/single")
}

func SingleDownload(ctx *middleware.Context, params martini.Params) {
	if !ctx.Repo.IsValid {
		ctx.Handle(404, "repo.SingleDownload", nil)
		return
	}

	// Get tree path
	treename := params["_1"]

	repoFile, err := models.GetTargetFile(params["username"], params["reponame"],
		params["branchname"], params["commitid"], treename)

	if err != nil {
		ctx.Handle(404, "repo.SingleDownload(GetTargetFile)", err)
		return
	}

	blob, err := repoFile.LookupBlob()
	if err != nil {
		ctx.Handle(404, "repo.SingleDownload(LookupBlob)", err)
		return
	}

	data := blob.Contents()
	contentType, isTextFile := base.IsTextFile(data)
	_, isImageFile := base.IsImageFile(data)
	ctx.Res.Header().Set("Content-Type", contentType)
	if !isTextFile {
		ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(treename))
		ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
	}
	ctx.Res.Write(data)
}

func Http(ctx *middleware.Context, params martini.Params) {
	/*if !ctx.Repo.IsValid {
		return
	}*/

	// TODO: access check

	username := params["username"]
	reponame := params["reponame"]
	if strings.HasSuffix(reponame, ".git") {
		reponame = reponame[:len(reponame)-4]
	}

	prefix := path.Join("/", username, params["reponame"])
	server := &webdav.Server{
		Fs:         webdav.Dir(models.RepoPath(username, reponame)),
		TrimPrefix: prefix,
		Listings:   true,
	}

	server.ServeHTTP(ctx.ResponseWriter, ctx.Req)
}

func Setting(ctx *middleware.Context, params martini.Params) {
	if !ctx.Repo.IsOwner {
		ctx.Handle(404, "repo.Setting", nil)
		return
	}

	ctx.Data["IsRepoToolbarSetting"] = true

	if ctx.Repo.Repository.IsBare {
		ctx.Data["IsBareRepo"] = true
		ctx.HTML(200, "repo/setting")
		return
	}

	var title string
	if t, ok := ctx.Data["Title"].(string); ok {
		title = t
	}

	ctx.Data["Title"] = title + " - settings"
	ctx.HTML(200, "repo/setting")
}

func SettingPost(ctx *middleware.Context) {
	if !ctx.Repo.IsOwner {
		ctx.Error(404)
		return
	}

	switch ctx.Query("action") {
	case "update":
		ctx.Repo.Repository.Description = ctx.Query("desc")
		ctx.Repo.Repository.Website = ctx.Query("site")
		if err := models.UpdateRepository(ctx.Repo.Repository); err != nil {
			ctx.Handle(404, "repo.SettingPost(update)", err)
			return
		}
		ctx.Data["IsSuccess"] = true
		ctx.HTML(200, "repo/setting")
		log.Trace("%s Repository updated: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
	case "delete":
		if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
			ctx.Data["ErrorMsg"] = "Please make sure you entered repository name is correct."
			ctx.HTML(200, "repo/setting")
			return
		}

		if err := models.DeleteRepository(ctx.User.Id, ctx.Repo.Repository.Id, ctx.User.LowerName); err != nil {
			ctx.Handle(200, "repo.Delete", err)
			return
		}

		log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
		ctx.Redirect("/")
	}
}

func Action(ctx *middleware.Context, params martini.Params) {
	var err error
	switch params["action"] {
	case "watch":
		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
	case "unwatch":
		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
	case "desc":
		if !ctx.Repo.IsOwner {
			ctx.Error(404)
			return
		}

		ctx.Repo.Repository.Description = ctx.Query("desc")
		ctx.Repo.Repository.Website = ctx.Query("site")
		err = models.UpdateRepository(ctx.Repo.Repository)
	}

	if err != nil {
		log.Error("repo.Action(%s): %v", params["action"], err)
		ctx.JSON(200, map[string]interface{}{
			"ok":  false,
			"err": err.Error(),
		})
		return
	}
	ctx.JSON(200, map[string]interface{}{
		"ok": true,
	})
}
<commit_msg>fix download<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 repo

import (
	"path"
	"path/filepath"
	"strings"

	"github.com/codegangsta/martini"

	"github.com/gogits/webdav"

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

func Create(ctx *middleware.Context, form auth.CreateRepoForm) {
	ctx.Data["Title"] = "Create repository"
	ctx.Data["PageIsNewRepo"] = true // For navbar arrow.
	ctx.Data["LanguageIgns"] = models.LanguageIgns
	ctx.Data["Licenses"] = models.Licenses

	if ctx.Req.Method == "GET" {
		ctx.HTML(200, "repo/create")
		return
	}

	if ctx.HasError() {
		ctx.HTML(200, "repo/create")
		return
	}

	_, err := models.CreateRepository(ctx.User, form.RepoName, form.Description,
		form.Language, form.License, form.Visibility == "private", form.InitReadme == "on")
	if err == nil {
		log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName)
		ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName)
		return
	} else if err == models.ErrRepoAlreadyExist {
		ctx.RenderWithErr("Repository name has already been used", "repo/create", &form)
		return
	} else if err == models.ErrRepoNameIllegal {
		ctx.RenderWithErr(models.ErrRepoNameIllegal.Error(), "repo/create", &form)
		return
	}
	ctx.Handle(200, "repo.Create", err)
}

func Single(ctx *middleware.Context, params martini.Params) {
	if !ctx.Repo.IsValid {
		return
	}

	branchName := params["branchname"]
	userName := params["username"]
	repoName := params["reponame"]

	// Get tree path
	treename := params["_1"]

	if len(treename) > 0 && treename[len(treename)-1] == '/' {
		ctx.Redirect("/" + ctx.Repo.Owner.LowerName + "/" +
			ctx.Repo.Repository.Name + "/src/" + branchName + "/" + treename[:len(treename)-1])
		return
	}

	ctx.Data["IsRepoToolbarSource"] = true

	// Branches.
	brs, err := models.GetBranches(userName, repoName)
	if err != nil {
		ctx.Handle(404, "repo.Single(GetBranches)", err)
		return
	} else if ctx.Repo.Repository.IsBare {
		ctx.Data["IsBareRepo"] = true
		ctx.HTML(200, "repo/single")
		return
	}
	ctx.Data["Branches"] = brs

	var commitId string
	if !models.IsBranchExist(userName, repoName, branchName) {
		commitId = branchName
	}

	repoFile, err := models.GetTargetFile(userName, repoName,
		branchName, commitId, treename)
	if err != nil && err != models.ErrRepoFileNotExist {
		ctx.Handle(404, "repo.Single(GetTargetFile)", err)
		return
	}

	branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + branchName
	rawLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/raw/" + branchName

	if len(treename) != 0 && repoFile == nil {
		ctx.Handle(404, "repo.Single", nil)
		return
	}

	if repoFile != nil && repoFile.IsFile() {
		if blob, err := repoFile.LookupBlob(); err != nil {
			ctx.Handle(404, "repo.Single(repoFile.LookupBlob)", err)
		} else {
			ctx.Data["FileSize"] = repoFile.Size
			ctx.Data["IsFile"] = true
			ctx.Data["FileName"] = repoFile.Name
			ext := path.Ext(repoFile.Name)
			if len(ext) > 0 {
				ext = ext[1:]
			}
			ctx.Data["FileExt"] = ext
			ctx.Data["FileLink"] = rawLink + "/" + treename

			data := blob.Contents()
			_, isTextFile := base.IsTextFile(data)
			_, isImageFile := base.IsImageFile(data)
			ctx.Data["FileIsText"] = isTextFile

			if isImageFile {
				ctx.Data["IsImageFile"] = true
			} else {
				readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name)
				ctx.Data["ReadmeExist"] = readmeExist
				if readmeExist {
					ctx.Data["FileContent"] = string(base.RenderMarkdown(data, ""))
				} else {
					if isTextFile {
						ctx.Data["FileContent"] = string(data)
					}
				}
			}
		}

	} else {
		// Directory and file list.
		files, err := models.GetReposFiles(userName, repoName,
			branchName, commitId, treename)
		if err != nil {
			ctx.Handle(404, "repo.Single(GetReposFiles)", err)
			return
		}

		ctx.Data["Files"] = files

		var readmeFile *models.RepoFile

		for _, f := range files {
			if !f.IsFile() || !base.IsReadmeFile(f.Name) {
				continue
			} else {
				readmeFile = f
				break
			}
		}

		if readmeFile != nil {
			ctx.Data["ReadmeInSingle"] = true
			ctx.Data["ReadmeExist"] = true
			if blob, err := readmeFile.LookupBlob(); err != nil {
				ctx.Handle(404, "repo.Single(readmeFile.LookupBlob)", err)
				return
			} else {
				ctx.Data["FileSize"] = readmeFile.Size
				ctx.Data["FileLink"] = rawLink + "/" + treename
				data := blob.Contents()
				_, isTextFile := base.IsTextFile(data)
				ctx.Data["FileIsText"] = isTextFile
				ctx.Data["FileName"] = readmeFile.Name
				if isTextFile {
					ctx.Data["FileContent"] = string(base.RenderMarkdown(data, branchLink))
				}
			}
		}
	}

	ctx.Data["Username"] = userName
	ctx.Data["Reponame"] = repoName

	var treenames []string
	Paths := make([]string, 0)

	if len(treename) > 0 {
		treenames = strings.Split(treename, "/")
		for i, _ := range treenames {
			Paths = append(Paths, strings.Join(treenames[0:i+1], "/"))
		}

		ctx.Data["HasParentPath"] = true
		if len(Paths)-2 >= 0 {
			ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2]
		}
	}

	// Get latest commit according username and repo name.
	commit, err := models.GetCommit(userName, repoName,
		branchName, commitId)
	if err != nil {
		log.Error("repo.Single(GetCommit): %v", err)
		ctx.Handle(404, "repo.Single(GetCommit)", err)
		return
	}
	ctx.Data["LastCommit"] = commit

	ctx.Data["CommitId"] = commitId

	ctx.Data["Paths"] = Paths
	ctx.Data["Treenames"] = treenames
	ctx.Data["BranchLink"] = branchLink
	ctx.HTML(200, "repo/single")
}

func SingleDownload(ctx *middleware.Context, params martini.Params) {
	if !ctx.Repo.IsValid {
		ctx.Handle(404, "repo.SingleDownload", nil)
		return
	}

	// Get tree path
	treename := params["_1"]

	repoFile, err := models.GetTargetFile(params["username"], params["reponame"],
		params["branchname"], params["commitid"], treename)

	if err != nil {
		ctx.Handle(404, "repo.SingleDownload(GetTargetFile)", err)
		return
	}

	blob, err := repoFile.LookupBlob()
	if err != nil {
		ctx.Handle(404, "repo.SingleDownload(LookupBlob)", err)
		return
	}

	data := blob.Contents()
	contentType, isTextFile := base.IsTextFile(data)
	_, isImageFile := base.IsImageFile(data)
	ctx.Res.Header().Set("Content-Type", contentType)
	if !isTextFile && !isImageFile {
		ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(treename))
		ctx.Res.Header().Set("Content-Transfer-Encoding", "binary")
	}
	ctx.Res.Write(data)
}

func Http(ctx *middleware.Context, params martini.Params) {
	/*if !ctx.Repo.IsValid {
		return
	}*/

	// TODO: access check

	username := params["username"]
	reponame := params["reponame"]
	if strings.HasSuffix(reponame, ".git") {
		reponame = reponame[:len(reponame)-4]
	}

	prefix := path.Join("/", username, params["reponame"])
	server := &webdav.Server{
		Fs:         webdav.Dir(models.RepoPath(username, reponame)),
		TrimPrefix: prefix,
		Listings:   true,
	}

	server.ServeHTTP(ctx.ResponseWriter, ctx.Req)
}

func Setting(ctx *middleware.Context, params martini.Params) {
	if !ctx.Repo.IsOwner {
		ctx.Handle(404, "repo.Setting", nil)
		return
	}

	ctx.Data["IsRepoToolbarSetting"] = true

	if ctx.Repo.Repository.IsBare {
		ctx.Data["IsBareRepo"] = true
		ctx.HTML(200, "repo/setting")
		return
	}

	var title string
	if t, ok := ctx.Data["Title"].(string); ok {
		title = t
	}

	ctx.Data["Title"] = title + " - settings"
	ctx.HTML(200, "repo/setting")
}

func SettingPost(ctx *middleware.Context) {
	if !ctx.Repo.IsOwner {
		ctx.Error(404)
		return
	}

	switch ctx.Query("action") {
	case "update":
		ctx.Repo.Repository.Description = ctx.Query("desc")
		ctx.Repo.Repository.Website = ctx.Query("site")
		if err := models.UpdateRepository(ctx.Repo.Repository); err != nil {
			ctx.Handle(404, "repo.SettingPost(update)", err)
			return
		}
		ctx.Data["IsSuccess"] = true
		ctx.HTML(200, "repo/setting")
		log.Trace("%s Repository updated: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
	case "delete":
		if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
			ctx.Data["ErrorMsg"] = "Please make sure you entered repository name is correct."
			ctx.HTML(200, "repo/setting")
			return
		}

		if err := models.DeleteRepository(ctx.User.Id, ctx.Repo.Repository.Id, ctx.User.LowerName); err != nil {
			ctx.Handle(200, "repo.Delete", err)
			return
		}

		log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName)
		ctx.Redirect("/")
	}
}

func Action(ctx *middleware.Context, params martini.Params) {
	var err error
	switch params["action"] {
	case "watch":
		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true)
	case "unwatch":
		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
	case "desc":
		if !ctx.Repo.IsOwner {
			ctx.Error(404)
			return
		}

		ctx.Repo.Repository.Description = ctx.Query("desc")
		ctx.Repo.Repository.Website = ctx.Query("site")
		err = models.UpdateRepository(ctx.Repo.Repository)
	}

	if err != nil {
		log.Error("repo.Action(%s): %v", params["action"], err)
		ctx.JSON(200, map[string]interface{}{
			"ok":  false,
			"err": err.Error(),
		})
		return
	}
	ctx.JSON(200, map[string]interface{}{
		"ok": true,
	})
}
<|endoftext|>


<commit_before>package srnd

import (
	"errors"
	"io"
	"net"
)

var ErrSpamFilterNotEnabled = errors.New("spam filter access attempted when disabled")

type SpamFilter struct {
	addr    string
	enabled bool
}

func (sp *SpamFilter) Configure(c SpamConfig) {
	sp.enabled = c.enabled
	sp.addr = c.addr
}

func (sp *SpamFilter) Enabled() bool {
	return sp.enabled
}

func (sp *SpamFilter) Rewrite(msg io.Reader, out io.WriteCloser) error {
	var buff [65636]byte
	if !sp.Enabled() {
		return ErrSpamFilterNotEnabled
	}
	addr, err := net.ResolveTCPAddr("tcp", sp.addr)
	if err != nil {
		return err
	}
	c, err := net.DialTCP("tcp", nil, addr)
	if err != nil {
		return err
	}
	io.WriteString(c, "SPAMC/1.5\r\n")
	io.CopyBuffer(c, msg, buff[:])
	c.CloseWrite()
	_, err = io.CopyBuffer(out, c, buff[:])
	c.Close()
	out.Close()
	return err
}
<commit_msg>fix sa hook<commit_after>package srnd

import (
	"errors"
	"io"
	"net"
)

var ErrSpamFilterNotEnabled = errors.New("spam filter access attempted when disabled")

type SpamFilter struct {
	addr    string
	enabled bool
}

func (sp *SpamFilter) Configure(c SpamConfig) {
	sp.enabled = c.enabled
	sp.addr = c.addr
}

func (sp *SpamFilter) Enabled() bool {
	return sp.enabled
}

func (sp *SpamFilter) Rewrite(msg io.Reader, out io.WriteCloser) error {
	var buff [65636]byte
	if !sp.Enabled() {
		return ErrSpamFilterNotEnabled
	}
	addr, err := net.ResolveTCPAddr("tcp", sp.addr)
	if err != nil {
		return err
	}
	c, err := net.DialTCP("tcp", nil, addr)
	if err != nil {
		return err
	}
	io.WriteString(c, "CHECK SPAMC/1.5\r\n")
	io.CopyBuffer(c, msg, buff[:])
	c.CloseWrite()
	_, err = io.CopyBuffer(out, c, buff[:])
	c.Close()
	out.Close()
	return err
}
<|endoftext|>


<commit_before>// response.go - Generic response handler
//
// To the extent possible under law, Yawning Angel waived all copyright
// and related or neighboring rights to bulb, using the creative
// commons "cc0" public domain dedication. See LICENSE or
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.

package bulb

import (
	"log"
	"net/textproto"
	"strconv"
)

// Response is a response to a control port command, or an asyncrhonous event.
type Response struct {
	// Err is the status code and string representation associated with a
	// response.  Responses that have completed successfully will also have
	// Err set to indicate such.
	Err *textproto.Error

	// Reply is the text on the EndReplyLine of the response.
	Reply string

	// Data is the MidReplyLines/DataReplyLines of the response.  Dot encoded
	// data is "decoded" and presented as a single string (terminal ".CRLF"
	// removed, all intervening CRs stripped).
	Data []string
}

// IsOk returns true if the response status code indicates success or
// an asynchronous event.
func (r *Response) IsOk() bool {
	switch r.Err.Code {
	case StatusOk, StatusOkUnneccecary, StatusAsyncEvent:
		return true
	default:
		return false
	}
}

// IsAsync returns true if the response is an asyncrhonous event.
func (r *Response) IsAsync() bool {
	return r.Err.Code == StatusAsyncEvent
}

// ReadResponse returns the next response object. Calling this
// simultaniously with Read, Request, or StartAsyncReader will lead to
// undefined behavior
func (c *Conn) ReadResponse() (*Response, error) {
	var resp *Response
	var statusCode int
	for {
		line, err := c.conn.ReadLine()
		if err != nil {
			return nil, err
		}
		if c.debugLog {
			log.Printf("S: %s", line)
		}

		// Parse the line that was just read.
		if len(line) < 4 {
			return nil, newProtocolError("truncated response: '%s'", line)
		}
		if code, err := strconv.Atoi(line[0:3]); err != nil {
			return nil, newProtocolError("invalid status code: '%s'", line[0:3])
		} else if code < 100 {
			return nil, newProtocolError("invalid status code: '%s'", line[0:3])
		} else if resp == nil {
			resp = new(Response)
			statusCode = code
		} else if code != statusCode {
			// The status code should stay fixed for all lines of the
			// response, since events can't be interleaved with response
			// lines.
			return nil, newProtocolError("status code changed: %03d != %03d", code, statusCode)
		}

		if line[3] == ' ' {
			// Final line in the response.
			resp.Reply = line[4:]
			resp.Err = statusCodeToError(statusCode, resp.Reply)
			return resp, nil
		}

		if resp.Data == nil {
			resp.Data = make([]string, 0, 1)
		}
		switch line[3] {
		case '-':
			// Continuation, keep reading.
			resp.Data = append(resp.Data, line[4:])
		case '+':
			// A "dot-encoded" payload follows.
			resp.Data = append(resp.Data, line[4:])
			dotBody, err := c.conn.ReadDotBytes()
			if err != nil {
				return nil, err
			}
			if c.debugLog {
				log.Printf("S: [dot encoded data]")
			}
			resp.Data = append(resp.Data, string(dotBody))
		default:
			return nil, newProtocolError("invalid separator: '%c'", line[3])
		}
	}
}
<commit_msg>include RawLines in Response<commit_after>// response.go - Generic response handler
//
// To the extent possible under law, Yawning Angel waived all copyright
// and related or neighboring rights to bulb, using the creative
// commons "cc0" public domain dedication. See LICENSE or
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.

package bulb

import (
	"log"
	"net/textproto"
	"strconv"
	"strings"
)

// Response is a response to a control port command, or an asyncrhonous event.
type Response struct {
	// Err is the status code and string representation associated with a
	// response.  Responses that have completed successfully will also have
	// Err set to indicate such.
	Err *textproto.Error

	// Reply is the text on the EndReplyLine of the response.
	Reply string

	// Data is the MidReplyLines/DataReplyLines of the response.  Dot encoded
	// data is "decoded" and presented as a single string (terminal ".CRLF"
	// removed, all intervening CRs stripped).
	Data []string

	// RawLines is all of the lines of a response, without CRLFs.
	RawLines []string
}

// IsOk returns true if the response status code indicates success or
// an asynchronous event.
func (r *Response) IsOk() bool {
	switch r.Err.Code {
	case StatusOk, StatusOkUnneccecary, StatusAsyncEvent:
		return true
	default:
		return false
	}
}

// IsAsync returns true if the response is an asyncrhonous event.
func (r *Response) IsAsync() bool {
	return r.Err.Code == StatusAsyncEvent
}

// ReadResponse returns the next response object. Calling this
// simultaniously with Read, Request, or StartAsyncReader will lead to
// undefined behavior
func (c *Conn) ReadResponse() (*Response, error) {
	var resp *Response
	var statusCode int
	for {
		line, err := c.conn.ReadLine()
		if err != nil {
			return nil, err
		}
		if c.debugLog {
			log.Printf("S: %s", line)
		}

		// Parse the line that was just read.
		if len(line) < 4 {
			return nil, newProtocolError("truncated response: '%s'", line)
		}
		if code, err := strconv.Atoi(line[0:3]); err != nil {
			return nil, newProtocolError("invalid status code: '%s'", line[0:3])
		} else if code < 100 {
			return nil, newProtocolError("invalid status code: '%s'", line[0:3])
		} else if resp == nil {
			resp = new(Response)
			statusCode = code
		} else if code != statusCode {
			// The status code should stay fixed for all lines of the
			// response, since events can't be interleaved with response
			// lines.
			return nil, newProtocolError("status code changed: %03d != %03d", code, statusCode)
		}
		if resp.RawLines == nil {
			resp.RawLines = make([]string, 0, 1)
		}

		if line[3] == ' ' {
			// Final line in the response.
			resp.Reply = line[4:]
			resp.Err = statusCodeToError(statusCode, resp.Reply)
			resp.RawLines = append(resp.RawLines, line)
			return resp, nil
		}

		if resp.Data == nil {
			resp.Data = make([]string, 0, 1)
		}
		switch line[3] {
		case '-':
			// Continuation, keep reading.
			resp.Data = append(resp.Data, line[4:])
			resp.RawLines = append(resp.RawLines, line)
		case '+':
			// A "dot-encoded" payload follows.
			resp.Data = append(resp.Data, line[4:])
			resp.RawLines = append(resp.RawLines, line)
			dotBody, err := c.conn.ReadDotBytes()
			if err != nil {
				return nil, err
			}
			if c.debugLog {
				log.Printf("S: [dot encoded data]")
			}
			resp.Data = append(resp.Data, string(dotBody))
			dotLines := strings.Split(string(dotBody), "\n")
			for _, dotLine := range dotLines[:len(dotLines)-1] {
				resp.RawLines = append(resp.RawLines, dotLine)
			}
			resp.RawLines = append(resp.RawLines, ".")
		default:
			return nil, newProtocolError("invalid separator: '%c'", line[3])
		}
	}
}
<|endoftext|>


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

import (
	"github.com/liangdas/mqant/utils"
	"fmt"
	"reflect"
	"github.com/liangdas/mqant/module"
)

var (
	NULL="null"	//nil   null
	BOOL="bool"	//bool
	INT="int"	//int
	LONG="long"	//long64
	FLOAT="float"	//float32
	DOUBLE="double"	//float64
	BYTES="bytes"	//[]byte
	STRING="string" //string
	MAP="map"	//map[string]interface{}
	MAPSTR="mapstr"	//map[string]string{}
)

func ArgsTypeAnd2Bytes(app module.App,arg interface{}) (string,[]byte,error) {
	switch v2:=arg.(type) {
	case nil:
		return NULL,nil,nil
	case string:
		return STRING,[]byte(v2),nil
	case bool:
		return BOOL,utils.BoolToBytes(v2),nil
	case int32:
		return INT,utils.Int32ToBytes(v2),nil
	case int64:
		return LONG,utils.Int64ToBytes(v2),nil
	case float32:
		return FLOAT,utils.Float32ToBytes(v2),nil
	case float64:
		return DOUBLE,utils.Float64ToBytes(v2),nil
	case []byte:
		return BYTES,v2,nil
	case map[string]interface{}:
		bytes,err:=utils.MapToBytes(v2)
		if err != nil{
			return MAP,nil,err
		}
		return MAP,bytes,nil
	case map[string]string:
		bytes,err:=utils.MapToBytesString(v2)
		if err != nil{
			return MAPSTR,nil,err
		}
		return MAPSTR,bytes,nil
	default:
		for _,v:=range app.GetRPCSerialize(){
			ptype,vk,err:=v.Serialize(arg)
			if err==nil{
				//解析成功了
				return ptype,vk,err
			}
		}
		return "", nil,fmt.Errorf("args [%s] Types not allowed",reflect.TypeOf(arg))
	}
}

func Bytes2Args(app module.App,argsType string,args []byte )(interface{},error){
	switch argsType {
	case NULL:
		return nil,nil
	case STRING:
		return string(args),nil
	case BOOL:
		return utils.BytesToBool(args),nil
	case INT:
		return utils.BytesToInt32(args),nil
	case LONG:
		return utils.BytesToInt64(args),nil
	case FLOAT:
		return utils.BytesToFloat32(args),nil
	case DOUBLE:
		return utils.BytesToFloat64(args),nil
	case BYTES:
		return args,nil
	case MAP:
		mps,errs:= utils.BytesToMap(args)
		if errs!=nil{
			return	nil,errs
		}
		return mps,nil
	case MAPSTR:
		mps,errs:= utils.BytesToMapString(args)
		if errs!=nil{
			return	nil,errs
		}
		return mps,nil
	default:
		for _,v:=range app.GetRPCSerialize(){
			vk,err:=v.Deserialize(argsType,args)
			if err==nil{
				//解析成功了
				return vk,err
			}
		}
		return	nil,fmt.Errorf("args [%s] Types not allowed",argsType)
	}
}

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

import (
	"github.com/liangdas/mqant/utils"
	"fmt"
	"reflect"
	"github.com/liangdas/mqant/module"
)

var (
	NULL="null"	//nil   null
	BOOL="bool"	//bool
	INT="int"	//int
	LONG="long"	//long64
	FLOAT="float"	//float32
	DOUBLE="double"	//float64
	BYTES="bytes"	//[]byte
	STRING="string" //string
	MAP="map"	//map[string]interface{}
	MAPSTR="mapstr"	//map[string]string{}
)

func ArgsTypeAnd2Bytes(app module.App,arg interface{}) (string,[]byte,error) {
	switch v2:=arg.(type) {
	case []uint8:
		return BYTES,v2,nil
	}
	switch v2:=arg.(type) {
	case nil:
		return NULL,nil,nil
	case string:
		return STRING,[]byte(v2),nil
	case bool:
		return BOOL,utils.BoolToBytes(v2),nil
	case int32:
		return INT,utils.Int32ToBytes(v2),nil
	case int64:
		return LONG,utils.Int64ToBytes(v2),nil
	case float32:
		return FLOAT,utils.Float32ToBytes(v2),nil
	case float64:
		return DOUBLE,utils.Float64ToBytes(v2),nil
	case []byte:
		return BYTES,v2,nil
	case map[string]interface{}:
		bytes,err:=utils.MapToBytes(v2)
		if err != nil{
			return MAP,nil,err
		}
		return MAP,bytes,nil
	case map[string]string:
		bytes,err:=utils.MapToBytesString(v2)
		if err != nil{
			return MAPSTR,nil,err
		}
		return MAPSTR,bytes,nil
	default:
		for _,v:=range app.GetRPCSerialize(){
			ptype,vk,err:=v.Serialize(arg)
			if err==nil{
				//解析成功了
				return ptype,vk,err
			}
		}
		return "", nil,fmt.Errorf("args [%s] Types not allowed",reflect.TypeOf(arg))
	}
}

func Bytes2Args(app module.App,argsType string,args []byte )(interface{},error){
	switch argsType {
	case NULL:
		return nil,nil
	case STRING:
		return string(args),nil
	case BOOL:
		return utils.BytesToBool(args),nil
	case INT:
		return utils.BytesToInt32(args),nil
	case LONG:
		return utils.BytesToInt64(args),nil
	case FLOAT:
		return utils.BytesToFloat32(args),nil
	case DOUBLE:
		return utils.BytesToFloat64(args),nil
	case BYTES:
		return args,nil
	case MAP:
		mps,errs:= utils.BytesToMap(args)
		if errs!=nil{
			return	nil,errs
		}
		return mps,nil
	case MAPSTR:
		mps,errs:= utils.BytesToMapString(args)
		if errs!=nil{
			return	nil,errs
		}
		return mps,nil
	default:
		for _,v:=range app.GetRPCSerialize(){
			vk,err:=v.Deserialize(argsType,args)
			if err==nil{
				//解析成功了
				return vk,err
			}
		}
		return	nil,fmt.Errorf("args [%s] Types not allowed",argsType)
	}
}

<|endoftext|>


<commit_before>// Route to server the files.
// Copyright © 2015 - Rémy MATHIEU

package server

import (
	"fmt"
	"net/http"

	"github.com/gorilla/mux"
)

type ServingHandler struct {
	Server *Server // pointer to the started server
}

func (s *ServingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Parse the route parameters
	vars := mux.Vars(r)

	id := vars["file"]

	// Some check on the file id
	if len(id) == 0 {
		w.WriteHeader(404)
		return
	}

	// Existing file ?
	if s.Server.Metadata.Data[id].Filename == "" {
		w.WriteHeader(404)
		return
	}

	// Existing, serve the file !

}
<commit_msg>Route serving data done.<commit_after>// Route to server the files.
// Copyright © 2015 - Rémy MATHIEU

package server

import (
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/gorilla/mux"
)

type ServingHandler struct {
	Server *Server // pointer to the started server
}

func (s *ServingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Parse the route parameters
	vars := mux.Vars(r)

	id := vars["file"]

	// Some check on the file id
	if len(id) == 0 {
		w.WriteHeader(404)
		return
	}

	// Existing file ?
	entry := s.Server.Metadata.Data[id]
	if entry.Filename == "" {
		w.WriteHeader(404)
		return
	}

	// Existing, serve the file !

	// read it
	file, err := os.Open(s.Server.Flags.OutputDirectory + "/" + entry.Filename)
	if err != nil {
		w.WriteHeader(500)
		log.Println("[err] While requesting:", entry.Filename)
		log.Println(err)
		return
	}

	data, err := ioutil.ReadAll(file)
	if err != nil {
		w.WriteHeader(500)
		log.Println("[err] While reading:", entry.Filename)
		log.Println(err)
	}

	contentType := http.DetectContentType(data)
	w.Header().Set("Content-Type", contentType)
	w.Write(data)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"testing"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/chaincfg"
)

func TestSphinxCorrectness(t *testing.T) {
	nodes := make([]*SphinxNode, numMaxHops)

	// Create numMaxHops random sphinx nodes.
	for i := 0; i < len(nodes); i++ {
		privKey, err := btcec.NewPrivateKey(btcec.S256())
		if err != nil {
			t.Fatalf("Unable to generate random key for sphinx node: %v", err)
		}

		nodes[i] = NewSphinxNode(privKey, &chaincfg.MainNetParams)
	}

	// Gather all the pub keys in the path.
	route := make([]*btcec.PublicKey, len(nodes))
	for i := 0; i < len(nodes); i++ {
		route[i] = nodes[i].lnKey.PubKey()
	}

	// Generate a forwarding message to route to the final node via the
	// generated intermdiates nodes above.
	fwdMsg, err := NewForwardingMessage(route, []byte("roasbeef"), []byte("testing"))
	if err != nil {
		t.Fatalf("Unable to create forwarding message: %#v", err)
	}

	// TODO(roasbeef): assert proper nodeID of first hop

	// Now simulate the message propagating through the mix net eventually
	// reaching the final destination.
	for i := 0; i < len(nodes); i++ {
		hop := nodes[i]

		processAction, err := hop.ProcessForwardingMessage(fwdMsg)
		if err != nil {
			t.Fatalf("Node %v was unabled to process the forwarding message: %v", i, err)
		}

		// If this is the last hop on the path, the node should
		// recognize that it's the exit node.
		if i == len(nodes)-1 {
			if processAction.action != ExitNode {
				t.Fatalf("Processing error, node %v is the last hop in"+
					"the path, yet it doesn't recognize so", i)
			}

			// The original destination address and message should
			// now be fully decrypted.
			if !bytes.Equal([]byte("roasbeef"), processAction.destAddr) {
				t.Fatalf("Message parsed incorrectly at final destination!"+
					"Should be %v, is instead %v",
					[]byte("roasbeef"), processAction.destAddr)
			}

			if !bytes.HasPrefix([]byte("testing"), processAction.destMsg) {
				t.Fatalf("Dest addr parsed incorrectly at final destination!"+
					"Should be %v, is instead %v",
					[]byte("testing"), processAction.destMsg)
			}

		} else if processAction.action != MoreHops {
			// If this isn't the last node in the path, then the returned
			// action should indicate that there are more hops to go.
			t.Fatalf("Processing error, node %v is not the final"+
				" hop, yet thinks it is.", i)
		}

		// The next hop should have been parsed as node[i+1].
		parsedNextHop := processAction.fwdMsg.Header.RoutingInfo[:securityParameter]
		if !bytes.Equal(parsedNextHop, nodes[i+1].nodeID[:]) {
			t.Fatalf("Processing error, next hop parsed incorrectly."+
				" next hop shoud be %v, was instead parsed as %v",
				nodes[i+1].nodeID[:], parsedNextHop)
		}

		fwdMsg = processAction.fwdMsg
	}
}
<commit_msg>add padding to destination address in tests<commit_after>package main

import (
	"bytes"
	"testing"

	"github.com/btcsuite/btcd/btcec"
	"github.com/btcsuite/btcd/chaincfg"
)

func TestSphinxCorrectness(t *testing.T) {
	nodes := make([]*SphinxNode, numMaxHops)

	// Create numMaxHops random sphinx nodes.
	for i := 0; i < len(nodes); i++ {
		privKey, err := btcec.NewPrivateKey(btcec.S256())
		if err != nil {
			t.Fatalf("Unable to generate random key for sphinx node: %v", err)
		}

		nodes[i] = NewSphinxNode(privKey, &chaincfg.MainNetParams)
	}

	// Gather all the pub keys in the path.
	route := make([]*btcec.PublicKey, len(nodes))
	for i := 0; i < len(nodes); i++ {
		route[i] = nodes[i].lnKey.PubKey()
	}

	// Generate a forwarding message to route to the final node via the
	// generated intermdiates nodes above.
	// Destination should be Hash160, adding padding so parsing still works.
	dest := append([]byte("roasbeef"), bytes.Repeat([]byte{0}, securityParameter-8)...)
	fwdMsg, err := NewForwardingMessage(route, dest, []byte("testing"))
	if err != nil {
		t.Fatalf("Unable to create forwarding message: %#v", err)
	}

	// TODO(roasbeef): assert proper nodeID of first hop

	// Now simulate the message propagating through the mix net eventually
	// reaching the final destination.
	for i := 0; i < len(nodes); i++ {
		hop := nodes[i]

		processAction, err := hop.ProcessForwardingMessage(fwdMsg)
		if err != nil {
			t.Fatalf("Node %v was unabled to process the forwarding message: %v", i, err)
		}

		// If this is the last hop on the path, the node should
		// recognize that it's the exit node.
		if i == len(nodes)-1 {
			if processAction.action != ExitNode {
				t.Fatalf("Processing error, node %v is the last hop in"+
					"the path, yet it doesn't recognize so", i)
			}

			// The original destination address and message should
			// now be fully decrypted.
			if !bytes.Equal([]byte("roasbeef"), processAction.destAddr) {
				t.Fatalf("Message parsed incorrectly at final destination!"+
					"Should be %v, is instead %v",
					[]byte("roasbeef"), processAction.destAddr)
			}

			if !bytes.HasPrefix([]byte("testing"), processAction.destMsg) {
				t.Fatalf("Dest addr parsed incorrectly at final destination!"+
					"Should be %v, is instead %v",
					[]byte("testing"), processAction.destMsg)
			}

		} else if processAction.action != MoreHops {
			// If this isn't the last node in the path, then the returned
			// action should indicate that there are more hops to go.
			t.Fatalf("Processing error, node %v is not the final"+
				" hop, yet thinks it is.", i)
		}

		// The next hop should have been parsed as node[i+1].
		parsedNextHop := processAction.fwdMsg.Header.RoutingInfo[:securityParameter]
		if !bytes.Equal(parsedNextHop, nodes[i+1].nodeID[:]) {
			t.Fatalf("Processing error, next hop parsed incorrectly."+
				" next hop shoud be %v, was instead parsed as %v",
				nodes[i+1].nodeID[:], parsedNextHop)
		}

		fwdMsg = processAction.fwdMsg
	}
}
<|endoftext|>


<commit_before>// Package spreadsheet provides access to the Google Spreadsheet.
//
// Usage example:
//
//   import "github.com/Iwark/spreadsheet"
//   ...
//   service, err := spreadsheet.New(oauthHttpClient)
package spreadsheet // import "github.com/Iwark/spreadsheet"

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"strings"
	"sync"
	"time"
)

const (
	basePath = "https://spreadsheets.google.com"
	docBase  = "https://docs.google.com/spreadsheets"
)

const (
	// SpreadsheetScope is a scope of View and manage your Google Spreadsheet data
	SpreadsheetScope = "https://spreadsheets.google.com/feeds"
)

// SyncCellsAtOnce is a length of cells to synchronize at once
var SyncCellsAtOnce = 1000

// MaxConnections is the number of max concurrent connections
var MaxConnections = 150

// New creates a Service object
func New(client *http.Client) (*Service, error) {
	if client == nil {
		return nil, errors.New("client is nil")
	}
	s := &Service{client: client, BasePath: basePath}
	s.Sheets = NewSheetsService(s)
	return s, nil
}

type Service struct {
	client   *http.Client
	BasePath string

	Sheets *SheetsService
}

func (s *Service) fetchAndUnmarshal(url string, v interface{}) error {
	resp, err := s.client.Get(url)
	if err != nil {
		return err
	}
	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return err
	}
	err = xml.Unmarshal(body, v)
	if err != nil {
		return err
	}
	return nil
}

func NewSheetsService(s *Service) *SheetsService {
	ss := &SheetsService{s: s}
	return ss
}

type SheetsService struct {
	s *Service
}

// Worksheets returns the Worksheets object of the client
func (ss *SheetsService) Worksheets(key string) (*Worksheets, error) {
	url := fmt.Sprintf("%s/feeds/worksheets/%s/private/full", ss.s.BasePath, key)
	worksheets := &Worksheets{ss: ss}
	err := ss.s.fetchAndUnmarshal(url, &worksheets)
	if err != nil {
		return nil, err
	}
	return worksheets, nil
}

type Worksheets struct {
	ss *SheetsService

	XMLName xml.Name     `xml:"feed"`
	Title   string       `xml:"title"`
	Links   []Link       `xml:"link"`
	Entries []*Worksheet `xml:"entry"`
}

// AddWorksheet adds worksheet
func (ws *Worksheets) AddWorksheet(title string, rowCount, colCount int) error {

	var url string
	for _, l := range ws.Links {
		if l.Rel == "http://schemas.google.com/g/2005#post" {
			url = l.Href
			break
		}
	}
	if url == "" {
		return errors.New("URL not found")
	}

	entry := `<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gs="http://schemas.google.com/spreadsheets/2006">` +
		"<title>" + title + "</title>" +
		fmt.Sprintf("<gs:rowCount>%d</gs:rowCount>", rowCount) +
		fmt.Sprintf("<gs:colCount>%d</gs:colCount>", colCount) +
		`</entry>`

	req, err := http.NewRequest("POST", url, strings.NewReader(entry))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/atom+xml;charset=utf-8")

	resp, err := ws.ss.s.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	added := &Worksheet{}
	err = xml.Unmarshal(body, added)
	if err != nil {
		return err
	}
	ws.Entries = append(ws.Entries, added)

	return nil
}

// Get returns the worksheet of passed index
func (w *Worksheets) Get(i int) (*Worksheet, error) {
	if len(w.Entries) <= i {
		return nil, errors.New(fmt.Sprintf("worksheet of index %d was not found", i))
	}
	ws := w.Entries[i]
	err := ws.build(w)
	if err != nil {
		return nil, err
	}
	return ws, nil
}

// FindById returns the worksheet of passed id
func (w *Worksheets) FindById(id string) (*Worksheet, error) {
	var validID = regexp.MustCompile(fmt.Sprintf("%s$", id))
	for _, e := range w.Entries {
		if validID.MatchString(e.Id) {
			err := e.build(w)
			if err != nil {
				return nil, err
			}
			return e, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("worksheet of id %s was not found", id))
}

// FindByTitle returns the worksheet of passed title
func (w *Worksheets) FindByTitle(title string) (*Worksheet, error) {
	for _, e := range w.Entries {
		if e.Title == title {
			err := e.build(w)
			if err != nil {
				return nil, err
			}
			return e, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("worksheet of title %s was not found", title))
}

// ExistsTitled returns whether there is a sheet titlted given parameter
func (w *Worksheets) ExistsTitled(title string) bool {
	for _, e := range w.Entries {
		if e.Title == title {
			return true
		}
	}
	return false
}

type Worksheet struct {
	Id      string    `xml:"id"`
	Updated time.Time `xml:"updated"`
	Title   string    `xml:"title"`
	Content string    `xml:"content"`
	Links   []Link    `xml:"link"`

	ws            *Worksheets
	CellsFeed     string
	EditLink      string
	CSVLink       string
	MaxRowNum     int
	MaxColNum     int
	Rows          [][]*Cell
	modifiedCells []*Cell
}

// DocsURL is a URL to the google docs spreadsheet (human readable)
func (w *Worksheet) DocsURL() string {
	r := regexp.MustCompile(`/d/(.*?)/export\?gid=(\d+)`)
	group := r.FindSubmatch([]byte(w.CSVLink))
	if len(group) < 3 {
		return ""
	}
	key := string(group[1])
	gid := string(group[2])
	return fmt.Sprintf("%s/d/%s/edit#gid=%s", docBase, key, gid)
}

func (ws *Worksheet) build(w *Worksheets) error {

	ws.ws = w

	for _, l := range ws.Links {
		switch l.Rel {
		case "http://schemas.google.com/spreadsheets/2006#cellsfeed":
			ws.CellsFeed = l.Href
		case "edit":
			ws.EditLink = l.Href
		case "http://schemas.google.com/spreadsheets/2006#exportcsv":
			ws.CSVLink = l.Href
		default:
		}
	}

	var cells *Cells
	err := ws.ws.ss.s.fetchAndUnmarshal(fmt.Sprintf("%s?return-empty=true", ws.CellsFeed), &cells)
	if err != nil {
		return err
	}
	ws.modifiedCells = make([]*Cell, 0)

	for _, cell := range cells.Entries {
		cell.ws = ws
		if cell.Pos.Row > ws.MaxRowNum {
			ws.MaxRowNum = cell.Pos.Row
		}
		if cell.Pos.Col > ws.MaxColNum {
			ws.MaxColNum = cell.Pos.Col
		}
	}
	rows := make([][]*Cell, ws.MaxRowNum)
	for i := 0; i < ws.MaxRowNum; i++ {
		rows[i] = make([]*Cell, ws.MaxColNum)
	}
	for _, cell := range cells.Entries {
		rows[cell.Pos.Row-1][cell.Pos.Col-1] = cell
	}
	ws.Rows = rows

	return nil
}

func (ws *Worksheet) Destroy() error {
	req, err := http.NewRequest("DELETE", ws.EditLink, nil)
	if err != nil {
		return err
	}

	resp, err := ws.ws.ss.s.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	for i, e := range ws.ws.Entries {
		if e.Id == ws.Id {
			ws.ws.Entries = append(ws.ws.Entries[:i], ws.ws.Entries[i+1:]...)
			break
		}
	}

	return nil
}

// Synchronize saves the modified cells
func (ws *Worksheet) Synchronize() error {

	var wg sync.WaitGroup
	c := make(chan int, MaxConnections)
	mCells := ws.modifiedCells
	target := []*Cell{}
	errors := []error{}
	for len(mCells) > 0 {
		wg.Add(1)
		if len(mCells) >= SyncCellsAtOnce {
			target = mCells[:SyncCellsAtOnce]
			mCells = mCells[SyncCellsAtOnce:]
		} else {
			target = mCells[:len(mCells)]
			mCells = []*Cell{}
		}
		go func(s chan int, cells []*Cell) {
			defer wg.Done()
			s <- 1
			err := ws.synchronize(cells)
			if err != nil {
				errors = append(errors, err)
			}
			<-s
		}(c, target)
	}
	wg.Wait()
	close(c)
	if len(errors) > 0 {
		return errors[0]
	}
	return nil
}

func (ws *Worksheet) synchronize(cells []*Cell) error {
	feed := `
    <feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:batch="http://schemas.google.com/gdata/batch"
      xmlns:gs="http://schemas.google.com/spreadsheets/2006">
  `
	feed += fmt.Sprintf("<id>%s</id>", ws.CellsFeed)
	for _, mc := range cells {
		feed += `<entry>`
		feed += fmt.Sprintf("<batch:id>%d, %d</batch:id>", mc.Pos.Row, mc.Pos.Col)
		feed += `<batch:operation type="update"/>`
		feed += fmt.Sprintf("<id>%s</id>", mc.Id)
		feed += fmt.Sprintf("<link rel=\"edit\" type=\"application/atom+xml\" href=\"%s\"/>", mc.EditLink())
		feed += fmt.Sprintf("<gs:cell row=\"%d\" col=\"%d\" inputValue=\"%s\"/>", mc.Pos.Row, mc.Pos.Col, mc.Content)
		feed += `</entry>`
	}
	feed += `</feed>`
	url := fmt.Sprintf("%s/batch", ws.CellsFeed)
	req, err := http.NewRequest("POST", url, strings.NewReader(feed))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/atom+xml;charset=utf-8")

	resp, err := ws.ws.ss.s.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	return nil
}

type Link struct {
	Rel  string `xml:"rel,attr"`
	Type string `xml:"type,attr"`
	Href string `xml:"href,attr"`
}

type Cells struct {
	XMLName xml.Name `xml:"feed"`
	Title   string   `xml:"title"`
	Entries []*Cell  `xml:"entry"`
}

type Cell struct {
	ws      *Worksheet
	Id      string    `xml:"id"`
	Updated time.Time `xml:"updated"`
	Title   string    `xml:"title"`
	Content string    `xml:"content"`
	Links   []Link    `xml:"link"`
	Pos     struct {
		Row int `xml:"row,attr"`
		Col int `xml:"col,attr"`
	} `xml:"cell"`
}

func (c *Cell) Update(content string) {
	c.Content = content
	for _, mc := range c.ws.modifiedCells {
		if mc.Id == c.Id {
			return
		}
	}
	c.ws.modifiedCells = append(c.ws.modifiedCells, c)
}

func (c *Cell) FastUpdate(content string) {
	c.Content = content
	c.ws.modifiedCells = append(c.ws.modifiedCells, c)
}

func (c *Cell) EditLink() string {
	for _, l := range c.Links {
		if l.Rel == "edit" {
			return l.Href
		}
	}
	return ""
}
<commit_msg>Fix synchronize bug<commit_after>// Package spreadsheet provides access to the Google Spreadsheet.
//
// Usage example:
//
//   import "github.com/Iwark/spreadsheet"
//   ...
//   service, err := spreadsheet.New(oauthHttpClient)
package spreadsheet // import "github.com/Iwark/spreadsheet"

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"strings"
	"sync"
	"time"
)

const (
	basePath = "https://spreadsheets.google.com"
	docBase  = "https://docs.google.com/spreadsheets"
)

const (
	// SpreadsheetScope is a scope of View and manage your Google Spreadsheet data
	SpreadsheetScope = "https://spreadsheets.google.com/feeds"
)

// SyncCellsAtOnce is a length of cells to synchronize at once
var SyncCellsAtOnce = 1000

// MaxConnections is the number of max concurrent connections
var MaxConnections = 300

// New creates a Service object
func New(client *http.Client) (*Service, error) {
	if client == nil {
		return nil, errors.New("client is nil")
	}
	s := &Service{client: client, BasePath: basePath}
	s.Sheets = NewSheetsService(s)
	return s, nil
}

type Service struct {
	client   *http.Client
	BasePath string

	Sheets *SheetsService
}

func (s *Service) fetchAndUnmarshal(url string, v interface{}) error {
	resp, err := s.client.Get(url)
	if err != nil {
		return err
	}
	body, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return err
	}
	err = xml.Unmarshal(body, v)
	if err != nil {
		return err
	}
	return nil
}

func NewSheetsService(s *Service) *SheetsService {
	ss := &SheetsService{s: s}
	return ss
}

type SheetsService struct {
	s *Service
}

// Worksheets returns the Worksheets object of the client
func (ss *SheetsService) Worksheets(key string) (*Worksheets, error) {
	url := fmt.Sprintf("%s/feeds/worksheets/%s/private/full", ss.s.BasePath, key)
	worksheets := &Worksheets{ss: ss}
	err := ss.s.fetchAndUnmarshal(url, &worksheets)
	if err != nil {
		return nil, err
	}
	return worksheets, nil
}

type Worksheets struct {
	ss *SheetsService

	XMLName xml.Name     `xml:"feed"`
	Title   string       `xml:"title"`
	Links   []Link       `xml:"link"`
	Entries []*Worksheet `xml:"entry"`
}

// AddWorksheet adds worksheet
func (ws *Worksheets) AddWorksheet(title string, rowCount, colCount int) error {

	var url string
	for _, l := range ws.Links {
		if l.Rel == "http://schemas.google.com/g/2005#post" {
			url = l.Href
			break
		}
	}
	if url == "" {
		return errors.New("URL not found")
	}

	entry := `<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gs="http://schemas.google.com/spreadsheets/2006">` +
		"<title>" + title + "</title>" +
		fmt.Sprintf("<gs:rowCount>%d</gs:rowCount>", rowCount) +
		fmt.Sprintf("<gs:colCount>%d</gs:colCount>", colCount) +
		`</entry>`

	req, err := http.NewRequest("POST", url, strings.NewReader(entry))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/atom+xml;charset=utf-8")

	resp, err := ws.ss.s.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	added := &Worksheet{}
	err = xml.Unmarshal(body, added)
	if err != nil {
		return err
	}
	ws.Entries = append(ws.Entries, added)

	return nil
}

// Get returns the worksheet of passed index
func (w *Worksheets) Get(i int) (*Worksheet, error) {
	if len(w.Entries) <= i {
		return nil, errors.New(fmt.Sprintf("worksheet of index %d was not found", i))
	}
	ws := w.Entries[i]
	err := ws.build(w)
	if err != nil {
		return nil, err
	}
	return ws, nil
}

// FindById returns the worksheet of passed id
func (w *Worksheets) FindById(id string) (*Worksheet, error) {
	var validID = regexp.MustCompile(fmt.Sprintf("%s$", id))
	for _, e := range w.Entries {
		if validID.MatchString(e.Id) {
			err := e.build(w)
			if err != nil {
				return nil, err
			}
			return e, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("worksheet of id %s was not found", id))
}

// FindByTitle returns the worksheet of passed title
func (w *Worksheets) FindByTitle(title string) (*Worksheet, error) {
	for _, e := range w.Entries {
		if e.Title == title {
			err := e.build(w)
			if err != nil {
				return nil, err
			}
			return e, nil
		}
	}
	return nil, errors.New(fmt.Sprintf("worksheet of title %s was not found", title))
}

// ExistsTitled returns whether there is a sheet titlted given parameter
func (w *Worksheets) ExistsTitled(title string) bool {
	for _, e := range w.Entries {
		if e.Title == title {
			return true
		}
	}
	return false
}

type Worksheet struct {
	Id      string    `xml:"id"`
	Updated time.Time `xml:"updated"`
	Title   string    `xml:"title"`
	Content string    `xml:"content"`
	Links   []Link    `xml:"link"`

	ws            *Worksheets
	CellsFeed     string
	EditLink      string
	CSVLink       string
	MaxRowNum     int
	MaxColNum     int
	Rows          [][]*Cell
	modifiedCells []*Cell
}

// DocsURL is a URL to the google docs spreadsheet (human readable)
func (w *Worksheet) DocsURL() string {
	r := regexp.MustCompile(`/d/(.*?)/export\?gid=(\d+)`)
	group := r.FindSubmatch([]byte(w.CSVLink))
	if len(group) < 3 {
		return ""
	}
	key := string(group[1])
	gid := string(group[2])
	return fmt.Sprintf("%s/d/%s/edit#gid=%s", docBase, key, gid)
}

func (ws *Worksheet) build(w *Worksheets) error {

	ws.ws = w

	for _, l := range ws.Links {
		switch l.Rel {
		case "http://schemas.google.com/spreadsheets/2006#cellsfeed":
			ws.CellsFeed = l.Href
		case "edit":
			ws.EditLink = l.Href
		case "http://schemas.google.com/spreadsheets/2006#exportcsv":
			ws.CSVLink = l.Href
		default:
		}
	}

	var cells *Cells
	err := ws.ws.ss.s.fetchAndUnmarshal(fmt.Sprintf("%s?return-empty=true", ws.CellsFeed), &cells)
	if err != nil {
		return err
	}
	ws.modifiedCells = make([]*Cell, 0)

	for _, cell := range cells.Entries {
		cell.ws = ws
		if cell.Pos.Row > ws.MaxRowNum {
			ws.MaxRowNum = cell.Pos.Row
		}
		if cell.Pos.Col > ws.MaxColNum {
			ws.MaxColNum = cell.Pos.Col
		}
	}
	rows := make([][]*Cell, ws.MaxRowNum)
	for i := 0; i < ws.MaxRowNum; i++ {
		rows[i] = make([]*Cell, ws.MaxColNum)
	}
	for _, cell := range cells.Entries {
		rows[cell.Pos.Row-1][cell.Pos.Col-1] = cell
	}
	ws.Rows = rows

	return nil
}

func (ws *Worksheet) Destroy() error {
	req, err := http.NewRequest("DELETE", ws.EditLink, nil)
	if err != nil {
		return err
	}

	resp, err := ws.ws.ss.s.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	for i, e := range ws.ws.Entries {
		if e.Id == ws.Id {
			ws.ws.Entries = append(ws.ws.Entries[:i], ws.ws.Entries[i+1:]...)
			break
		}
	}

	return nil
}

// Synchronize saves the modified cells
func (ws *Worksheet) Synchronize() error {

	var wg sync.WaitGroup
	c := make(chan int, MaxConnections)
	mCells := ws.modifiedCells
	target := []*Cell{}
	errors := []error{}
	for len(mCells) > 0 {
		wg.Add(1)
		if len(mCells) >= SyncCellsAtOnce {
			target = mCells[:SyncCellsAtOnce]
			mCells = mCells[SyncCellsAtOnce:]
		} else {
			target = mCells[:len(mCells)]
			mCells = []*Cell{}
		}
		go func(s chan int, cells []*Cell) {
			defer wg.Done()
			s <- 1
			err := ws.synchronize(cells)
			if err != nil {
				errors = append(errors, err)
			}
			<-s
		}(c, target)
	}
	wg.Wait()
	close(c)
	if len(errors) > 0 {
		return errors[0]
	}
	return nil
}

type GSCell struct {
	XMLName    xml.Name `xml:"gs:cell"`
	InputValue string   `xml:"inputValue,attr"`
	Row        int      `xml:"row,attr"`
	Col        int      `xml:"col,attr"`
}

func (ws *Worksheet) synchronize(cells []*Cell) error {
	feed := `
    <feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:batch="http://schemas.google.com/gdata/batch"
      xmlns:gs="http://schemas.google.com/spreadsheets/2006">
  `
	feed += fmt.Sprintf("<id>%s</id>", ws.CellsFeed)
	for _, mc := range cells {
		feed += `<entry>`
		feed += fmt.Sprintf("<batch:id>%d, %d</batch:id>", mc.Pos.Row, mc.Pos.Col)
		feed += `<batch:operation type="update"/>`
		feed += fmt.Sprintf("<id>%s</id>", mc.Id)
		feed += fmt.Sprintf("<link rel=\"edit\" type=\"application/atom+xml\" href=\"%s\"/>", mc.EditLink())
		cell := GSCell{InputValue: mc.Content, Row: mc.Pos.Row, Col: mc.Pos.Col}
		b, err := xml.Marshal(&cell)
		if err != nil {
			return err
		}
		feed += string(b)
		feed += `</entry>`
	}
	feed += `</feed>`
	url := fmt.Sprintf("%s/batch", ws.CellsFeed)
	req, err := http.NewRequest("POST", url, strings.NewReader(feed))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/atom+xml;charset=utf-8")

	resp, err := ws.ws.ss.s.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	return nil
}

type Link struct {
	Rel  string `xml:"rel,attr"`
	Type string `xml:"type,attr"`
	Href string `xml:"href,attr"`
}

type Cells struct {
	XMLName xml.Name `xml:"feed"`
	Title   string   `xml:"title"`
	Entries []*Cell  `xml:"entry"`
}

type Cell struct {
	ws      *Worksheet
	Id      string    `xml:"id"`
	Updated time.Time `xml:"updated"`
	Title   string    `xml:"title"`
	Content string    `xml:"content"`
	Links   []Link    `xml:"link"`
	Pos     struct {
		Row int `xml:"row,attr"`
		Col int `xml:"col,attr"`
	} `xml:"cell"`
}

func (c *Cell) Update(content string) {
	c.Content = content
	for _, mc := range c.ws.modifiedCells {
		if mc.Id == c.Id {
			return
		}
	}
	c.ws.modifiedCells = append(c.ws.modifiedCells, c)
}

func (c *Cell) FastUpdate(content string) {
	c.Content = content
	c.ws.modifiedCells = append(c.ws.modifiedCells, c)
}

func (c *Cell) EditLink() string {
	for _, l := range c.Links {
		if l.Rel == "edit" {
			return l.Href
		}
	}
	return ""
}
<|endoftext|>


<commit_before>// Main package wraps sprite_sass tool for use with the command line
// See -h for list of available options
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime/pprof"
	"strings"

	"github.com/drewwells/sprite_sass/context"
	"github.com/drewwells/spritewell"

	sprite "github.com/drewwells/sprite_sass"
)

const version = `v0.2.1`

var (
	Dir, Gen, Input, Includes string
	MainFile, Style           string
	Comments                  bool
	cpuprofile                string
	Help, ShowVersion         bool
	BuildDir                  string
)

func init() {
	flag.StringVar(&BuildDir, "b", "", "Build Directory")
	flag.StringVar(&Includes, "p", "", "SASS import path")
	flag.BoolVar(&Help, "help", false, "this help")
	flag.BoolVar(&Help, "h", false, "this help")
	flag.StringVar(&Dir, "dir", "", "Image directory")
	flag.StringVar(&Dir, "d", "", "Image directory")
	flag.StringVar(&Gen, "gen", ".", "Directory for generated images")
	flag.StringVar(&Style, "style", "nested", "CSS nested style")
	flag.StringVar(&Style, "s", "nested", "CSS nested style")
	flag.BoolVar(&Comments, "comment", true, "Turn on source comments")
	flag.BoolVar(&Comments, "c", true, "Turn on source comments")
	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
	flag.BoolVar(&ShowVersion, "version", false, "Show the app version")
}

func main() {
	flag.Parse()

	if ShowVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	// Profiling code
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Starting profiler")
		pprof.StartCPUProfile(f)
		defer func() {
			pprof.StopCPUProfile()
			err := f.Close()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Stopping Profiller")
		}()
	}

	for _, v := range flag.Args() {
		if strings.HasPrefix(v, "-") {
			log.Fatalf("Please specify flags before other arguments: %s", v)
		}
	}

	if Help {
		fmt.Println("Please specify input filepath.")
		fmt.Println("\nAvailable options:")
		flag.PrintDefaults()
		return
	}

	style, ok := context.Style[Style]

	if !ok {
		style = context.NESTED_STYLE
	}

	if len(flag.Args()) == 0 {
		// Read from stdin
		log.Print("Reading from stdin, -h for help")
		out := os.Stdout
		in := os.Stdin

		var pout bytes.Buffer
		ctx := context.Context{}
		err := startParser(ctx, in, &pout, "")
		if err != nil {
			log.Println(err)
		}
		err = ctx.Compile(&pout, out)

		if err != nil {
			log.Println(err)
		}
	}

	SpriteCache := spritewell.SafeImageMap{
		M: make(map[string]spritewell.ImageList, 100)}
	ImageCache := spritewell.SafeImageMap{
		M: make(map[string]spritewell.ImageList, 100)}

	for _, f := range flag.Args() {
		// Remove partials
		if strings.HasPrefix(filepath.Base(f), "_") {
			continue
		}
		// log.Println("Open:", f)

		// If no imagedir specified, assume relative to the input file
		if Dir == "" {
			Dir = filepath.Dir(f)
		}
		var (
			out  io.WriteCloser
			fout string
		)
		if BuildDir != "" {
			// Build output file based off build directory and input filename
			rel, _ := filepath.Rel(Includes, filepath.Dir(f))
			filename := strings.Replace(filepath.Base(f), ".scss", ".css", 1)
			fout = filepath.Join(BuildDir, rel, filename)
		} else {
			out = os.Stdout
		}

		ctx := context.Context{
			// TODO: Most of these fields are no longer used
			Sprites:     SpriteCache,
			Imgs:        ImageCache,
			OutputStyle: style,
			ImageDir:    Dir,
			// Assumption that output is a file
			BuildDir:     BuildDir,
			GenImgDir:    Gen,
			MainFile:     f,
			Comments:     Comments,
			IncludePaths: []string{filepath.Dir(f)},
		}
		if Includes != "" {
			ctx.IncludePaths = append(ctx.IncludePaths,
				strings.Split(Includes, ",")...)
		}
		fRead, err := os.Open(f)
		defer fRead.Close()
		if err != nil {
			log.Fatal(err)
		}
		if fout != "" {
			dir := filepath.Dir(fout)
			err := os.MkdirAll(dir, 0755)
			if err != nil {
				log.Fatalf("Failed to create directory: %s", dir)
			}

			out, err = os.Create(fout)
			defer out.Close()
			if err != nil {
				log.Fatalf("Failed to create file: %s", f)
			}
			// log.Println("Created:", fout)
		}

		var pout bytes.Buffer
		err = startParser(ctx, fRead, &pout, filepath.Dir(Input))
		if err != nil {
			log.Println(err)
			continue
		}
		err = ctx.Compile(&pout, out)

		if err != nil {
			log.Println(err)
		}
	}
}

func startParser(ctx context.Context, in io.Reader, out io.Writer, pkgdir string) error {
	// Run the sprite_sass parser prior to passing to libsass
	parser := sprite.Parser{
		ImageDir:  ctx.ImageDir,
		Includes:  ctx.IncludePaths,
		BuildDir:  ctx.BuildDir,
		GenImgDir: ctx.GenImgDir,
	}
	// Save reference to parser in context
	bs, err := parser.Start(in, pkgdir)
	if err != nil {
		return err
	}
	out.Write(bs)
	return err
}
<commit_msg>mkdirall prior to attempting to run on it<commit_after>// Main package wraps sprite_sass tool for use with the command line
// See -h for list of available options
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"runtime/pprof"
	"strings"

	"github.com/drewwells/sprite_sass/context"
	"github.com/drewwells/spritewell"

	sprite "github.com/drewwells/sprite_sass"
)

const version = `v0.2.1`

var (
	Dir, Gen, Input, Includes string
	MainFile, Style           string
	Comments                  bool
	cpuprofile                string
	Help, ShowVersion         bool
	BuildDir                  string
)

func init() {
	flag.StringVar(&BuildDir, "b", "", "Build Directory")
	flag.StringVar(&Includes, "p", "", "SASS import path")
	flag.BoolVar(&Help, "help", false, "this help")
	flag.BoolVar(&Help, "h", false, "this help")
	flag.StringVar(&Dir, "dir", "", "Image directory")
	flag.StringVar(&Dir, "d", "", "Image directory")
	flag.StringVar(&Gen, "gen", ".", "Directory for generated images")
	flag.StringVar(&Style, "style", "nested", "CSS nested style")
	flag.StringVar(&Style, "s", "nested", "CSS nested style")
	flag.BoolVar(&Comments, "comment", true, "Turn on source comments")
	flag.BoolVar(&Comments, "c", true, "Turn on source comments")
	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
	flag.BoolVar(&ShowVersion, "version", false, "Show the app version")
}

func main() {
	flag.Parse()

	if ShowVersion {
		fmt.Println(version)
		os.Exit(0)
	}

	// Profiling code
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Starting profiler")
		pprof.StartCPUProfile(f)
		defer func() {
			pprof.StopCPUProfile()
			err := f.Close()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Stopping Profiller")
		}()
	}

	for _, v := range flag.Args() {
		if strings.HasPrefix(v, "-") {
			log.Fatalf("Please specify flags before other arguments: %s", v)
		}
	}

	if Help {
		fmt.Println("Please specify input filepath.")
		fmt.Println("\nAvailable options:")
		flag.PrintDefaults()
		return
	}

	if Gen != "" {
		err := os.MkdirAll(Gen, 0755)
		if err != nil {
			log.Fatal(err)
		}
	}

	style, ok := context.Style[Style]

	if !ok {
		style = context.NESTED_STYLE
	}

	if len(flag.Args()) == 0 {
		// Read from stdin
		log.Print("Reading from stdin, -h for help")
		out := os.Stdout
		in := os.Stdin

		var pout bytes.Buffer
		ctx := context.Context{}
		err := startParser(ctx, in, &pout, "")
		if err != nil {
			log.Println(err)
		}
		err = ctx.Compile(&pout, out)

		if err != nil {
			log.Println(err)
		}
	}

	SpriteCache := spritewell.SafeImageMap{
		M: make(map[string]spritewell.ImageList, 100)}
	ImageCache := spritewell.SafeImageMap{
		M: make(map[string]spritewell.ImageList, 100)}

	for _, f := range flag.Args() {
		// Remove partials
		if strings.HasPrefix(filepath.Base(f), "_") {
			continue
		}
		// log.Println("Open:", f)

		// If no imagedir specified, assume relative to the input file
		if Dir == "" {
			Dir = filepath.Dir(f)
		}
		var (
			out  io.WriteCloser
			fout string
		)
		if BuildDir != "" {
			// Build output file based off build directory and input filename
			rel, _ := filepath.Rel(Includes, filepath.Dir(f))
			filename := strings.Replace(filepath.Base(f), ".scss", ".css", 1)
			fout = filepath.Join(BuildDir, rel, filename)
		} else {
			out = os.Stdout
		}

		ctx := context.Context{
			// TODO: Most of these fields are no longer used
			Sprites:     SpriteCache,
			Imgs:        ImageCache,
			OutputStyle: style,
			ImageDir:    Dir,
			// Assumption that output is a file
			BuildDir:     BuildDir,
			GenImgDir:    Gen,
			MainFile:     f,
			Comments:     Comments,
			IncludePaths: []string{filepath.Dir(f)},
		}
		if Includes != "" {
			ctx.IncludePaths = append(ctx.IncludePaths,
				strings.Split(Includes, ",")...)
		}
		fRead, err := os.Open(f)
		defer fRead.Close()
		if err != nil {
			log.Fatal(err)
		}
		if fout != "" {
			dir := filepath.Dir(fout)
			err := os.MkdirAll(dir, 0755)
			if err != nil {
				log.Fatalf("Failed to create directory: %s", dir)
			}

			out, err = os.Create(fout)
			defer out.Close()
			if err != nil {
				log.Fatalf("Failed to create file: %s", f)
			}
			// log.Println("Created:", fout)
		}

		var pout bytes.Buffer
		err = startParser(ctx, fRead, &pout, filepath.Dir(Input))
		if err != nil {
			log.Println(err)
			continue
		}
		err = ctx.Compile(&pout, out)

		if err != nil {
			log.Println(err)
		}
	}
}

func startParser(ctx context.Context, in io.Reader, out io.Writer, pkgdir string) error {
	// Run the sprite_sass parser prior to passing to libsass
	parser := sprite.Parser{
		ImageDir:  ctx.ImageDir,
		Includes:  ctx.IncludePaths,
		BuildDir:  ctx.BuildDir,
		GenImgDir: ctx.GenImgDir,
	}
	// Save reference to parser in context
	bs, err := parser.Start(in, pkgdir)
	if err != nil {
		return err
	}
	out.Write(bs)
	return err
}
<|endoftext|>