<commit_before>package main

import (
        "./comms"
        "./EncryptionEngine"

        "flag"

        "net"
        "fmt"
        "io"

        "net/http"
        "net/url"
        "html"
        "log"
        "encoding/hex"
        "encoding/base64"
        "time"
       )

type State struct {
    Kc [16]byte
    clk uint32
    BD_ADDR [6]byte
    is_master bool
}

func server_main(s *State) net.Conn {
    ln, err := net.Listen("tcp", ":8080")

    if err != nil {
        fmt.Println("net.Listen failed:", err)
    }

    conn, err := ln.Accept()

    fmt.Printf("Accepted Connection\n")

    if err != nil {
        fmt.Println("ln.Accept failed:", err)
    }

    return conn
}

func client_main() net.Conn {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")

    if err != nil {
        fmt.Println("net.Dial failed:", err)
    }

    return conn
}

func is_bigger(ours, theirs [6]byte) bool{
    //TODO: This... Note the bit ordering..
    return ours[0] > theirs[0]
}

func receiver(conn io.ReadWriter, s *State) {
  LOOP:
  for {
        packet_type := comms.Recv_packet(conn)

        switch packet_type {
            case 0:
                OTHER_BD_ADDR := comms.Recv_neg(conn)
                if !is_bigger(s.BD_ADDR, OTHER_BD_ADDR) {
                    s.BD_ADDR = OTHER_BD_ADDR
                    s.is_master = true
                } else {
                    s.is_master = false
                }
            case 1:
                s.clk, _, s.Kc = comms.Recv_init(conn)
            case 2:
                var msg []byte
                s.clk, msg = comms.Recv_data(conn)

                fmt.Println("Recieved: ", string(msg))

                keyStream := EncryptionEngine.GetKeyStream(s.Kc, s.BD_ADDR, s.clk, len(msg))
                decrypted_msg := EncryptionEngine.Encrypt(msg, keyStream)

                fmt.Println("Decypted as: ", string(decrypted_msg))

                ciphertext_b64 := base64.StdEncoding.EncodeToString(msg)
                keystream_b64 := base64.StdEncoding.EncodeToString(keyStream)

                var role string

                if s.is_master {
                    role = "master"
                } else {
                    role = "slave"
                }

                _, err := http.PostForm("http://127.0.0.1:8000/log?role=" + role,
                        url.Values{
                        "is_receiving" : { "true" },
                        "keystream" : { keystream_b64 },
                        "ciphertext" : { ciphertext_b64 },
                        "plaintext" : { string(decrypted_msg) },
                        "timestamp" : { time.Now().Format("Jan _2 15:04:05") },
                        })

                if err != nil {
                    fmt.Println("There was an http error: ", err)
                }

            case 99: break LOOP
        }
    }
}

func main() {
    isServerPtr := flag.Bool("server", false, "Run in server mode?")
    flag.Parse()
    fmt.Println("Is Server: ", *isServerPtr)

    var conn net.Conn
    var p string

    var state State

    state.Kc = [16]byte{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    state.clk = 0

    if *isServerPtr {
        state.BD_ADDR = [6]byte{255, 255, 255, 255, 255, 255}
        conn = server_main(&state)
        p = ":8888"
    } else {
        state.BD_ADDR = [6]byte{0, 0, 0, 0, 0, 0}
        conn = client_main()
        p = ":6666"
    }

    fmt.Println("Running on ", p)

    comms.Send_neg(conn, state.BD_ADDR)

    go receiver(conn, &state)

    http.HandleFunc("/Kc", func(w http.ResponseWriter, r *http.Request) {
        var Kc_str string
        if r.Method == "POST" {
            r.ParseForm()
            Kc_str = r.PostForm["Kc"][0]
            fmt.Println("Kc String: ", Kc_str)
        }

        Kc, err := hex.DecodeString(Kc_str)

        if err != nil || len(Kc) < 16 {
            fmt.Println("Invalid key!")
            return
        }

        fmt.Println("Kc: ", Kc)

        for i:= 0; i < 16; i++ {
            state.Kc[i] = Kc[i]
        }

    })

    http.HandleFunc("/message", func(w http.ResponseWriter, r *http.Request) {

        if r.Method == "POST" {
            state.clk++
            r.ParseForm()

            pt := []byte(r.PostForm["plaintext"][0])
            fmt.Println("Sending: ", string(pt))

            fmt.Fprintf(w, html.EscapeString("Sending packet"))

            keyStream := EncryptionEngine.GetKeyStream(
                state.Kc, state.BD_ADDR, state.clk, len(pt))
            msg := EncryptionEngine.Encrypt(pt, keyStream)

            comms.Send_data(conn, state.clk, msg)

            fmt.Println("Encrypted as: ", string(msg))

            keystream_b64 := base64.StdEncoding.EncodeToString(keyStream)
            ciphertext_b64 := base64.StdEncoding.EncodeToString(msg)

            var role string

            if state.is_master {
                role = "master"
            } else {
                role = "slave"
            }

            _, err := http.PostForm("http://127.0.0.1:8000/log?role=" + role,
                    url.Values{
                    "is_receiving" : { "false" },
                    "keystream" : { keystream_b64 },
                    "ciphertext" : { ciphertext_b64 },
                    "plaintext" : { string(pt) },
                    "timestamp" : { time.Now().Format("Jan _2 15:04:05") },
                    })

            if err != nil {
                    fmt.Println("There was an http error: ", err)
            }
        }
    })

    log.Fatal(http.ListenAndServe(p, nil))

    conn.Close()
}
<commit_msg>Added clock to log<commit_after>package main

import (
        "./comms"
        "./EncryptionEngine"

        "flag"

        "net"
        "fmt"
        "io"

        "net/http"
        "net/url"
        "html"
        "log"
        "encoding/hex"
        "encoding/base64"
        "time"
       )

type State struct {
    Kc [16]byte
    clk uint32
    BD_ADDR [6]byte
    is_master bool
}

func server_main(s *State) net.Conn {
    ln, err := net.Listen("tcp", ":8080")

    if err != nil {
        fmt.Println("net.Listen failed:", err)
    }

    conn, err := ln.Accept()

    fmt.Printf("Accepted Connection\n")

    if err != nil {
        fmt.Println("ln.Accept failed:", err)
    }

    return conn
}

func client_main() net.Conn {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")

    if err != nil {
        fmt.Println("net.Dial failed:", err)
    }

    return conn
}

func is_bigger(ours, theirs [6]byte) bool{
    //TODO: This... Note the bit ordering..
    return ours[0] > theirs[0]
}

func receiver(conn io.ReadWriter, s *State) {
  LOOP:
  for {
        packet_type := comms.Recv_packet(conn)

        switch packet_type {
            case 0:
                OTHER_BD_ADDR := comms.Recv_neg(conn)
                if !is_bigger(s.BD_ADDR, OTHER_BD_ADDR) {
                    s.BD_ADDR = OTHER_BD_ADDR
                    s.is_master = true
                } else {
                    s.is_master = false
                }
            case 1:
                s.clk, _, s.Kc = comms.Recv_init(conn)
            case 2:
                var msg []byte
                s.clk, msg = comms.Recv_data(conn)

                fmt.Println("Recieved: ", string(msg))

                keyStream := EncryptionEngine.GetKeyStream(s.Kc, s.BD_ADDR, s.clk, len(msg))
                decrypted_msg := EncryptionEngine.Encrypt(msg, keyStream)

                fmt.Println("Decypted as: ", string(decrypted_msg))

                ciphertext_b64 := base64.StdEncoding.EncodeToString(msg)
                keystream_b64 := base64.StdEncoding.EncodeToString(keyStream)

                var role string

                if s.is_master {
                    role = "master"
                } else {
                    role = "slave"
                }

                _, err := http.PostForm("http://127.0.0.1:8000/log?role=" + role,
                        url.Values{
                        "CLK" : { string(s.clk) },
                        "is_receiving" : { "true" },
                        "keystream" : { keystream_b64 },
                        "ciphertext" : { ciphertext_b64 },
                        "plaintext" : { string(decrypted_msg) },
                        "timestamp" : { time.Now().Format("Jan _2 15:04:05") },
                        })

                if err != nil {
                    fmt.Println("There was an http error: ", err)
                }

            case 99: break LOOP
        }
    }
}

func main() {
    isServerPtr := flag.Bool("server", false, "Run in server mode?")
    flag.Parse()
    fmt.Println("Is Server: ", *isServerPtr)

    var conn net.Conn
    var p string

    var state State

    state.Kc = [16]byte{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    state.clk = 0

    if *isServerPtr {
        state.BD_ADDR = [6]byte{255, 255, 255, 255, 255, 255}
        conn = server_main(&state)
        p = ":8888"
    } else {
        state.BD_ADDR = [6]byte{0, 0, 0, 0, 0, 0}
        conn = client_main()
        p = ":6666"
    }

    fmt.Println("Running on ", p)

    comms.Send_neg(conn, state.BD_ADDR)

    go receiver(conn, &state)

    http.HandleFunc("/Kc", func(w http.ResponseWriter, r *http.Request) {
        var Kc_str string
        if r.Method == "POST" {
            r.ParseForm()
            Kc_str = r.PostForm["Kc"][0]
            fmt.Println("Kc String: ", Kc_str)
        }

        Kc, err := hex.DecodeString(Kc_str)

        if err != nil || len(Kc) < 16 {
            fmt.Println("Invalid key!")
            return
        }

        fmt.Println("Kc: ", Kc)

        for i:= 0; i < 16; i++ {
            state.Kc[i] = Kc[i]
        }

    })

    http.HandleFunc("/message", func(w http.ResponseWriter, r *http.Request) {

        if r.Method == "POST" {
            state.clk++
            r.ParseForm()

            pt := []byte(r.PostForm["plaintext"][0])
            fmt.Println("Sending: ", string(pt))

            fmt.Fprintf(w, html.EscapeString("Sending packet"))

            keyStream := EncryptionEngine.GetKeyStream(
                state.Kc, state.BD_ADDR, state.clk, len(pt))
            msg := EncryptionEngine.Encrypt(pt, keyStream)

            comms.Send_data(conn, state.clk, msg)

            fmt.Println("Encrypted as: ", string(msg))

            keystream_b64 := base64.StdEncoding.EncodeToString(keyStream)
            ciphertext_b64 := base64.StdEncoding.EncodeToString(msg)

            var role string

            if state.is_master {
                role = "master"
            } else {
                role = "slave"
            }

            _, err := http.PostForm("http://127.0.0.1:8000/log?role=" + role,
                    url.Values{
                    "CLK" : { string(state.clk) },
                    "is_receiving" : { "false" },
                    "keystream" : { keystream_b64 },
                    "ciphertext" : { ciphertext_b64 },
                    "plaintext" : { string(pt) },
                    "timestamp" : { time.Now().Format("Jan _2 15:04:05") },
                    })

            if err != nil {
                    fmt.Println("There was an http error: ", err)
            }
        }
    })

    log.Fatal(http.ListenAndServe(p, nil))

    conn.Close()
}
<|endoftext|>


<commit_before>//
// markup.go
// memeposting markup parser
//
package srnd

import (
	"github.com/mvdan/xurls"
	"html"
	"regexp"
	"strings"
)

// copypasted from https://stackoverflow.com/questions/161738/what-is-the-best-regular-expression-to-check-if-a-string-is-a-valid-url
// var re_external_link = regexp.MustCompile(`((?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?)`);
var re_external_link = xurls.Strict
var re_backlink = regexp.MustCompile(`>> ?([0-9a-f]+)`)

// parse backlink
func backlink(word string) (markup string) {
	re := regexp.MustCompile(`>> ?([0-9a-f]+)`)
	link := re.FindString(word)
	if len(link) > 2 {
		link = strings.Trim(link[2:], " ")
		if len(link) > 2 {
			url := template.findLink(link)
			if len(url) == 0 {
				return ">>" + link + ""
			}
			// backlink exists
			return `<a href="` + url + `">>>` + link + ""
		} else {
			return html.EscapeString(word)
		}
	}
	return html.EscapeString(word)
}

func formatline(line string) (markup string) {
	line = strings.Trim(line, "\t\r\n ")
	if len(line) > 0 {
		if strings.HasPrefix(line, ">") && !(strings.HasPrefix(line, ">>") && re_backlink.MatchString(strings.Split(line, " ")[0])) {
			// le ebin meme arrows
			markup += ""
			markup += html.EscapeString(line)
			markup += ""
		} else if strings.HasPrefix(line, "==") && strings.HasSuffix(line, "==") {
			// redtext
			markup += ""
			markup += html.EscapeString(line[2 : len(line)-2])
			markup += ""
		} else {
			// regular line
			// for each word
			for _, word := range strings.Split(line, " ") {
				// check for backlink
				if re_backlink.MatchString(word) {
					markup += backlink(word)
				} else {
					// linkify as needed
					word = html.EscapeString(word)
					markup += re_external_link.ReplaceAllString(word, `<a href="$1">$1</a>`)
				}
				markup += " "
			}
		}
	}
	markup += "
"
	return
}

// format lines inside a code tag
func formatcodeline(line string) (markup string) {
	markup += html.EscapeString(line)
	markup += "\n"
	return
}

func memeposting(src string) (markup string) {
	found_tag := false
	tag_content := ""
	tag := ""
	// for each line...
	for _, line := range strings.Split(src, "\n") {
		// beginning of code tag ?
		if strings.Count(line, "[code]") > 0 {
			// yes there's a code tag
			found_tag = true
			tag = "code"
		} else if strings.Count(line, "[spoiler]") > 0 {
			// spoiler tag
			found_tag = true
			tag = "spoiler"
		} else if strings.Count(line, "[psy]") > 0 {
			// psy tag
			found_tag = true
			tag = "psy"
		}
		if found_tag {
			// collect content of tag
			tag_content += line + "\n"
			// end of our tag ?
			if strings.Count(line, "[/"+tag+"]") == 1 {
				// yah
				found_tag = false
				var tag_open, tag_close string
				if tag == "code" {
					tag_open = "
"
					tag_close = "
"
				} else if tag == "spoiler" {
					tag_open = ""
					tag_close = ""
				} else if tag == "psy" {
					tag_open = ""
					tag_close = ""
				}
				markup += tag_open
				// remove open tag, only once so we can have a code tag verbatum inside
				tag_content = strings.Replace(tag_content, "["+tag+"]", "", 1)
				// remove all close tags, should only have 1
				tag_content = strings.Replace(tag_content, "[/"+tag+"]", "", -1)
				// make into lines
				for _, tag_line := range strings.Split(tag_content, "\n") {
					if tag == "code" {
						markup += formatcodeline(tag_line)
					} else {
						markup += formatline(tag_line)
					}
				}
				// close pre tag
				markup += tag_close
				// reset content buffer
				tag_content = ""
			}
			// next line
			continue
		}
		// format line regularlly
		markup += formatline(line)
	}
	// flush the rest of an incomplete code tag
	for _, line := range strings.Split(tag_content, "\n") {
		markup += formatline(line)
	}
	return
}
<commit_msg>remove tag based markup formatting<commit_after>//
// markup.go
// memeposting markup parser
//
package srnd

import (
	"github.com/mvdan/xurls"
	"html"
	"regexp"
	"strings"
)

// copypasted from https://stackoverflow.com/questions/161738/what-is-the-best-regular-expression-to-check-if-a-string-is-a-valid-url
// var re_external_link = regexp.MustCompile(`((?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?)`);
var re_external_link = xurls.Strict
var re_backlink = regexp.MustCompile(`>> ?([0-9a-f]+)`)

// parse backlink
func backlink(word string) (markup string) {
	re := regexp.MustCompile(`>> ?([0-9a-f]+)`)
	link := re.FindString(word)
	if len(link) > 2 {
		link = strings.Trim(link[2:], " ")
		if len(link) > 2 {
			url := template.findLink(link)
			if len(url) == 0 {
				return ">>" + link + ""
			}
			// backlink exists
			return `<a href="` + url + `">>>` + link + ""
		} else {
			return html.EscapeString(word)
		}
	}
	return html.EscapeString(word)
}

func formatline(line string) (markup string) {
	line = strings.Trim(line, "\t\r\n ")
	if len(line) > 0 {
		if strings.HasPrefix(line, ">") && !(strings.HasPrefix(line, ">>") && re_backlink.MatchString(strings.Split(line, " ")[0])) {
			// le ebin meme arrows
			markup += ""
			markup += html.EscapeString(line)
			markup += ""
		} else if strings.HasPrefix(line, "==") && strings.HasSuffix(line, "==") {
			// redtext
			markup += ""
			markup += html.EscapeString(line[2 : len(line)-2])
			markup += ""
		} else {
			// regular line
			// for each word
			for _, word := range strings.Split(line, " ") {
				// check for backlink
				if re_backlink.MatchString(word) {
					markup += backlink(word)
				} else {
					// linkify as needed
					word = html.EscapeString(word)
					markup += re_external_link.ReplaceAllString(word, `<a href="$1">$1</a>`)
				}
				markup += " "
			}
		}
	}
	markup += "
"
	return
}

func memeposting(src string) (markup string) {
	for _, line := range strings.Split(src, "\n") {
		markup += formatline(line)
	}
	return
}
<|endoftext|>


<commit_before>package cert

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"time"

	"github.com/golang/go/src/pkg/text/template"
)

const defaultTempl = `{{range .}}DomainName: {{.DomainName}}
Issuer:     {{.Issuer}}
Start:      {{.Start}}
End:        {{.End}}
CommonName: {{.CommonName}}
SANs:       {{.SANs}}

{{end}}
`

const markdownTempl = `ドメイン名 | 発行元 | 有効期間の開始 | 有効期間の終了 | CN | SANs
--- | --- | --- | --- | --- | ---
{{range .}}{{.DomainName}} | {{.Issuer}} | {{.Start}} | {{.End}} | {{.CommonName}} | {{range .SANs}}{{.}}
{{end}} {{end}}
`

type Certs []*Cert

type Cert struct {
	DomainName string
	Issuer     string
	CommonName string
	SANs       []string
	Start      string
	End        string
}

func NewCerts(s []string) (Certs, error) {
	if len(s) < 1 {
		return nil, fmt.Errorf("ドメイン名をひとつ以上指定してください。")
	}
	certs := Certs{}
	for _, d := range s[:] {
		c, err := NewCert(d)
		if err != nil {
			return nil, err
		}
		certs = append(certs, c)
	}
	return certs, nil
}

func (certs Certs) String() string {
	var b bytes.Buffer
	t, err := template.New("default").Parse(defaultTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs); err != nil {
		panic(err)
	}
	return b.String()
}

func (certs Certs) Markdown() string {
	var b bytes.Buffer
	t, err := template.New("markdown").Parse(markdownTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs); err != nil {
		panic(err)
	}
	return b.String()
}

func NewCert(d string) (*Cert, error) {
	conn, err := tls.Dial("tcp", d+":443", &tls.Config{})
	if err != nil {
		return nil, err
	}
	cert := conn.ConnectionState().PeerCertificates[0]
	conn.Close()
	return &Cert{
		DomainName: d,
		Issuer:     cert.Issuer.Organization[0],
		CommonName: cert.Subject.CommonName,
		SANs:       cert.DNSNames,
		Start:      cert.NotBefore.In(time.Local).Format("2006/01/02 15:04:05"),
		End:        cert.NotAfter.In(time.Local).Format("2006/01/02 15:04:05"),
	}, nil
}
<commit_msg>English<commit_after>package cert

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"time"

	"github.com/golang/go/src/pkg/text/template"
)

const defaultTempl = `{{range .}}DomainName: {{.DomainName}}
Issuer:     {{.Issuer}}
NotBefore:  {{.NotBefore}}
NotAfter:   {{.NotAfter}}
CommonName: {{.CommonName}}
SANs:       {{.SANs}}

{{end}}
`

const markdownTempl = `DomainName | Issuer | NotBefore | NotAfter | CN | SANs
--- | --- | --- | --- | --- | ---
{{range .}}{{.DomainName}} | {{.Issuer}} | {{.NotBefore}} | {{.NotAfter}} | {{.CommonName}} | {{range .SANs}}{{.}}
{{end}} {{end}}
`

type Certs []*Cert

type Cert struct {
	DomainName string
	Issuer     string
	CommonName string
	SANs       []string
	NotBefore  string
	NotAfter   string
}

func NewCerts(s []string) (Certs, error) {
	if len(s) < 1 {
		return nil, fmt.Errorf("Input at least one domain name.")
	}
	certs := Certs{}
	for _, d := range s[:] {
		c, err := NewCert(d)
		if err != nil {
			return nil, err
		}
		certs = append(certs, c)
	}
	return certs, nil
}

func (certs Certs) String() string {
	var b bytes.Buffer
	t, err := template.New("default").Parse(defaultTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs); err != nil {
		panic(err)
	}
	return b.String()
}

func (certs Certs) Markdown() string {
	var b bytes.Buffer
	t, err := template.New("markdown").Parse(markdownTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs); err != nil {
		panic(err)
	}
	return b.String()
}

func NewCert(d string) (*Cert, error) {
	conn, err := tls.Dial("tcp", d+":443", &tls.Config{})
	if err != nil {
		return nil, err
	}
	cert := conn.ConnectionState().PeerCertificates[0]
	conn.Close()
	return &Cert{
		DomainName: d,
		Issuer:     cert.Issuer.Organization[0],
		CommonName: cert.Subject.CommonName,
		SANs:       cert.DNSNames,
		NotBefore:  cert.NotBefore.In(time.Local).Format("2006/01/02 15:04:05"),
		NotAfter:   cert.NotAfter.In(time.Local).Format("2006/01/02 15:04:05"),
	}, nil
}
<|endoftext|>


<commit_before>package cert

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"time"

	"github.com/golang/go/src/pkg/text/template"
)

const defaultTempl = `{{range .}}DomainName: {{.DomainName}}
Start:      {{.Start}}
End:        {{.End}}
CommonName: {{.CommonName}}
SANs:       {{.SANs}}

{{end}}
`

const markdownTempl = `ドメイン名 | 有効期間の開始 | 有効期間の終了 | CN | SANs
--- | --- | --- | --- | ---
{{range .}}{{.DomainName}} | {{.Start}} | {{.End}} | {{.CommonName}} | {{range .SANs}}{{.}}
{{end}} {{end}}
`

type Certs []*Cert

type Cert struct {
	DomainName string
	CommonName string
	SANs       []string
	Start      string
	End        string
}

func init() {
	loc, err := time.LoadLocation("Asia/Tokyo")
	if err != nil {
		loc = time.FixedZone("Asia/Tokyo", 9*60*60)
	}
	time.Local = loc
}

func NewCerts(s []string) (Certs, error) {
	if len(s) < 1 {
		return nil, fmt.Errorf("ドメイン名をひとつ以上指定してください。")
	}
	certs := Certs{}
	for _, d := range s[:] {
		c, err := NewCert(d)
		if err != nil {
			return nil, err
		}
		certs = append(certs, c)
	}
	return certs, nil
}

func (certs Certs) String() string {
	var b bytes.Buffer
	t, err := template.New("default").Parse(defaultTempl)
	if err != nil {
		panic(err)
	}
	err = t.Execute(&b, certs)
	if err != nil {
		panic(err)
	}
	return b.String()
}

func (certs Certs) Markdown() string {
	var b bytes.Buffer
	t, err := template.New("markdown").Parse(markdownTempl)
	if err != nil {
		panic(err)
	}
	err = t.Execute(&b, certs)
	if err != nil {
		panic(err)
	}
	return b.String()
}

func NewCert(d string) (*Cert, error) {
	conn, err := tls.Dial("tcp", d+":443", &tls.Config{})
	if err != nil {
		return nil, err
	}
	cert := conn.ConnectionState().PeerCertificates[0]
	conn.Close()
	return &Cert{
		DomainName: d,
		CommonName: cert.Subject.CommonName,
		SANs:       cert.DNSNames,
		Start:      cert.NotBefore.In(time.Local).Format("2006/01/02 15:04:05"),
		End:        cert.NotAfter.In(time.Local).Format("2006/01/02 15:04:05"),
	}, nil
}
<commit_msg>style<commit_after>package cert

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"time"

	"github.com/golang/go/src/pkg/text/template"
)

const defaultTempl = `{{range .}}DomainName: {{.DomainName}}
Start:      {{.Start}}
End:        {{.End}}
CommonName: {{.CommonName}}
SANs:       {{.SANs}}

{{end}}
`

const markdownTempl = `ドメイン名 | 有効期間の開始 | 有効期間の終了 | CN | SANs
--- | --- | --- | --- | ---
{{range .}}{{.DomainName}} | {{.Start}} | {{.End}} | {{.CommonName}} | {{range .SANs}}{{.}}
{{end}} {{end}}
`

type Certs []*Cert

type Cert struct {
	DomainName string
	CommonName string
	SANs       []string
	Start      string
	End        string
}

func init() {
	loc, err := time.LoadLocation("Asia/Tokyo")
	if err != nil {
		loc = time.FixedZone("Asia/Tokyo", 9*60*60)
	}
	time.Local = loc
}

func NewCerts(s []string) (Certs, error) {
	if len(s) < 1 {
		return nil, fmt.Errorf("ドメイン名をひとつ以上指定してください。")
	}
	certs := Certs{}
	for _, d := range s[:] {
		c, err := NewCert(d)
		if err != nil {
			return nil, err
		}
		certs = append(certs, c)
	}
	return certs, nil
}

func (certs Certs) String() string {
	var b bytes.Buffer
	t, err := template.New("default").Parse(defaultTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs); err != nil {
		panic(err)
	}
	return b.String()
}

func (certs Certs) Markdown() string {
	var b bytes.Buffer
	t, err := template.New("markdown").Parse(markdownTempl)
	if err != nil {
		panic(err)
	}
	if err := t.Execute(&b, certs); err != nil {
		panic(err)
	}
	return b.String()
}

func NewCert(d string) (*Cert, error) {
	conn, err := tls.Dial("tcp", d+":443", &tls.Config{})
	if err != nil {
		return nil, err
	}
	cert := conn.ConnectionState().PeerCertificates[0]
	conn.Close()
	return &Cert{
		DomainName: d,
		CommonName: cert.Subject.CommonName,
		SANs:       cert.DNSNames,
		Start:      cert.NotBefore.In(time.Local).Format("2006/01/02 15:04:05"),
		End:        cert.NotAfter.In(time.Local).Format("2006/01/02 15:04:05"),
	}, nil
}
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME     = ""
	DEFAULT_MESSAGE_ASUSER       = false
	DEFAULT_MESSAGE_PARSE        = ""
	DEFAULT_MESSAGE_LINK_NAMES   = 0
	DEFAULT_MESSAGE_UNFURL_LINKS = false
	DEFAULT_MESSAGE_UNFURL_MEDIA = true
	DEFAULT_MESSAGE_ICON_URL     = ""
	DEFAULT_MESSAGE_ICON_EMOJI   = ""
	DEFAULT_MESSAGE_MARKDOWN     = true
	DEFAULT_MESSAGE_ESCAPE_TEXT  = true
)

type chatResponseFull struct {
	Channel   string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text        string       `json:"text"`
	Username    string       `json:"user_name"`
	AsUser      bool         `json:"as_user"`
	Parse       string       `json:"parse"`
	LinkNames   int          `json:"link_names"`
	Attachments []Attachment `json:"attachments"`
	UnfurlLinks bool         `json:"unfurl_links"`
	UnfurlMedia bool         `json:"unfurl_media"`
	IconURL     string       `json:"icon_url"`
	IconEmoji   string       `json:"icon_emoji"`
	Markdown    bool         `json:"mrkdwn,omitempty"`
	EscapeText  bool         `json:"escape_text"`
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		AsUser:      DEFAULT_MESSAGE_ASUSER,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:    DEFAULT_MESSAGE_MARKDOWN,
		EscapeText:  DEFAULT_MESSAGE_ESCAPE_TEXT,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.Channel, response.Timestamp, nil
}

func escapeMessage(message string) string {
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
	if params.EscapeText {
		text = escapeMessage(text)
	}
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"text":    {text},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.AsUser != DEFAULT_MESSAGE_ASUSER {
		values.Set("as_user", "true")
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "true")
	}
	// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
	// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
	if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "false")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
		values.Set("mrkdwn", "false")
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.Channel, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.Channel, response.Timestamp, response.Text, nil
}
<commit_msg>Add thread_ts support when posting messages<commit_after>package slack

import (
	"encoding/json"
	"errors"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME         = ""
	DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
	DEFAULT_MESSAGE_ASUSER           = false
	DEFAULT_MESSAGE_PARSE            = ""
	DEFAULT_MESSAGE_LINK_NAMES       = 0
	DEFAULT_MESSAGE_UNFURL_LINKS     = false
	DEFAULT_MESSAGE_UNFURL_MEDIA     = true
	DEFAULT_MESSAGE_ICON_URL         = ""
	DEFAULT_MESSAGE_ICON_EMOJI       = ""
	DEFAULT_MESSAGE_MARKDOWN         = true
	DEFAULT_MESSAGE_ESCAPE_TEXT      = true
)

type chatResponseFull struct {
	Channel   string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Text            string       `json:"text"`
	Username        string       `json:"user_name"`
	AsUser          bool         `json:"as_user"`
	Parse           string       `json:"parse"`
	ThreadTimestamp string       `json:"thread_ts"`
	LinkNames       int          `json:"link_names"`
	Attachments     []Attachment `json:"attachments"`
	UnfurlLinks     bool         `json:"unfurl_links"`
	UnfurlMedia     bool         `json:"unfurl_media"`
	IconURL         string       `json:"icon_url"`
	IconEmoji       string       `json:"icon_emoji"`
	Markdown        bool         `json:"mrkdwn,omitempty"`
	EscapeText      bool         `json:"escape_text"`
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:    DEFAULT_MESSAGE_USERNAME,
		AsUser:      DEFAULT_MESSAGE_ASUSER,
		Parse:       DEFAULT_MESSAGE_PARSE,
		LinkNames:   DEFAULT_MESSAGE_LINK_NAMES,
		Attachments: nil,
		UnfurlLinks: DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia: DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:     DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:   DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:    DEFAULT_MESSAGE_MARKDOWN,
		EscapeText:  DEFAULT_MESSAGE_ESCAPE_TEXT,
	}
}

func chatRequest(path string, values url.Values, debug bool) (*chatResponseFull, error) {
	response := &chatResponseFull{}
	err := post(path, values, response, debug)
	if err != nil {
		return nil, err
	}
	if !response.Ok {
		return nil, errors.New(response.Error)
	}
	return response, nil
}

// DeleteMessage deletes a message in a channel
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"ts":      {messageTimestamp},
	}
	response, err := chatRequest("chat.delete", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.Channel, response.Timestamp, nil
}

func escapeMessage(message string) string {
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

// PostMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
	if params.EscapeText {
		text = escapeMessage(text)
	}
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"text":    {text},
	}
	if params.Username != DEFAULT_MESSAGE_USERNAME {
		values.Set("username", string(params.Username))
	}
	if params.AsUser != DEFAULT_MESSAGE_ASUSER {
		values.Set("as_user", "true")
	}
	if params.Parse != DEFAULT_MESSAGE_PARSE {
		values.Set("parse", string(params.Parse))
	}
	if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
		values.Set("link_names", "1")
	}
	if params.Attachments != nil {
		attachments, err := json.Marshal(params.Attachments)
		if err != nil {
			return "", "", err
		}
		values.Set("attachments", string(attachments))
	}
	if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "true")
	}
	// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
	// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
	if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
		values.Set("unfurl_links", "false")
	}
	if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
		values.Set("unfurl_media", "false")
	}
	if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
		values.Set("icon_url", params.IconURL)
	}
	if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
		values.Set("icon_emoji", params.IconEmoji)
	}
	if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
		values.Set("mrkdwn", "false")
	}
	if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
		values.Set("thread_ts", params.ThreadTimestamp)
	}

	response, err := chatRequest("chat.postMessage", values, api.debug)
	if err != nil {
		return "", "", err
	}
	return response.Channel, response.Timestamp, nil
}

// UpdateMessage updates a message in a channel
func (api *Client) UpdateMessage(channel, timestamp, text string) (string, string, string, error) {
	values := url.Values{
		"token":   {api.config.token},
		"channel": {channel},
		"text":    {escapeMessage(text)},
		"ts":      {timestamp},
	}
	response, err := chatRequest("chat.update", values, api.debug)
	if err != nil {
		return "", "", "", err
	}
	return response.Channel, response.Timestamp, response.Text, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Collins
//
// 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 main

import (
	"fmt"
	"math"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/resource/locale"
)

const (
	chatHistoryLines = 10
	maxLineWidth     = 500
)

type ChatUI struct {
	Elements []*chatUIElement

	Lines    [chatHistoryLines]chat.AnyComponent
	lineFade [chatHistoryLines]float64

	lineLength float64

	enteringText bool
	inputLine    []rune
	cursorTick   float64
	first        bool
}

func (c *ChatUI) render(delta float64) {
	c.Elements = c.Elements[:0]
	for i, line := range c.Lines {
		c.newLine()
		if line.Value == nil {
			continue
		}
		c.lineLength = 0
		c.renderComponent(i, line.Value, nil)
	}

	if c.enteringText {
		// Shift all the lines up
		c.newLine()
		c.lineLength = 0

		color := chat.White
		gc := func() chat.Color { return color }
		line := c.inputLine
		// Make it clear that a command is being typed
		if len(line) != 0 && line[0] == '/' {
			color = chat.Gold
			c.renderText(len(c.Lines), line[:1], gc)
			color = chat.Yellow
			line = line[1:]
		}
		c.renderText(len(c.Lines), line, gc)
		c.cursorTick += delta
		// Add on our cursor
		if int(c.cursorTick/30)%2 == 0 {
			c.renderText(len(c.Lines), []rune{'|'}, gc)
		}
		// Lazy way of preventing rounding errors buiding up over time
		if c.cursorTick > 0xFFFFFF {
			c.cursorTick = 0
		}
	}
	// Slowly fade out each line
	for i := range c.lineFade {
		c.lineFade[i] -= 0.005 * delta
		if c.lineFade[i] < 0 {
			c.lineFade[i] = 0
		}
	}
	solid := render.GetTexture("solid")
	first := true
	top := 0
	for _, e := range c.Elements {
		if !e.draw {
			continue
		}
		if first {
			first = false
			top = e.offset
		}
		x, y, w, h := e.x, 480-18*float64(e.offset+1), e.width, 18.0
		ux, uy := x, y
		if x == 2 {
			ux -= 2
			w += 2
		}
		if e.offset == top {
			uy -= 2
			h += 2
		}
		background := render.DrawUIElement(solid, ux, uy, w, h, 0, 0, 1, 1)
		background.R = 0
		background.G = 0
		background.B = 0
		ba := 0.3
		text := render.DrawUIText(e.text, x, y, e.r, e.g, e.b)
		// If entering text show every line
		if !c.enteringText {
			text.Alpha(c.lineFade[e.line])
			ba -= 1.0 - c.lineFade[e.line]
			ba = math.Min(ba, 0.5)
		}
		background.Alpha(ba)
	}
}

func (c *ChatUI) handleKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	if (key == glfw.KeyEscape || key == glfw.KeyEnter) && action == glfw.Release {
		if key == glfw.KeyEnter && len(c.inputLine) != 0 {
			writeChan <- &protocol.ChatMessage{string(c.inputLine)}
		}
		// Return control back to the default
		c.enteringText = false
		c.inputLine = c.inputLine[:0]
		lockMouse = true
		w.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
		w.SetCharCallback(nil)
		return
	}
	if key == glfw.KeyBackspace && action != glfw.Release {
		if len(c.inputLine) > 0 {
			c.inputLine = c.inputLine[:len(c.inputLine)-1]
		}
	}
}

func (c *ChatUI) handleChar(w *glfw.Window, char rune) {
	if c.first {
		c.first = false
		return
	}
	if len(c.inputLine) < 100 {
		c.inputLine = append(c.inputLine, char)
	}
}

func (c *ChatUI) renderComponent(line int, co interface{}, color chatGetColorFunc) {
	switch co := co.(type) {
	case *chat.TextComponent:
		getColor := chatGetColor(&co.Component, color)
		c.renderText(line, []rune(co.Text), getColor)
		for _, e := range co.Extra {
			c.renderComponent(line, e.Value, getColor)
		}
	case *chat.TranslateComponent:
		getColor := chatGetColor(&co.Component, color)
		for _, part := range locale.Get(co.Translate) {
			switch part := part.(type) {
			case string:
				c.renderText(line, []rune(part), getColor)
			case int:
				if part < 0 || part >= len(co.With) {
					continue
				}
				c.renderComponent(line, co.With[part].Value, getColor)
			}
		}
		for _, e := range co.Extra {
			c.renderComponent(line, e.Value, getColor)
		}
	default:
		fmt.Printf("Can't handle %T\n", co)
	}
}

func (c *ChatUI) renderText(line int, runes []rune, getColor chatGetColorFunc) {
	width := 0.0
	r, g, b := chatColorRGB(getColor())
	for i := 0; i < len(runes); i++ {
		size := float64(render.SizeOfCharacter(runes[i]))
		if c.lineLength+width+size > maxLineWidth {
			c.appendText(line, string(runes[:i]), r, g, b)
			c.lineLength = 0
			runes = runes[i:]
			i = 0
			width = 0
			c.newLine()
		}
		width += size
	}
	c.lineLength += c.appendText(line, string(runes), r, g, b)
}

type chatUIElement struct {
	text    string
	x       float64
	width   float64
	r, g, b int
	offset  int
	line    int
	draw    bool
}

func (c *ChatUI) appendText(line int, str string, r, g, b int) float64 {
	if str == "" {
		return 0
	}
	e := &chatUIElement{
		text:  str,
		x:     2 + c.lineLength,
		width: render.SizeOfString(str) + 2,
		r:     r, g: g, b: b,
		offset: 0,
		line:   line,
		draw:   true,
	}
	c.Elements = append(c.Elements, e)
	return e.width
}

type chatGetColorFunc func() chat.Color

func chatGetColor(c *chat.Component, parent chatGetColorFunc) chatGetColorFunc {
	return func() chat.Color {
		if c.Color != "" {
			return c.Color
		}
		if parent != nil {
			return parent()
		}
		return chat.White
	}
}

func chatColorRGB(c chat.Color) (r, g, b int) {
	switch c {
	case chat.Black:
		return 0, 0, 0
	case chat.DarkBlue:
		return 0, 0, 170
	case chat.DarkGreen:
		return 0, 170, 0
	case chat.DarkAqua:
		return 0, 170, 170
	case chat.DarkRed:
		return 170, 0, 0
	case chat.DarkPurple:
		return 170, 0, 170
	case chat.Gold:
		return 255, 170, 0
	case chat.Gray:
		return 170, 170, 170
	case chat.DarkGray:
		return 85, 85, 85
	case chat.Blue:
		return 85, 85, 255
	case chat.Green:
		return 85, 255, 85
	case chat.Aqua:
		return 85, 255, 255
	case chat.Red:
		return 255, 85, 85
	case chat.LightPurple:
		return 255, 85, 255
	case chat.Yellow:
		return 255, 255, 85
	case chat.White:
		return 255, 255, 255

	}
	return 255, 255, 255
}

func (c *ChatUI) newLine() {
	for _, e := range c.Elements {
		e.offset++
		if e.offset >= chatHistoryLines {
			e.draw = false
		}
	}
}

func (c *ChatUI) Add(msg chat.AnyComponent) {
	copy(c.Lines[0:chatHistoryLines-1], c.Lines[1:])
	copy(c.lineFade[0:chatHistoryLines-1], c.lineFade[1:])
	c.Lines[chatHistoryLines-1] = msg
	c.lineFade[chatHistoryLines-1] = 3.0
}
<commit_msg>steven: fade the background of chat more inline with text<commit_after>// Copyright 2015 Matthew Collins
//
// 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 main

import (
	"fmt"
	"math"

	"github.com/go-gl/glfw/v3.1/glfw"
	"github.com/thinkofdeath/steven/chat"
	"github.com/thinkofdeath/steven/protocol"
	"github.com/thinkofdeath/steven/render"
	"github.com/thinkofdeath/steven/resource/locale"
)

const (
	chatHistoryLines = 10
	maxLineWidth     = 500
)

type ChatUI struct {
	Elements []*chatUIElement

	Lines    [chatHistoryLines]chat.AnyComponent
	lineFade [chatHistoryLines]float64

	lineLength float64

	enteringText bool
	inputLine    []rune
	cursorTick   float64
	first        bool
}

func (c *ChatUI) render(delta float64) {
	c.Elements = c.Elements[:0]
	for i, line := range c.Lines {
		c.newLine()
		if line.Value == nil {
			continue
		}
		c.lineLength = 0
		c.renderComponent(i, line.Value, nil)
	}

	if c.enteringText {
		// Shift all the lines up
		c.newLine()
		c.lineLength = 0

		color := chat.White
		gc := func() chat.Color { return color }
		line := c.inputLine
		// Make it clear that a command is being typed
		if len(line) != 0 && line[0] == '/' {
			color = chat.Gold
			c.renderText(len(c.Lines), line[:1], gc)
			color = chat.Yellow
			line = line[1:]
		}
		c.renderText(len(c.Lines), line, gc)
		c.cursorTick += delta
		// Add on our cursor
		if int(c.cursorTick/30)%2 == 0 {
			c.renderText(len(c.Lines), []rune{'|'}, gc)
		}
		// Lazy way of preventing rounding errors buiding up over time
		if c.cursorTick > 0xFFFFFF {
			c.cursorTick = 0
		}
	}
	// Slowly fade out each line
	for i := range c.lineFade {
		c.lineFade[i] -= 0.005 * delta
		if c.lineFade[i] < 0 {
			c.lineFade[i] = 0
		}
	}
	solid := render.GetTexture("solid")
	first := true
	top := 0
	for _, e := range c.Elements {
		if !e.draw {
			continue
		}
		if first {
			first = false
			top = e.offset
		}
		x, y, w, h := e.x, 480-18*float64(e.offset+1), e.width, 18.0
		ux, uy := x, y
		if x == 2 {
			ux -= 2
			w += 2
		}
		if e.offset == top {
			uy -= 2
			h += 2
		}
		background := render.DrawUIElement(solid, ux, uy, w, h, 0, 0, 1, 1)
		background.R = 0
		background.G = 0
		background.B = 0
		ba := 0.3
		text := render.DrawUIText(e.text, x, y, e.r, e.g, e.b)
		// If entering text show every line
		if !c.enteringText {
			text.Alpha(c.lineFade[e.line])
			ba -= (1.0 - c.lineFade[e.line]) / 2.0
			ba = math.Min(ba, 0.5)
		}
		background.Alpha(ba)
	}
}

func (c *ChatUI) handleKey(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
	if (key == glfw.KeyEscape || key == glfw.KeyEnter) && action == glfw.Release {
		if key == glfw.KeyEnter && len(c.inputLine) != 0 {
			writeChan <- &protocol.ChatMessage{string(c.inputLine)}
		}
		// Return control back to the default
		c.enteringText = false
		c.inputLine = c.inputLine[:0]
		lockMouse = true
		w.SetInputMode(glfw.CursorMode, glfw.CursorDisabled)
		w.SetCharCallback(nil)
		return
	}
	if key == glfw.KeyBackspace && action != glfw.Release {
		if len(c.inputLine) > 0 {
			c.inputLine = c.inputLine[:len(c.inputLine)-1]
		}
	}
}

func (c *ChatUI) handleChar(w *glfw.Window, char rune) {
	if c.first {
		c.first = false
		return
	}
	if len(c.inputLine) < 100 {
		c.inputLine = append(c.inputLine, char)
	}
}

func (c *ChatUI) renderComponent(line int, co interface{}, color chatGetColorFunc) {
	switch co := co.(type) {
	case *chat.TextComponent:
		getColor := chatGetColor(&co.Component, color)
		c.renderText(line, []rune(co.Text), getColor)
		for _, e := range co.Extra {
			c.renderComponent(line, e.Value, getColor)
		}
	case *chat.TranslateComponent:
		getColor := chatGetColor(&co.Component, color)
		for _, part := range locale.Get(co.Translate) {
			switch part := part.(type) {
			case string:
				c.renderText(line, []rune(part), getColor)
			case int:
				if part < 0 || part >= len(co.With) {
					continue
				}
				c.renderComponent(line, co.With[part].Value, getColor)
			}
		}
		for _, e := range co.Extra {
			c.renderComponent(line, e.Value, getColor)
		}
	default:
		fmt.Printf("Can't handle %T\n", co)
	}
}

func (c *ChatUI) renderText(line int, runes []rune, getColor chatGetColorFunc) {
	width := 0.0
	r, g, b := chatColorRGB(getColor())
	for i := 0; i < len(runes); i++ {
		size := float64(render.SizeOfCharacter(runes[i]))
		if c.lineLength+width+size > maxLineWidth {
			c.appendText(line, string(runes[:i]), r, g, b)
			c.lineLength = 0
			runes = runes[i:]
			i = 0
			width = 0
			c.newLine()
		}
		width += size
	}
	c.lineLength += c.appendText(line, string(runes), r, g, b)
}

type chatUIElement struct {
	text    string
	x       float64
	width   float64
	r, g, b int
	offset  int
	line    int
	draw    bool
}

func (c *ChatUI) appendText(line int, str string, r, g, b int) float64 {
	if str == "" {
		return 0
	}
	e := &chatUIElement{
		text:  str,
		x:     2 + c.lineLength,
		width: render.SizeOfString(str) + 2,
		r:     r, g: g, b: b,
		offset: 0,
		line:   line,
		draw:   true,
	}
	c.Elements = append(c.Elements, e)
	return e.width
}

type chatGetColorFunc func() chat.Color

func chatGetColor(c *chat.Component, parent chatGetColorFunc) chatGetColorFunc {
	return func() chat.Color {
		if c.Color != "" {
			return c.Color
		}
		if parent != nil {
			return parent()
		}
		return chat.White
	}
}

func chatColorRGB(c chat.Color) (r, g, b int) {
	switch c {
	case chat.Black:
		return 0, 0, 0
	case chat.DarkBlue:
		return 0, 0, 170
	case chat.DarkGreen:
		return 0, 170, 0
	case chat.DarkAqua:
		return 0, 170, 170
	case chat.DarkRed:
		return 170, 0, 0
	case chat.DarkPurple:
		return 170, 0, 170
	case chat.Gold:
		return 255, 170, 0
	case chat.Gray:
		return 170, 170, 170
	case chat.DarkGray:
		return 85, 85, 85
	case chat.Blue:
		return 85, 85, 255
	case chat.Green:
		return 85, 255, 85
	case chat.Aqua:
		return 85, 255, 255
	case chat.Red:
		return 255, 85, 85
	case chat.LightPurple:
		return 255, 85, 255
	case chat.Yellow:
		return 255, 255, 85
	case chat.White:
		return 255, 255, 255

	}
	return 255, 255, 255
}

func (c *ChatUI) newLine() {
	for _, e := range c.Elements {
		e.offset++
		if e.offset >= chatHistoryLines {
			e.draw = false
		}
	}
}

func (c *ChatUI) Add(msg chat.AnyComponent) {
	copy(c.Lines[0:chatHistoryLines-1], c.Lines[1:])
	copy(c.lineFade[0:chatHistoryLines-1], c.lineFade[1:])
	c.Lines[chatHistoryLines-1] = msg
	c.lineFade[chatHistoryLines-1] = 3.0
}
<|endoftext|>


<commit_before>package wag

import (
	"errors"
	"fmt"
	"strings"

	"github.com/tsavola/wag/internal/links"
	"github.com/tsavola/wag/internal/regs"
	"github.com/tsavola/wag/internal/types"
)

const (
	wordSize = 8
)

func (m *Module) Code() []byte {
	code := programCoder{
		mach:          machine.NewCoder(),
		functionLinks: make(map[*Function]*links.L),
	}

	code.module(m)

	return code.mach.Bytes()
}

type programCoder struct {
	mach          machineCoder
	functionLinks map[*Function]*links.L
}

func (code *programCoder) module(m *Module) {
	for _, f := range m.FunctionList {
		code.functionLinks[f] = new(links.L)
	}

	start := m.Functions[m.Start]
	code.function(m, start)

	for _, f := range m.FunctionList {
		if f != start {
			code.function(m, f)
		}
	}

	for _, link := range code.functionLinks {
		code.mach.UpdateCalls(link)
	}

	return
}

func (program *programCoder) function(m *Module, f *Function) {
	code := functionCoder{
		program:  program,
		module:   m,
		function: f,
		mach:     program.mach,
	}

	program.functionLinks[f].Address = code.mach.Len()

	if f.NumLocals > 0 {
		// TODO: decrement stack pointer and check bounds instead
		code.mach.InstClear(regs.R0)
		for i := 0; i < f.NumLocals; i++ {
			code.mach.InstPush(regs.R0)
		}
	}

	for _, x := range f.body {
		code.expr(x)
	}

	if code.stackOffset != 0 {
		panic(errors.New("internal: stack offset is non-zero at end of function"))
	}

	if offset := code.getLocalsEndOffset(); offset > 0 {
		code.mach.InstAddToStackPtr(offset)
	}

	code.mach.InstRet()

	for _, link := range code.labelLinks {
		code.mach.UpdateBranches(link)
	}
}

type functionCoder struct {
	module      *Module
	program     *programCoder
	function    *Function
	mach        machineCoder
	stackOffset int
	labelLinks  []*links.L
}

func (code *functionCoder) expr(x interface{}) {
	expr := x.([]interface{})
	exprName := expr[0].(string)
	args := expr[1:]

	if strings.Contains(exprName, ".") {
		tokens := strings.SplitN(exprName, ".", 2)

		exprType, found := types.ByString[tokens[0]]
		if !found {
			panic(fmt.Errorf("unknown operand type: %s", exprName))
		}

		instName := tokens[1]

		switch instName {
		case "add", "and", "ne", "or", "sub", "xor":
			if len(args) != 2 {
				panic(fmt.Errorf("%s: wrong number of operands", exprName))
			}
			code.expr(args[0])
			code.mach.InstPush(regs.R0)
			code.stackOffset += wordSize
			code.expr(args[1])
			code.mach.InstMoveRegToReg(regs.R0, regs.R1)
			code.mach.InstPop(regs.R0)
			code.stackOffset -= wordSize
			code.mach.TypedBinaryInst(exprType, instName, regs.R1, regs.R0)

		case "const":
			if len(args) != 1 {
				panic(fmt.Errorf("%s: wrong number of operands", exprName))
			}
			code.mach.InstMoveImmToReg(exprType, args[0], regs.R0)

		default:
			fmt.Printf("operation not supported: %v\n", exprName)
			code.mach.InstInvalid()
		}
	} else {
		switch exprName {
		case "call":
			if len(args) < 1 {
				panic(fmt.Errorf("%s: too few operands", exprName))
			}
			funcName := args[0].(string)
			target, found := code.module.Functions[funcName]
			if !found {
				panic(fmt.Errorf("%s: function not found: %s", exprName, funcName))
			}
			if len(target.Signature.ArgTypes) != len(args)-1 {
				panic(fmt.Errorf("%s: wrong number of arguments", exprName))
			}
			funcArgs := args[1:]
			for _, arg := range funcArgs {
				code.expr(arg)
				code.mach.InstPush(regs.R0)
				code.stackOffset += wordSize
			}
			code.instCall(code.program.functionLinks[target])
			for range funcArgs {
				code.mach.InstPop(regs.R1)
				code.stackOffset -= wordSize
			}

		case "get_local":
			if len(args) != 1 {
				panic(fmt.Errorf("%s: wrong number of operands", exprName))
			}
			varName := args[0].(string)
			offset, found := code.getVarOffset(varName)
			if !found {
				panic(fmt.Errorf("%s: variable not found: %s", exprName, varName))
			}
			code.mach.InstMoveVarToReg(offset, regs.R0)

		case "if":
			if len(args) < 2 {
				panic(fmt.Errorf("%s: too few operands", exprName))
			}
			haveElse := len(args) == 3
			if len(args) > 3 {
				panic(fmt.Errorf("%s: too many operands", exprName))
			}
			afterThen := new(links.L)
			afterElse := new(links.L)
			code.expr(args[0])
			code.instBranchIfNot(0, afterThen)
			for _, e := range args[1].([]interface{}) {
				code.expr(e)
			}
			if haveElse {
				code.instBranch(afterElse)
			}
			code.label(afterThen)
			if haveElse {
				for _, e := range args[2].([]interface{}) {
					code.expr(e)
				}
				code.label(afterElse)
			}

		case "return":
			if code.function.Signature.ResultType == types.Void {
				if len(args) == 1 {
					// this should return a void...
					code.expr(args[0])
				} else if len(args) != 0 {
					panic(fmt.Errorf("%s: wrong number of operands", exprName))
				}
			} else {
				if len(args) != 1 {
					panic(fmt.Errorf("%s: wrong number of operands", exprName))
				}
				code.expr(args[0])
			}
			if offset := code.getLocalsEndOffset(); offset > 0 {
				code.mach.InstAddToStackPtr(offset)
			}
			code.mach.InstRet()

		case "unreachable":
			if len(args) != 0 {
				panic(fmt.Errorf("%s: wrong number of operands", exprName))
			}
			code.mach.InstInvalid()

		default:
			fmt.Printf("operation not supported: %v\n", exprName)
			code.mach.InstInvalid()
		}
	}
}

func (code *functionCoder) instBranch(l *links.L) {
	code.mach.InstBranchStub()
	l.Sites = append(l.Sites, code.mach.Len())
	code.labelLinks = append(code.labelLinks, l)
}

func (code *functionCoder) instBranchIfNot(reg regs.R, l *links.L) {
	code.mach.InstBranchIfNotStub(reg)
	l.Sites = append(l.Sites, code.mach.Len())
	code.labelLinks = append(code.labelLinks, l)
}

func (code *functionCoder) instCall(l *links.L) {
	code.mach.InstCallStub()
	l.Sites = append(l.Sites, code.mach.Len())
}

func (code *functionCoder) label(l *links.L) {
	l.Address = code.mach.Len()
}

func (code *functionCoder) getVarOffset(name string) (offset int, found bool) {
	v, found := code.function.Vars[name]
	if !found {
		return
	}

	index := v.Index

	if v.Param {
		// function's return address is between locals and params
		index = code.function.NumLocals + 1 + (code.function.NumParams - index - 1)
	}

	offset = code.stackOffset + index*wordSize
	return
}

func (code *functionCoder) getLocalsEndOffset() int {
	return code.stackOffset + code.function.NumLocals*wordSize
}
<commit_msg>refactor push/pop with stack offset tracking<commit_after>package wag

import (
	"errors"
	"fmt"
	"strings"

	"github.com/tsavola/wag/internal/links"
	"github.com/tsavola/wag/internal/regs"
	"github.com/tsavola/wag/internal/types"
)

const (
	wordSize = 8
)

func (m *Module) Code() []byte {
	code := programCoder{
		mach:          machine.NewCoder(),
		functionLinks: make(map[*Function]*links.L),
	}

	code.module(m)

	return code.mach.Bytes()
}

type programCoder struct {
	mach          machineCoder
	functionLinks map[*Function]*links.L
}

func (code *programCoder) module(m *Module) {
	for _, f := range m.FunctionList {
		code.functionLinks[f] = new(links.L)
	}

	start := m.Functions[m.Start]
	code.function(m, start)

	for _, f := range m.FunctionList {
		if f != start {
			code.function(m, f)
		}
	}

	for _, link := range code.functionLinks {
		code.mach.UpdateCalls(link)
	}

	return
}

func (program *programCoder) function(m *Module, f *Function) {
	code := functionCoder{
		program:  program,
		module:   m,
		function: f,
		mach:     program.mach,
	}

	program.functionLinks[f].Address = code.mach.Len()

	if f.NumLocals > 0 {
		// TODO: decrement stack pointer and check bounds instead
		code.mach.InstClear(regs.R0)
		for i := 0; i < f.NumLocals; i++ {
			code.mach.InstPush(regs.R0)
		}
	}

	for _, x := range f.body {
		code.expr(x)
	}

	if code.stackOffset != 0 {
		panic(errors.New("internal: stack offset is non-zero at end of function"))
	}

	if offset := code.getLocalsEndOffset(); offset > 0 {
		code.mach.InstAddToStackPtr(offset)
	}

	code.mach.InstRet()

	for _, link := range code.labelLinks {
		code.mach.UpdateBranches(link)
	}
}

type functionCoder struct {
	module      *Module
	program     *programCoder
	function    *Function
	mach        machineCoder
	stackOffset int
	labelLinks  []*links.L
}

func (code *functionCoder) expr(x interface{}) {
	expr := x.([]interface{})
	exprName := expr[0].(string)
	args := expr[1:]

	if strings.Contains(exprName, ".") {
		tokens := strings.SplitN(exprName, ".", 2)

		exprType, found := types.ByString[tokens[0]]
		if !found {
			panic(fmt.Errorf("unknown operand type: %s", exprName))
		}

		instName := tokens[1]

		switch instName {
		case "add", "and", "ne", "or", "sub", "xor":
			if len(args) != 2 {
				panic(fmt.Errorf("%s: wrong number of operands", exprName))
			}
			code.expr(args[0])
			code.instPush(regs.R0)
			code.expr(args[1])
			code.mach.InstMoveRegToReg(regs.R0, regs.R1)
			code.instPop(regs.R0)
			code.mach.TypedBinaryInst(exprType, instName, regs.R1, regs.R0)

		case "const":
			if len(args) != 1 {
				panic(fmt.Errorf("%s: wrong number of operands", exprName))
			}
			code.mach.InstMoveImmToReg(exprType, args[0], regs.R0)

		default:
			fmt.Printf("operation not supported: %v\n", exprName)
			code.mach.InstInvalid()
		}
	} else {
		switch exprName {
		case "call":
			if len(args) < 1 {
				panic(fmt.Errorf("%s: too few operands", exprName))
			}
			funcName := args[0].(string)
			target, found := code.module.Functions[funcName]
			if !found {
				panic(fmt.Errorf("%s: function not found: %s", exprName, funcName))
			}
			if len(target.Signature.ArgTypes) != len(args)-1 {
				panic(fmt.Errorf("%s: wrong number of arguments", exprName))
			}
			funcArgs := args[1:]
			for _, arg := range funcArgs {
				code.expr(arg)
				code.instPush(regs.R0)
			}
			code.instCall(code.program.functionLinks[target])
			for range funcArgs {
				code.instPop(regs.R1)
			}

		case "get_local":
			if len(args) != 1 {
				panic(fmt.Errorf("%s: wrong number of operands", exprName))
			}
			varName := args[0].(string)
			offset, found := code.getVarOffset(varName)
			if !found {
				panic(fmt.Errorf("%s: variable not found: %s", exprName, varName))
			}
			code.mach.InstMoveVarToReg(offset, regs.R0)

		case "if":
			if len(args) < 2 {
				panic(fmt.Errorf("%s: too few operands", exprName))
			}
			haveElse := len(args) == 3
			if len(args) > 3 {
				panic(fmt.Errorf("%s: too many operands", exprName))
			}
			afterThen := new(links.L)
			afterElse := new(links.L)
			code.expr(args[0])
			code.instBranchIfNot(0, afterThen)
			for _, e := range args[1].([]interface{}) {
				code.expr(e)
			}
			if haveElse {
				code.instBranch(afterElse)
			}
			code.label(afterThen)
			if haveElse {
				for _, e := range args[2].([]interface{}) {
					code.expr(e)
				}
				code.label(afterElse)
			}

		case "return":
			if code.function.Signature.ResultType == types.Void {
				if len(args) == 1 {
					// this should return a void...
					code.expr(args[0])
				} else if len(args) != 0 {
					panic(fmt.Errorf("%s: wrong number of operands", exprName))
				}
			} else {
				if len(args) != 1 {
					panic(fmt.Errorf("%s: wrong number of operands", exprName))
				}
				code.expr(args[0])
			}
			if offset := code.getLocalsEndOffset(); offset > 0 {
				code.mach.InstAddToStackPtr(offset)
			}
			code.mach.InstRet()

		case "unreachable":
			if len(args) != 0 {
				panic(fmt.Errorf("%s: wrong number of operands", exprName))
			}
			code.mach.InstInvalid()

		default:
			fmt.Printf("operation not supported: %v\n", exprName)
			code.mach.InstInvalid()
		}
	}
}

func (code *functionCoder) instBranch(l *links.L) {
	code.mach.InstBranchStub()
	l.Sites = append(l.Sites, code.mach.Len())
	code.labelLinks = append(code.labelLinks, l)
}

func (code *functionCoder) instBranchIfNot(reg regs.R, l *links.L) {
	code.mach.InstBranchIfNotStub(reg)
	l.Sites = append(l.Sites, code.mach.Len())
	code.labelLinks = append(code.labelLinks, l)
}

func (code *functionCoder) instCall(l *links.L) {
	code.mach.InstCallStub()
	l.Sites = append(l.Sites, code.mach.Len())
}

func (code *functionCoder) instPop(reg regs.R) {
	code.mach.InstPop(reg)
	code.stackOffset -= wordSize
}

func (code *functionCoder) instPush(reg regs.R) {
	code.mach.InstPush(reg)
	code.stackOffset += wordSize
}

func (code *functionCoder) label(l *links.L) {
	l.Address = code.mach.Len()
}

func (code *functionCoder) getVarOffset(name string) (offset int, found bool) {
	v, found := code.function.Vars[name]
	if !found {
		return
	}

	index := v.Index

	if v.Param {
		// function's return address is between locals and params
		index = code.function.NumLocals + 1 + (code.function.NumParams - index - 1)
	}

	offset = code.stackOffset + index*wordSize
	return
}

func (code *functionCoder) getLocalsEndOffset() int {
	return code.stackOffset + code.function.NumLocals*wordSize
}
<|endoftext|>


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

package ldap

import (
	"crypto/tls"
	"errors"
	"log"
	"net"
	"sync"

	"gopkg.in/asn1-ber.v1"
)

const (
	MessageQuit     = 0
	MessageRequest  = 1
	MessageResponse = 2
	MessageFinish   = 3
)

type messagePacket struct {
	Op        int
	MessageID int64
	Packet    *ber.Packet
	Channel   chan *ber.Packet
}

// Conn represents an LDAP Connection
type Conn struct {
	conn          net.Conn
	isTLS         bool
	isClosing     bool
	Debug         debugging
	chanConfirm   chan bool
	chanResults   map[int64]chan *ber.Packet
	chanMessage   chan *messagePacket
	chanMessageID chan int64
	wgSender      sync.WaitGroup
	wgClose       sync.WaitGroup
	once          sync.Once
}

// Dial connects to the given address on the given network using net.Dial
// and then returns a new Conn for the connection.
func Dial(network, addr string) (*Conn, error) {
	c, err := net.Dial(network, addr)
	if err != nil {
		return nil, NewError(ErrorNetwork, err)
	}
	conn := NewConn(c)
	conn.start()
	return conn, nil
}

// DialTLS connects to the given address on the given network using tls.Dial
// and then returns a new Conn for the connection.
func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
	c, err := tls.Dial(network, addr, config)
	if err != nil {
		return nil, NewError(ErrorNetwork, err)
	}
	conn := NewConn(c)
	conn.isTLS = true
	conn.start()
	return conn, nil
}

// NewConn returns a new Conn using conn for network I/O.
func NewConn(conn net.Conn) *Conn {
	return &Conn{
		conn:          conn,
		chanConfirm:   make(chan bool),
		chanMessageID: make(chan int64),
		chanMessage:   make(chan *messagePacket, 10),
		chanResults:   map[int64]chan *ber.Packet{},
	}
}

func (l *Conn) start() {
	go l.reader()
	go l.processMessages()
	l.wgClose.Add(1)
}

// Close closes the connection.
func (l *Conn) Close() {
	l.once.Do(func() {
		l.isClosing = true
		l.wgSender.Wait()

		l.Debug.Printf("Sending quit message and waiting for confirmation")
		l.chanMessage <- &messagePacket{Op: MessageQuit}
		<-l.chanConfirm
		close(l.chanMessage)

		l.Debug.Printf("Closing network connection")
		if err := l.conn.Close(); err != nil {
			log.Print(err)
		}

		l.conn = nil
		l.wgClose.Done()
	})
	l.wgClose.Wait()
}

// Returns the next available messageID
func (l *Conn) nextMessageID() int64 {
	if l.chanMessageID != nil {
		if messageID, ok := <-l.chanMessageID; ok {
			return messageID
		}
	}
	return 0
}

// StartTLS sends the command to start a TLS session and then creates a new TLS Client
func (l *Conn) StartTLS(config *tls.Config) error {
	messageID := l.nextMessageID()

	if l.isTLS {
		return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
	}

	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
	request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
	packet.AppendChild(request)
	l.Debug.PrintPacket(packet)

	_, err := l.conn.Write(packet.Bytes())
	if err != nil {
		return NewError(ErrorNetwork, err)
	}

	packet, err = ber.ReadPacket(l.conn)
	if err != nil {
		return NewError(ErrorNetwork, err)
	}

	if l.Debug {
		if err := addLDAPDescriptions(packet); err != nil {
			return err
		}
		ber.PrintPacket(packet)
	}

	if packet.Children[1].Children[0].Value.(int64) == 0 {
		conn := tls.Client(l.conn, config)
		l.isTLS = true
		l.conn = conn
	}

	return nil
}

func (l *Conn) sendMessage(packet *ber.Packet) (chan *ber.Packet, error) {
	if l.isClosing {
		return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
	}
	out := make(chan *ber.Packet)
	message := &messagePacket{
		Op:        MessageRequest,
		MessageID: packet.Children[0].Value.(int64),
		Packet:    packet,
		Channel:   out,
	}
	l.sendProcessMessage(message)
	return out, nil
}

func (l *Conn) finishMessage(messageID int64) {
	if l.isClosing {
		return
	}
	message := &messagePacket{
		Op:        MessageFinish,
		MessageID: messageID,
	}
	l.sendProcessMessage(message)
}

func (l *Conn) sendProcessMessage(message *messagePacket) bool {
	if l.isClosing {
		return false
	}
	l.wgSender.Add(1)
	l.chanMessage <- message
	l.wgSender.Done()
	return true
}

func (l *Conn) processMessages() {
	defer func() {
		for messageID, channel := range l.chanResults {
			l.Debug.Printf("Closing channel for MessageID %d", messageID)
			close(channel)
			delete(l.chanResults, messageID)
		}
		close(l.chanMessageID)
		l.chanConfirm <- true
		close(l.chanConfirm)
	}()

	var messageID int64 = 1
	for {
		select {
		case l.chanMessageID <- messageID:
			messageID++
		case messagePacket, ok := <-l.chanMessage:
			if !ok {
				l.Debug.Printf("Shutting down - message channel is closed")
				return
			}
			switch messagePacket.Op {
			case MessageQuit:
				l.Debug.Printf("Shutting down - quit message received")
				return
			case MessageRequest:
				// Add to message list and write to network
				l.Debug.Printf("Sending message %d", messagePacket.MessageID)
				l.chanResults[messagePacket.MessageID] = messagePacket.Channel
				// go routine
				buf := messagePacket.Packet.Bytes()

				_, err := l.conn.Write(buf)
				if err != nil {
					l.Debug.Printf("Error Sending Message: %s", err.Error())
					break
				}
			case MessageResponse:
				l.Debug.Printf("Receiving message %d", messagePacket.MessageID)
				if chanResult, ok := l.chanResults[messagePacket.MessageID]; ok {
					chanResult <- messagePacket.Packet
				} else {
					log.Printf("Received unexpected message %d", messagePacket.MessageID)
					ber.PrintPacket(messagePacket.Packet)
				}
			case MessageFinish:
				// Remove from message list
				l.Debug.Printf("Finished message %d", messagePacket.MessageID)
				close(l.chanResults[messagePacket.MessageID])
				delete(l.chanResults, messagePacket.MessageID)
			}
		}
	}
}

func (l *Conn) reader() {
	defer func() {
		l.Close()
	}()

	for {
		packet, err := ber.ReadPacket(l.conn)
		if err != nil {
			l.Debug.Printf("reader: %s", err.Error())
			return
		}
		addLDAPDescriptions(packet)
		message := &messagePacket{
			Op:        MessageResponse,
			MessageID: packet.Children[0].Value.(int64),
			Packet:    packet,
		}
		if !l.sendProcessMessage(message) {
			return
		}

	}
}
<commit_msg>Fix StartTLS as it wasn't working.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ldap

import (
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net"
	"sync"

	"gopkg.in/asn1-ber.v1"
)

const (
	MessageQuit     = 0
	MessageRequest  = 1
	MessageResponse = 2
	MessageFinish   = 3
)

type messagePacket struct {
	Op        int
	MessageID int64
	Packet    *ber.Packet
	Channel   chan *ber.Packet
}

type sendMessageFlags uint

const (
	startTLS sendMessageFlags = 1 << iota
)

// Conn represents an LDAP Connection
type Conn struct {
	conn                net.Conn
	isTLS               bool
	isClosing           bool
	isStartingTLS       bool
	Debug               debugging
	chanConfirm         chan bool
	chanResults         map[int64]chan *ber.Packet
	chanMessage         chan *messagePacket
	chanMessageID       chan int64
	wgSender            sync.WaitGroup
	wgClose             sync.WaitGroup
	once                sync.Once
	outstandingRequests uint
	messageMutex        sync.Mutex
}

// Dial connects to the given address on the given network using net.Dial
// and then returns a new Conn for the connection.
func Dial(network, addr string) (*Conn, error) {
	c, err := net.Dial(network, addr)
	if err != nil {
		return nil, NewError(ErrorNetwork, err)
	}
	conn := NewConn(c)
	conn.start()
	return conn, nil
}

// DialTLS connects to the given address on the given network using tls.Dial
// and then returns a new Conn for the connection.
func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
	c, err := tls.Dial(network, addr, config)
	if err != nil {
		return nil, NewError(ErrorNetwork, err)
	}
	conn := NewConn(c)
	conn.isTLS = true
	conn.start()
	return conn, nil
}

// NewConn returns a new Conn using conn for network I/O.
func NewConn(conn net.Conn) *Conn {
	return &Conn{
		conn:          conn,
		chanConfirm:   make(chan bool),
		chanMessageID: make(chan int64),
		chanMessage:   make(chan *messagePacket, 10),
		chanResults:   map[int64]chan *ber.Packet{},
	}
}

func (l *Conn) start() {
	go l.reader()
	go l.processMessages()
	l.wgClose.Add(1)
}

// Close closes the connection.
func (l *Conn) Close() {
	l.once.Do(func() {
		l.isClosing = true
		l.wgSender.Wait()

		l.Debug.Printf("Sending quit message and waiting for confirmation")
		l.chanMessage <- &messagePacket{Op: MessageQuit}
		<-l.chanConfirm
		close(l.chanMessage)

		l.Debug.Printf("Closing network connection")
		if err := l.conn.Close(); err != nil {
			log.Print(err)
		}

		l.conn = nil
		l.wgClose.Done()
	})
	l.wgClose.Wait()
}

// Returns the next available messageID
func (l *Conn) nextMessageID() int64 {
	if l.chanMessageID != nil {
		if messageID, ok := <-l.chanMessageID; ok {
			return messageID
		}
	}
	return 0
}

// StartTLS sends the command to start a TLS session and then creates a new TLS Client
func (l *Conn) StartTLS(config *tls.Config) error {
	messageID := l.nextMessageID()

	if l.isTLS {
		return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
	}

	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
	request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
	packet.AppendChild(request)
	l.Debug.PrintPacket(packet)

	channel, err := l.sendMessageWithFlags(packet, startTLS)
	if err != nil {
		return err
	}
	if channel == nil {
		return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
	}

	l.Debug.Printf("%d: waiting for response", messageID)
	packet = <-channel
	l.Debug.Printf("%d: got response %p", messageID, packet)
	l.finishMessage(messageID)

	if l.Debug {
		if err := addLDAPDescriptions(packet); err != nil {
			l.Close()
			return err
		}
		ber.PrintPacket(packet)
	}

	if packet.Children[1].Children[0].Value.(int64) == 0 {
		conn := tls.Client(l.conn, config)

		if err := conn.Handshake(); err != nil {
			l.Close()
			return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", err))
		}

		l.isTLS = true
		l.conn = conn
	}
	go l.reader()

	return nil
}

func (l *Conn) sendMessage(packet *ber.Packet) (chan *ber.Packet, error) {
	return l.sendMessageWithFlags(packet, 0)
}

func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (chan *ber.Packet, error) {
	if l.isClosing {
		l.messageMutex.Unlock()
		return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
	}
	l.messageMutex.Lock()
	l.Debug.Printf("flags&startTLS = %d", flags&startTLS)
	if l.isStartingTLS {
		l.messageMutex.Unlock()
		return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase."))
	}
	if flags&startTLS != 0 {
		if l.outstandingRequests != 0 {
			return nil, NewError(ErrorNetwork, errors.New("ldap: cannot StartTLS with outstanding requests"))
		} else {
			l.isStartingTLS = true
		}
	}
	l.outstandingRequests++

	l.messageMutex.Unlock()

	out := make(chan *ber.Packet)
	message := &messagePacket{
		Op:        MessageRequest,
		MessageID: packet.Children[0].Value.(int64),
		Packet:    packet,
		Channel:   out,
	}
	l.sendProcessMessage(message)
	return out, nil
}

func (l *Conn) finishMessage(messageID int64) {
	if l.isClosing {
		return
	}

	l.messageMutex.Lock()
	l.outstandingRequests--
	if l.isStartingTLS {
		l.isStartingTLS = false
	}
	l.messageMutex.Unlock()

	message := &messagePacket{
		Op:        MessageFinish,
		MessageID: messageID,
	}
	l.sendProcessMessage(message)
}

func (l *Conn) sendProcessMessage(message *messagePacket) bool {
	if l.isClosing {
		return false
	}
	l.wgSender.Add(1)
	l.chanMessage <- message
	l.wgSender.Done()
	return true
}

func (l *Conn) processMessages() {
	defer func() {
		for messageID, channel := range l.chanResults {
			l.Debug.Printf("Closing channel for MessageID %d", messageID)
			close(channel)
			delete(l.chanResults, messageID)
		}
		close(l.chanMessageID)
		l.chanConfirm <- true
		close(l.chanConfirm)
	}()

	var messageID int64 = 1
	for {
		select {
		case l.chanMessageID <- messageID:
			messageID++
		case messagePacket, ok := <-l.chanMessage:
			if !ok {
				l.Debug.Printf("Shutting down - message channel is closed")
				return
			}
			switch messagePacket.Op {
			case MessageQuit:
				l.Debug.Printf("Shutting down - quit message received")
				return
			case MessageRequest:
				// Add to message list and write to network
				l.Debug.Printf("Sending message %d", messagePacket.MessageID)
				l.chanResults[messagePacket.MessageID] = messagePacket.Channel
				// go routine
				buf := messagePacket.Packet.Bytes()

				_, err := l.conn.Write(buf)
				if err != nil {
					l.Debug.Printf("Error Sending Message: %s", err.Error())
					break
				}
			case MessageResponse:
				l.Debug.Printf("Receiving message %d", messagePacket.MessageID)
				if chanResult, ok := l.chanResults[messagePacket.MessageID]; ok {
					chanResult <- messagePacket.Packet
				} else {
					log.Printf("Received unexpected message %d", messagePacket.MessageID)
					ber.PrintPacket(messagePacket.Packet)
				}
			case MessageFinish:
				// Remove from message list
				l.Debug.Printf("Finished message %d", messagePacket.MessageID)
				close(l.chanResults[messagePacket.MessageID])
				delete(l.chanResults, messagePacket.MessageID)
			}
		}
	}
}

func (l *Conn) reader() {
	cleanstop := false
	defer func() {
		if !cleanstop {
			l.Close()
		}
	}()

	for {
		if cleanstop {
			l.Debug.Printf("reader clean stopping (without closing the connection)")
			return
		}
		packet, err := ber.ReadPacket(l.conn)
		if err != nil {
			l.Debug.Printf("reader error: %s", err.Error())
			return
		}
		addLDAPDescriptions(packet)
		if len(packet.Children) == 0 {
			l.Debug.Printf("Received bad ldap packet")
			continue
		}
		l.messageMutex.Lock()
		if l.isStartingTLS {
			cleanstop = true
		}
		l.messageMutex.Unlock()
		message := &messagePacket{
			Op:        MessageResponse,
			MessageID: packet.Children[0].Value.(int64),
			Packet:    packet,
		}
		if !l.sendProcessMessage(message) {
			return
		}

	}
}
<|endoftext|>


<commit_before>package quobar

import (
	"fmt"
	"image"
	"image/draw"
	"sort"
	"time"

	"github.com/BurntSushi/xgb/xproto"
	"github.com/BurntSushi/xgbutil"
	"github.com/BurntSushi/xgbutil/ewmh"
	"github.com/BurntSushi/xgbutil/xevent"
	"github.com/BurntSushi/xgbutil/xgraphics"
	"github.com/BurntSushi/xgbutil/xwindow"
)

func drawAll(ximg *xgraphics.Image, drawers []Drawer) error {
	offset := image.Pt(ximg.Bounds().Max.X, 0).Div(len(drawers))
	shape := image.Rect(0, 0, offset.X, ximg.Bounds().Max.Y)
	for idx, drawer := range drawers {
		sub := ximg.SubImage(shape.Add(offset.Mul(idx)))
		if sub == nil {
			return fmt.Errorf("buggy shape math: shape=%v offset=%v idx=%v", shape, offset, idx)
		}
		if err := drawer.Draw(sub); err != nil {
			return fmt.Errorf("drawer failed: %v", err)
		}
	}
	return nil
}

func stopMainloop(xu *xgbutil.XUtil, event interface{}) bool {
	xevent.Quit(xu)
	return true
}

// Main runs the main loop for quobar. It is available in library form
// to keep github.com/tv42/quobar/cmd/quobar short and easy to copy
// for editing.
func Main(defaultConfig Config) error {
	Xu, err := xgbutil.NewConn()
	if err != nil {
		return fmt.Errorf("cannot connect to X11: %v", err)
	}
	X := Xu.Conn()
	defer X.Close()

	setup := xproto.Setup(X)
	screen := setup.DefaultScreen(X)

	state := &State{
		Resolution: NewResolution(screen.HeightInPixels, screen.HeightInMillimeters),
		Config:     defaultConfig,
	}
	// TODO load config

	// TODO get plugins from config
	//
	// as a placeholder, just include all of them, but make sure it's
	// the same order on every run
	pluginNames := make([]string, 0, len(plugins))
	for k := range plugins {
		pluginNames = append(pluginNames, k)
	}
	sort.Strings(pluginNames)

	// TODO feed config to each plugin
	drawers := make([]Drawer, 0, len(plugins))
	for _, name := range pluginNames {
		p := plugins[name]
		if !p.first {
			continue
		}
		d, err := p.New(state)
		if err != nil {
			return fmt.Errorf("plugin error: %v", err)
		}
		drawers = append(drawers, d)
	}

	// Height of the status bar, in pixels.
	height := state.Resolution.Pixels(state.Config.HeightMillimeters)

	win, err := xwindow.Generate(Xu)
	if err != nil {
		return fmt.Errorf("cannot create X11 window: %v", err)
	}
	win.Create(Xu.RootWin(),
		0, int(screen.HeightInPixels)-height,
		int(screen.WidthInPixels), height,
		xproto.CwBackPixel, 0xffffff)
	win.Stack(xproto.StackModeBelow)

	// http://standards.freedesktop.org/wm-spec/wm-spec-latest.html

	if err := ewmh.WmWindowTypeSet(Xu, win.Id, []string{"_NET_WM_WINDOW_TYPE_DOCK"}); err != nil {
		return fmt.Errorf("cannot set window to be a dock: %v", err)
	}

	if err := ewmh.WmStateReq(Xu, win.Id, ewmh.StateAdd, "_NET_WM_STATE_BELOW"); err != nil {
		return fmt.Errorf("cannot lower window: %v", err)
	}

	if err := ewmh.WmNameSet(Xu, win.Id, "quobar"); err != nil {
		return fmt.Errorf("cannot set window title: %v", err)
	}
	win.Map()

	if err := ewmh.WmStrutSet(Xu, win.Id, &ewmh.WmStrut{
		Left:   0,
		Right:  0,
		Top:    0,
		Bottom: uint(height),
	}); err != nil {
		return fmt.Errorf("setting struts: %v", err)
	}

	errCh := make(chan error, 1)
	go func() {
		// xgbutil's quit mechanism is only meant to be used from the
		// same goroutine where xevent.Main is running (from the
		// callbacks). We'd really like to say `defer xevent.Quit(Xu)`
		// here, but have to do this weird thing (and wait for the
		// next event) to be goroutine safe.
		//
		// https://github.com/BurntSushi/xgbutil/issues/9
		defer xevent.HookFun(stopMainloop).Connect(Xu)
		defer close(errCh)
		ximg := xgraphics.New(Xu, image.Rect(0, 0, int(screen.WidthInPixels), height))
		defer ximg.Destroy()
		for {
			draw.Draw(ximg, ximg.Bounds(), image.NewUniform(state.Config.Background), image.ZP, draw.Src)

			if err := drawAll(ximg, drawers); err != nil {
				errCh <- fmt.Errorf("draw error: %v", err)
				return
			}

			if err := ximg.XSurfaceSet(win.Id); err != nil {
				errCh <- fmt.Errorf("XSurfaceSet: %v", err)
				return
			}
			ximg.XDraw()
			ximg.XPaint(win.Id)
			time.Sleep(1 * time.Second)
		}
	}()
	go xevent.Main(Xu)
	return <-errCh
}
<commit_msg>Implement space-dividing drawing without X11 dependency<commit_after>package quobar

import (
	"fmt"
	"image"
	"image/draw"
	"sort"
	"time"

	"github.com/BurntSushi/xgb/xproto"
	"github.com/BurntSushi/xgbutil"
	"github.com/BurntSushi/xgbutil/ewmh"
	"github.com/BurntSushi/xgbutil/xevent"
	"github.com/BurntSushi/xgbutil/xgraphics"
	"github.com/BurntSushi/xgbutil/xwindow"
)

type Image interface {
	draw.Image
	// SubImage provides a sub image of Image without copying image
	// data. The underlying type returned is expected to implement
	// draw.Image.
	SubImage(r image.Rectangle) image.Image
}

func drawAll(ximg Image, drawers []Drawer) error {
	offset := image.Pt(ximg.Bounds().Max.X, 0).Div(len(drawers))
	shape := image.Rect(0, 0, offset.X, ximg.Bounds().Max.Y)
	for idx, drawer := range drawers {
		sub := ximg.SubImage(shape.Add(offset.Mul(idx)))
		if sub == nil {
			return fmt.Errorf("buggy shape math: shape=%v offset=%v idx=%v", shape, offset, idx)
		}
		dr, ok := sub.(draw.Image)
		if !ok {
			return fmt.Errorf("drawer subimage is not drawable: %v", drawer)
		}
		if err := drawer.Draw(dr); err != nil {
			return fmt.Errorf("drawer failed: %v", err)
		}
	}
	return nil
}

func stopMainloop(xu *xgbutil.XUtil, event interface{}) bool {
	xevent.Quit(xu)
	return true
}

// Main runs the main loop for quobar. It is available in library form
// to keep github.com/tv42/quobar/cmd/quobar short and easy to copy
// for editing.
func Main(defaultConfig Config) error {
	Xu, err := xgbutil.NewConn()
	if err != nil {
		return fmt.Errorf("cannot connect to X11: %v", err)
	}
	X := Xu.Conn()
	defer X.Close()

	setup := xproto.Setup(X)
	screen := setup.DefaultScreen(X)

	state := &State{
		Resolution: NewResolution(screen.HeightInPixels, screen.HeightInMillimeters),
		Config:     defaultConfig,
	}
	// TODO load config

	// TODO get plugins from config
	//
	// as a placeholder, just include all of them, but make sure it's
	// the same order on every run
	pluginNames := make([]string, 0, len(plugins))
	for k := range plugins {
		pluginNames = append(pluginNames, k)
	}
	sort.Strings(pluginNames)

	// TODO feed config to each plugin
	drawers := make([]Drawer, 0, len(plugins))
	for _, name := range pluginNames {
		p := plugins[name]
		if !p.first {
			continue
		}
		d, err := p.New(state)
		if err != nil {
			return fmt.Errorf("plugin error: %v", err)
		}
		drawers = append(drawers, d)
	}

	// Height of the status bar, in pixels.
	height := state.Resolution.Pixels(state.Config.HeightMillimeters)

	win, err := xwindow.Generate(Xu)
	if err != nil {
		return fmt.Errorf("cannot create X11 window: %v", err)
	}
	win.Create(Xu.RootWin(),
		0, int(screen.HeightInPixels)-height,
		int(screen.WidthInPixels), height,
		xproto.CwBackPixel, 0xffffff)
	win.Stack(xproto.StackModeBelow)

	// http://standards.freedesktop.org/wm-spec/wm-spec-latest.html

	if err := ewmh.WmWindowTypeSet(Xu, win.Id, []string{"_NET_WM_WINDOW_TYPE_DOCK"}); err != nil {
		return fmt.Errorf("cannot set window to be a dock: %v", err)
	}

	if err := ewmh.WmStateReq(Xu, win.Id, ewmh.StateAdd, "_NET_WM_STATE_BELOW"); err != nil {
		return fmt.Errorf("cannot lower window: %v", err)
	}

	if err := ewmh.WmNameSet(Xu, win.Id, "quobar"); err != nil {
		return fmt.Errorf("cannot set window title: %v", err)
	}
	win.Map()

	if err := ewmh.WmStrutSet(Xu, win.Id, &ewmh.WmStrut{
		Left:   0,
		Right:  0,
		Top:    0,
		Bottom: uint(height),
	}); err != nil {
		return fmt.Errorf("setting struts: %v", err)
	}

	errCh := make(chan error, 1)
	go func() {
		// xgbutil's quit mechanism is only meant to be used from the
		// same goroutine where xevent.Main is running (from the
		// callbacks). We'd really like to say `defer xevent.Quit(Xu)`
		// here, but have to do this weird thing (and wait for the
		// next event) to be goroutine safe.
		//
		// https://github.com/BurntSushi/xgbutil/issues/9
		defer xevent.HookFun(stopMainloop).Connect(Xu)
		defer close(errCh)
		ximg := xgraphics.New(Xu, image.Rect(0, 0, int(screen.WidthInPixels), height))
		defer ximg.Destroy()
		for {
			draw.Draw(ximg, ximg.Bounds(), image.NewUniform(state.Config.Background), image.ZP, draw.Src)

			if err := drawAll(ximg, drawers); err != nil {
				errCh <- fmt.Errorf("draw error: %v", err)
				return
			}

			if err := ximg.XSurfaceSet(win.Id); err != nil {
				errCh <- fmt.Errorf("XSurfaceSet: %v", err)
				return
			}
			ximg.XDraw()
			ximg.XPaint(win.Id)
			time.Sleep(1 * time.Second)
		}
	}()
	go xevent.Main(Xu)
	return <-errCh
}
<|endoftext|>