<commit_before>// Author: Yoshiyuki Koyanagi <moutend@gmail.com>
// License: mIT

// Package main implements mediumctl.
package main

import (
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	medium "github.com/moutend/go-medium"
	"github.com/skratchdot/open-golang/open"
)

type token struct {
	ApplicationID     string
	ApplicationSecret string
	AccessToken       string
	ExpiresAt         int
}

var (
	version       = "v0.1.1"
	revision      = "latest"
	tokenFilePath string
)

const tokenFileName = ".mediumctl"

func showPostedArticleInfo(p *medium.PostedArticle) {
	fmt.Println("Your article was successfully posted.")
	fmt.Printf("Title: %s\n", p.Title)
	fmt.Printf("Status: %s\n", p.PublishStatus)
	if len(p.Tags) > 0 {
		fmt.Printf("Tags: %s\n", strings.Join(p.Tags, " "))
	}
	fmt.Printf("URL: %s\n", p.URL)
	if p.CanonicalURL != "" {
		fmt.Printf("Canonical URL: %s\n", p.CanonicalURL)
	}
	return
}

func parseArticle(filename string) (article medium.Article, publicationNumber int, err error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("%s is empty", filename)
		return
	}

	var (
		title        string
		tags         []string
		content      string
		format       string
		license      string
		status       string
		canonicalURL string
		notify       bool
	)
	format = "markdown"
	if strings.HasSuffix(filename, "html") || strings.HasSuffix(filename, "htm") {
		format = "html"
	}
	title = "untitled"
	status = "public"
	lines := strings.Split(string(b), "\n")

	for i, line := range lines[1:] {
		if strings.HasPrefix(line, "---") {
			content = strings.Join(lines[i+2:], "\n")
			break
		}
		if strings.HasPrefix(line, "number: ") {
			publicationNumber, err = strconv.Atoi(line[len("number: "):])
			if err != nil {
				return
			}
		}
		if strings.HasPrefix(line, "title: ") {
			title = line[len("title: "):]
		}
		if strings.HasPrefix(line, "tags: ") {
			tags = strings.Split(line[len("tags: "):], " ")
		}
		if strings.HasPrefix(line, "notify: true") {
			notify = true
		}
		if strings.HasPrefix(line, "status: ") {
			status = line[len("status: "):]
		}
		if strings.HasPrefix(line, "license: ") {
			license = line[len("license: "):]
		}
		if strings.HasPrefix(line, "canonicalURL: ") {
			canonicalURL = line[len("canonicalURL: "):]
		}
	}
	if content == "" {
		content = strings.Join(lines, "\n")
	}
	article = medium.Article{
		Title:           title,
		ContentFormat:   format,
		Content:         content,
		CanonicalURL:    canonicalURL,
		Tags:            tags,
		PublishStatus:   status,
		License:         license,
		NotifyFollowers: notify,
	}
	return
}
func getCode(clientID, redirectURI string) (code string, err error) {
	l, err := net.Listen("tcp", "192.168.1.107:4000")
	if err != nil {
		return
	}
	defer l.Close()

	type value struct {
		code  string
		error error
	}
	quit := make(chan value)
	go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte(`<script>window.open("about:blank","_self").close()</script>`))
		w.(http.Flusher).Flush()
		c := req.FormValue("code")
		e := req.FormValue("error")
		v := value{
			code:  c,
			error: nil,
		}
		if e != "" {
			v.error = fmt.Errorf(e)
		}
		quit <- v
	}))
	stateBytes := make([]byte, 88)
	_, err = rand.Read(stateBytes)
	if err != nil {
		return
	}
	state := fmt.Sprintf("%x", stateBytes)
	scope := "basicProfile,listPublications,publishPost"
	redirectURI = url.QueryEscape(redirectURI)
	q := fmt.Sprintf("client_id=%s&scope=%s&state=%s&response_type=code&redirect_uri=%s", clientID, scope, state, redirectURI)
	p := "https://medium.com/m/oauth/authorize?" + q
	if err = open.Start(p); err != nil {
		return
	}
	select {
	case v := <-quit:
		if v.error != nil {
			return "", v.error
		}
		return v.code, nil
	case <-time.After(60 * time.Second):
		return "", fmt.Errorf("timeout")
	}
}

func saveToken(clientID, clientSecret string, t *medium.Token) (err error) {
	b, err := json.Marshal(token{
		ApplicationID:     clientID,
		ApplicationSecret: clientSecret,
		AccessToken:       t.AccessToken,
		ExpiresAt:         t.ExpiresAt,
	})
	if err != nil {
		return
	}
	err = ioutil.WriteFile(tokenFilePath, b, 0644)
	return
}

func loadToken() (*token, error) {
	b, err := ioutil.ReadFile(tokenFilePath)
	if err != nil {
		return nil, fmt.Errorf("API token is not set. Please run 'auth' at first")
	}
	var t token
	err = json.Unmarshal(b, &t)
	return &t, err
}

func main() {
	err := run(os.Args)

	if err != nil {
		log.New(os.Stderr, "error: ", 0).Fatal(err)
		os.Exit(1)
	}

	os.Exit(0)
}
func run(args []string) (err error) {
	if len(args) < 2 {
		return helpCommand(args)
	}
	u, err := user.Current()
	if err != nil {
		return
	}
	tokenFilePath = filepath.Join(u.HomeDir, tokenFileName)
	switch args[1] {
	case "oauth":
		err = authCommand(args)
	case "o":
		err = authCommand(args)
	case "i":
		err = infoCommand(args)
	case "info":
		err = infoCommand(args)
	case "p":
		err = postCommand(args, false)
	case "publication":
		err = postCommand(args, false)
	case "u":
		err = postCommand(args, true)
	case "user":
		err = postCommand(args, true)
	case "v":
		err = versionCommand(args)
	case "version":
		err = versionCommand(args)
	case "h":
		err = helpCommand(args)
	case "help":
		err = helpCommand(args)
	default:
		fmt.Fprintf(os.Stderr, "%s: '%s' is not a %s subcommand.\n", args[0], args[1], args[0])
		err = helpCommand(args)
	}
	return
}

func authCommand(args []string) (err error) {
	var (
		clientIDFlag     string
		clientSecretFlag string
		debugFlag        bool
		redirectURIFlag  string
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.StringVar(&redirectURIFlag, "u", "", "Redirect URI for OAuth application.")
	f.StringVar(&clientIDFlag, "i", "", "Client ID of OAuth application.")
	f.StringVar(&clientSecretFlag, "s", "", "Client secret of OAuth application.")
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])
	if redirectURIFlag == "" {
		return fmt.Errorf("please specify redirect URI")
	}
	if clientIDFlag == "" {
		return fmt.Errorf("please specify client ID")
	}
	if clientSecretFlag == "" {
		return fmt.Errorf("please specify client secret")
	}

	code, err := getCode(clientIDFlag, redirectURIFlag)
	if err != nil {
		return
	}
	c := medium.NewClient(clientIDFlag, clientSecretFlag, "")
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	token, err := c.Token(code, redirectURIFlag)
	if err != nil {
		return
	}
	if err = saveToken(clientIDFlag, clientSecretFlag, token); err != nil {
		return
	}
	fmt.Printf("Your API token was successfully saved in '%s'.\n", tokenFilePath)
	fmt.Println("Note: This file should be treated as the password and please do NOT expose it.")
	return
}

func infoCommand(args []string) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	fmt.Printf("You are logged in as:\n\n")
	fmt.Printf("Name: %s\n", u.Name)
	fmt.Printf("Username: %s\n", u.Username)
	fmt.Printf("URL: %s", u.URL)
	fmt.Printf("\n")

	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		fmt.Println("You have no publications yet.")
		return
	}
	fmt.Printf("\nYou have publication(s) below:\n\n")
	for i, p := range ps {
		fmt.Printf("Number: %d\n", i)
		fmt.Printf("Name: %s\n", p.Name)
		fmt.Printf("Description: %s\n", p.Description)
		fmt.Printf("URL: %s\n\n", p.URL)
	}
	return
}

func postCommand(args []string, userFlag bool) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	article, publicationNumber, err := parseArticle(f.Args()[0])
	if err != nil {
		return
	}
	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	if userFlag {
		p, err := u.Post(article)
		if err != nil {
			return err
		}
		showPostedArticleInfo(p)
		return nil
	}
	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		return fmt.Errorf("you have no publications yet")
	}
	if publicationNumber < 0 || publicationNumber > len(ps)-1 {
		err = fmt.Errorf("publication number '%d' is invalid", publicationNumber)
		return
	}
	p, err := ps[publicationNumber].Post(article)
	if err != nil {
		return
	}
	showPostedArticleInfo(p)
	return
}

func versionCommand(args []string) (err error) {
	fmt.Printf("%s-%s\n", version, revision)
	return
}

func helpCommand(args []string) (err error) {
	fmt.Println(`usage: mediumctl <command> [options]

Commands:
  oauth, o
    Setting up API token for Medium with OAuth.
  info, i
    Show the information about current user and its publications.
  user, u
    Post HTML or Markdown file to current user profile.
  publication, p
    Post HTML or Markdown file to current user's publication.
  version, v
  Show version and revision information.
  help, h
    Show this message.

For more information, please see https://github.com/moutend/mediumctl.`)
	return
}
<commit_msg>Support publishedAt property and rename some properties<commit_after>// Author: Yoshiyuki Koyanagi <moutend@gmail.com>
// License: mIT

// Package main implements mediumctl.
package main

import (
	"crypto/rand"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	medium "github.com/moutend/go-medium"
	"github.com/skratchdot/open-golang/open"
)

type token struct {
	ApplicationID     string
	ApplicationSecret string
	AccessToken       string
	ExpiresAt         int
}

var (
	version       = "v0.1.1"
	revision      = "latest"
	tokenFilePath string
)

const tokenFileName = ".mediumctl"

func showPostedArticleInfo(p *medium.PostedArticle) {
	fmt.Printf("Your article was successfully posted.\n\n")
	fmt.Printf("title: %s\n", p.Title)
	fmt.Printf("publishStatus: %s\n", p.PublishStatus)
	if len(p.Tags) > 0 {
		fmt.Printf("tags: %s\n", strings.Join(p.Tags, " "))
	}
	fmt.Printf("URL: %s\n", p.URL)
	if p.CanonicalURL != "" {
		fmt.Printf("canonicalURL: %s\n", p.CanonicalURL)
	}
	return
}

func parseArticle(filename string) (article medium.Article, publicationNumber int, err error) {
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return
	}
	if len(b) == 0 {
		err = fmt.Errorf("%s is empty", filename)
		return
	}

	var (
		content         string
		contentFormat   string
		canonicalURL    string
		license         string
		notifyFollowers bool
		publishedAt     string
		publishStatus   string
		tags            []string
		title           string
	)
	contentFormat = "markdown"
	if strings.HasSuffix(filename, "html") || strings.HasSuffix(filename, "htm") {
		contentFormat = "html"
	}
	lines := strings.Split(string(b), "\n")

	for i, line := range lines[1:] {
		if strings.HasPrefix(line, "---") {
			content = strings.Join(lines[i+2:], "\n")
			break
		}
		if strings.HasPrefix(line, "canonicalURL: ") {
			canonicalURL = line[len("canonicalURL: "):]
		}
		if strings.HasPrefix(line, "license: ") {
			license = line[len("license: "):]
		}
		if strings.HasPrefix(line, "number: ") {
			publicationNumber, err = strconv.Atoi(line[len("number: "):])
			if err != nil {
				return
			}
		}
		if strings.HasPrefix(line, "notifyFollowers: true") {
			notifyFollowers = true
		}
		if strings.HasPrefix(line, "publishedAt: ") {
			publishedAt = line[len("publishedAt: "):]
		}
		if strings.HasPrefix(line, "publishStatus: ") {
			publishStatus = line[len("publishStatus: "):]
		}
		if strings.HasPrefix(line, "tags: ") {
			tags = strings.Split(line[len("tags: "):], " ")
		}
		if strings.HasPrefix(line, "title: ") {
			title = line[len("title: "):]
		}
	}
	if content == "" {
		content = strings.Join(lines, "\n")
	}
	article = medium.Article{
		Title:           title,
		ContentFormat:   contentFormat,
		Content:         content,
		CanonicalURL:    canonicalURL,
		Tags:            tags,
		PublishStatus:   publishStatus,
		PublishedAt:     publishedAt,
		License:         license,
		NotifyFollowers: notifyFollowers,
	}
	return
}
func getCode(clientID, redirectURI string) (code string, err error) {
	l, err := net.Listen("tcp", "192.168.1.107:4000")
	if err != nil {
		return
	}
	defer l.Close()

	type value struct {
		code  string
		error error
	}
	quit := make(chan value)
	go http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Write([]byte(`<script>window.open("about:blank","_self").close()</script>`))
		w.(http.Flusher).Flush()
		c := req.FormValue("code")
		e := req.FormValue("error")
		v := value{
			code:  c,
			error: nil,
		}
		if e != "" {
			v.error = fmt.Errorf(e)
		}
		quit <- v
	}))
	stateBytes := make([]byte, 88)
	_, err = rand.Read(stateBytes)
	if err != nil {
		return
	}
	state := fmt.Sprintf("%x", stateBytes)
	scope := "basicProfile,listPublications,publishPost"
	redirectURI = url.QueryEscape(redirectURI)
	q := fmt.Sprintf("client_id=%s&scope=%s&state=%s&response_type=code&redirect_uri=%s", clientID, scope, state, redirectURI)
	p := "https://medium.com/m/oauth/authorize?" + q
	if err = open.Start(p); err != nil {
		return
	}
	select {
	case v := <-quit:
		if v.error != nil {
			return "", v.error
		}
		return v.code, nil
	case <-time.After(60 * time.Second):
		return "", fmt.Errorf("timeout")
	}
}

func saveToken(clientID, clientSecret string, t *medium.Token) (err error) {
	b, err := json.Marshal(token{
		ApplicationID:     clientID,
		ApplicationSecret: clientSecret,
		AccessToken:       t.AccessToken,
		ExpiresAt:         t.ExpiresAt,
	})
	if err != nil {
		return
	}
	err = ioutil.WriteFile(tokenFilePath, b, 0644)
	return
}

func loadToken() (*token, error) {
	b, err := ioutil.ReadFile(tokenFilePath)
	if err != nil {
		return nil, fmt.Errorf("API token is not set. Please run 'auth' at first")
	}
	var t token
	err = json.Unmarshal(b, &t)
	return &t, err
}

func main() {
	err := run(os.Args)

	if err != nil {
		log.New(os.Stderr, "error: ", 0).Fatal(err)
		os.Exit(1)
	}

	os.Exit(0)
}
func run(args []string) (err error) {
	if len(args) < 2 {
		return helpCommand(args)
	}
	u, err := user.Current()
	if err != nil {
		return
	}
	tokenFilePath = filepath.Join(u.HomeDir, tokenFileName)
	switch args[1] {
	case "oauth":
		err = authCommand(args)
	case "o":
		err = authCommand(args)
	case "i":
		err = infoCommand(args)
	case "info":
		err = infoCommand(args)
	case "p":
		err = postCommand(args, false)
	case "publication":
		err = postCommand(args, false)
	case "u":
		err = postCommand(args, true)
	case "user":
		err = postCommand(args, true)
	case "v":
		err = versionCommand(args)
	case "version":
		err = versionCommand(args)
	case "h":
		err = helpCommand(args)
	case "help":
		err = helpCommand(args)
	default:
		fmt.Fprintf(os.Stderr, "%s: '%s' is not a %s subcommand.\n", args[0], args[1], args[0])
		err = helpCommand(args)
	}
	return
}

func authCommand(args []string) (err error) {
	var (
		clientIDFlag     string
		clientSecretFlag string
		debugFlag        bool
		redirectURIFlag  string
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.StringVar(&redirectURIFlag, "u", "", "Redirect URI for OAuth application.")
	f.StringVar(&clientIDFlag, "i", "", "Client ID of OAuth application.")
	f.StringVar(&clientSecretFlag, "s", "", "Client secret of OAuth application.")
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])
	if redirectURIFlag == "" {
		return fmt.Errorf("please specify redirect URI")
	}
	if clientIDFlag == "" {
		return fmt.Errorf("please specify client ID")
	}
	if clientSecretFlag == "" {
		return fmt.Errorf("please specify client secret")
	}

	code, err := getCode(clientIDFlag, redirectURIFlag)
	if err != nil {
		return
	}
	c := medium.NewClient(clientIDFlag, clientSecretFlag, "")
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	token, err := c.Token(code, redirectURIFlag)
	if err != nil {
		return
	}
	if err = saveToken(clientIDFlag, clientSecretFlag, token); err != nil {
		return
	}
	fmt.Printf("Your API token was successfully saved in '%s'.\n", tokenFilePath)
	fmt.Println("Note: This file should be treated as the password and please do NOT expose it.")
	return
}

func infoCommand(args []string) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	fmt.Printf("You are logged in as:\n\n")
	fmt.Printf("Name: %s\n", u.Name)
	fmt.Printf("Username: %s\n", u.Username)
	fmt.Printf("URL: %s", u.URL)
	fmt.Printf("\n")

	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		fmt.Println("You have no publications yet.")
		return
	}
	fmt.Printf("\nYou have publication(s) below:\n\n")
	for i, p := range ps {
		fmt.Printf("Number: %d\n", i)
		fmt.Printf("Name: %s\n", p.Name)
		fmt.Printf("Description: %s\n", p.Description)
		fmt.Printf("URL: %s\n\n", p.URL)
	}
	return
}

func postCommand(args []string, userFlag bool) (err error) {
	var (
		debugFlag bool
	)

	f := flag.NewFlagSet(fmt.Sprintf("%s %s", args[0], args[1]), flag.ExitOnError)
	f.BoolVar(&debugFlag, "debug", false, "Enable debug output.")
	f.Parse(args[2:])

	article, publicationNumber, err := parseArticle(f.Args()[0])
	if err != nil {
		return
	}
	t, err := loadToken()
	if err != nil {
		return
	}
	c := medium.NewClient(t.ApplicationID, t.ApplicationSecret, t.AccessToken)
	if debugFlag {
		c.SetLogger(log.New(os.Stdout, "debug: ", 0))
	}
	u, err := c.User()
	if err != nil {
		return
	}
	if userFlag {
		p, err := u.Post(article)
		if err != nil {
			return err
		}
		showPostedArticleInfo(p)
		return nil
	}
	ps, err := u.Publications()
	if err != nil {
		return
	}
	if len(ps) == 0 {
		return fmt.Errorf("you have no publications yet")
	}
	if publicationNumber < 0 || publicationNumber > len(ps)-1 {
		err = fmt.Errorf("publication number '%d' is invalid", publicationNumber)
		return
	}
	p, err := ps[publicationNumber].Post(article)
	if err != nil {
		return
	}
	showPostedArticleInfo(p)
	return
}

func versionCommand(args []string) (err error) {
	fmt.Printf("%s-%s\n", version, revision)
	return
}

func helpCommand(args []string) (err error) {
	fmt.Println(`usage: mediumctl <command> [options]

Commands:
  oauth, o
    Setting up API token for Medium with OAuth.
  info, i
    Show the information about current user and its publications.
  user, u
    Post HTML or Markdown file to current user profile.
  publication, p
    Post HTML or Markdown file to current user's publication.
  version, v
  Show version and revision information.
  help, h
    Show this message.

For more information, please see https://github.com/moutend/mediumctl.`)
	return
}
<|endoftext|>


<commit_before>// © 2014 the flac Authors under the MIT license. See AUTHORS for the list of authors.

// +build ignore

package main

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"os"

	"github.com/eaburns/flac"

	//	"github.com/davecheney/profile"
)

func main() {
	//	defer profile.Start(profile.CPUProfile).Stop()
	data, meta, err := flac.Decode(bufio.NewReader(os.Stdin))
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	writeWAV(data, meta)
}

type wavFmt struct {
	format        int16
	channels      int16
	sampleRate    int32
	dataRate      int32
	dataBlockSize int16
	bitsPerSample int16
}

const pcmFormat = 1

func writeWAV(data []byte, meta flac.MetaData) {
	wdata := bytes.NewBuffer(nil)
	wdata.WriteString("WAVE")

	wdata.WriteString("fmt ")
	binary.Write(wdata, binary.LittleEndian, uint32(16))
	binary.Write(wdata, binary.LittleEndian, wavFmt{
		format:        pcmFormat,
		channels:      int16(meta.NChannels),
		sampleRate:    int32(meta.SampleRate),
		dataRate:      int32(meta.NChannels * meta.SampleRate * (meta.BitsPerSample / 8)),
		dataBlockSize: int16(meta.NChannels * (meta.BitsPerSample / 8)),
		bitsPerSample: int16(meta.BitsPerSample),
	})
	wdata.WriteString("data")
	binary.Write(wdata, binary.LittleEndian, uint32(len(data)))
	wdata.Write(data)

	wav, err := os.Create("out.wav")
	if err != nil {
		panic(err)
	}
	defer wav.Close()

	wav.WriteString("RIFF")
	binary.Write(wav, binary.LittleEndian, uint32(len(wdata.Bytes())))
	wav.Write(wdata.Bytes())
}
<commit_msg>go fmt main<commit_after>// © 2014 the flac Authors under the MIT license. See AUTHORS for the list of authors.

// +build ignore

package main

import (
	"bufio"
	"bytes"
	"encoding/binary"
	"fmt"
	"os"

	"github.com/eaburns/flac"
	//	"github.com/davecheney/profile"
)

func main() {
	//	defer profile.Start(profile.CPUProfile).Stop()
	data, meta, err := flac.Decode(bufio.NewReader(os.Stdin))
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	writeWAV(data, meta)
}

type wavFmt struct {
	format        int16
	channels      int16
	sampleRate    int32
	dataRate      int32
	dataBlockSize int16
	bitsPerSample int16
}

const pcmFormat = 1

func writeWAV(data []byte, meta flac.MetaData) {
	wdata := bytes.NewBuffer(nil)
	wdata.WriteString("WAVE")

	wdata.WriteString("fmt ")
	binary.Write(wdata, binary.LittleEndian, uint32(16))
	binary.Write(wdata, binary.LittleEndian, wavFmt{
		format:        pcmFormat,
		channels:      int16(meta.NChannels),
		sampleRate:    int32(meta.SampleRate),
		dataRate:      int32(meta.NChannels * meta.SampleRate * (meta.BitsPerSample / 8)),
		dataBlockSize: int16(meta.NChannels * (meta.BitsPerSample / 8)),
		bitsPerSample: int16(meta.BitsPerSample),
	})
	wdata.WriteString("data")
	binary.Write(wdata, binary.LittleEndian, uint32(len(data)))
	wdata.Write(data)

	wav, err := os.Create("out.wav")
	if err != nil {
		panic(err)
	}
	defer wav.Close()

	wav.WriteString("RIFF")
	binary.Write(wav, binary.LittleEndian, uint32(len(wdata.Bytes())))
	wav.Write(wdata.Bytes())
}
<|endoftext|>


<commit_before>package main

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

	"github.com/Sirupsen/logrus"
	"github.com/jessevdk/go-flags"
	"github.com/justone/pmb/api"
)

var opts struct {
	Verbose bool    `short:"v" long:"verbose" description:"Show verbose debug information."`
	Primary string  `short:"m" long:"pmb-uri" description:"Primary PMB URI."`
	Level   float64 `short:"l" long:"level" description:"Level at which to send notifications." default:"4"`
	Host    string  `short:"h" long:"host" description:"Host to listen on." default:"0.0.0.0"`
	Port    string  `short:"p" long:"port" description:"Port to listen on." default:"3000"`
}

func main() {

	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(1)
	}

	bus := pmb.GetPMB(opts.Primary)
	id := pmb.GenerateRandomID("github")

	if opts.Verbose {
		logrus.SetLevel(logrus.DebugLevel)
	} else {
		logrus.SetLevel(logrus.InfoLevel)
	}
	logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})

	conn, err := bus.ConnectClient(id, false)
	if err != nil {
		logrus.Warnf("Error connecting to PMB: %s", err)
		os.Exit(1)
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Unable to read request body: "+err.Error(), http.StatusInternalServerError)
			return
		}
		var eventName string
		if eventHeaders, ok := r.Header["X-Github-Event"]; ok {
			eventName = eventHeaders[0]
		} else {
			logrus.Warnf("Github event name not found")
			return
		}

		eventJSON := string(body)

		notification, err := parseEvent(eventName, eventJSON)
		if err != nil {
			logrus.Warnf("Unable to parse event %s: %s, body: %s", eventName, err, eventJSON)
			return
		}

		notification.Level = opts.Level

		go func() {
			pmb.SendNotification(conn, *notification)
		}()
	})

	http.ListenAndServe(fmt.Sprintf("%s:%s", opts.Host, opts.Port), nil)
}
<commit_msg>adding more logging for requests<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/jessevdk/go-flags"
	"github.com/justone/pmb/api"
)

var opts struct {
	Verbose bool    `short:"v" long:"verbose" description:"Show verbose debug information."`
	Primary string  `short:"m" long:"pmb-uri" description:"Primary PMB URI."`
	Level   float64 `short:"l" long:"level" description:"Level at which to send notifications." default:"4"`
	Host    string  `short:"h" long:"host" description:"Host to listen on." default:"0.0.0.0"`
	Port    string  `short:"p" long:"port" description:"Port to listen on." default:"3000"`
}

func main() {

	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(1)
	}

	bus := pmb.GetPMB(opts.Primary)
	id := pmb.GenerateRandomID("github")

	if opts.Verbose {
		logrus.SetLevel(logrus.DebugLevel)
	} else {
		logrus.SetLevel(logrus.InfoLevel)
	}
	logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})

	conn, err := bus.ConnectClient(id, false)
	if err != nil {
		logrus.Warnf("Error connecting to PMB: %s", err)
		os.Exit(1)
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var ip string
		if realIP, ok := r.Header["X-Real-Ip"]; ok {
			ip = realIP[0]
		} else {
			ip = r.RemoteAddr
		}
		logrus.Infof(strings.Join([]string{r.RequestURI, ip, r.Method, fmt.Sprintf("%s", r.Header)}, " "))

		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Unable to read request body: "+err.Error(), http.StatusInternalServerError)
			return
		}
		var eventName string
		if eventHeaders, ok := r.Header["X-Github-Event"]; ok {
			eventName = eventHeaders[0]
		} else {
			logrus.Warnf("Github event name not found")
			return
		}

		eventJSON := string(body)

		notification, err := parseEvent(eventName, eventJSON)
		if err != nil {
			logrus.Warnf("Unable to parse event %s: %s, body: %s", eventName, err, eventJSON)
			return
		}

		notification.Level = opts.Level

		logrus.Infof("Sending notification: %s", notification)
		go func() {
			pmb.SendNotification(conn, *notification)
		}()
	})

	http.ListenAndServe(fmt.Sprintf("%s:%s", opts.Host, opts.Port), nil)
}
<|endoftext|>


<commit_before>// Copyright 2015 The present-tex 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"html/template"
	"log"
	"os"
	"path"
	"strings"

	"golang.org/x/tools/present"
)

func printf(format string, args ...interface{}) (int, error) {
	return fmt.Fprintf(os.Stderr, format, args...)
}

func main() {
	flag.Parse()
	input := flag.Arg(0)
	output := input
	if flag.NArg() > 1 {
		output = flag.Arg(1)
	} else {
		output = input
		if strings.HasSuffix(output, ".slide") {
			output = output[:len(output)-len(".slide")] + ".pdf"
		} else {
			output += ".pdf"
		}
	}
	printf("input:  [%s]...\n", input)
	printf("output: [%s]...\n", output)

	f, err := os.Open(input)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	doc, err := present.Parse(f, input, 0)
	if err != nil {
		log.Fatal(err)
	}

	printf("doc:\ntitle: %q\nsub: %q\ntime: %v\nauthors: %v\ntags: %v\n",
		doc.Title, doc.Subtitle, doc.Time, doc.Authors,
		doc.Tags,
	)
	/*
		for _, section := range doc.Sections {
			printf("--- section %v %q---\n", section.Number, section.Title)
			for _, elem := range section.Elem {
				switch elem := elem.(type) {
				default:
					printf("%#v\n", elem)
				case present.Code:
					printf("code: %s\n", string(elem.Raw))
				}
			}
		}
	*/

	tmpl, err := initTemplates("templates")
	if err != nil {
		log.Fatal(err)
	}

	buf := new(bytes.Buffer)
	err = doc.Render(buf, tmpl)
	if err != nil {
		log.Fatal(err)
	}

	out := bytes.Replace(buf.Bytes(), []byte(`"`), []byte(`"`), -1)
	os.Stdout.Write(out)
}

func initTemplates(base string) (*template.Template, error) {
	fname := path.Join(base, "beamer.tmpl")
	tmpl := template.New("").Funcs(funcs).Delims("<<", ">>")
	_, err := tmpl.ParseFiles(fname)
	if err != nil {
		return nil, err
	}

	return tmpl, err
}

// renderElem implements the elem template function, used to render
// sub-templates.
func renderElem(t *template.Template, e present.Elem) (template.HTML, error) {
	var data interface{} = e
	if s, ok := e.(present.Section); ok {
		data = struct {
			present.Section
			Template *template.Template
		}{s, t}
	}
	return execTemplate(t, e.TemplateName(), data)
}

var (
	funcs = template.FuncMap{}
)

func init() {
	funcs["elem"] = renderElem
	funcs["stringFromBytes"] = func(raw []byte) string { return string(raw) }
	funcs["join"] = func(lines []string) string { return strings.Join(lines, "\n") }
	funcs["nodot"] = func(s string) string {
		if strings.HasPrefix(s, ".") {
			return s[1:]
		}
		return s
	}
}

// execTemplate is a helper to execute a template and return the output as a
// template.HTML value.
func execTemplate(t *template.Template, name string, data interface{}) (template.HTML, error) {
	b := new(bytes.Buffer)
	err := t.ExecuteTemplate(b, name, data)
	if err != nil {
		return "", err
	}
	return template.HTML(b.String()), nil
}
<commit_msg>main: unescape HTML<commit_after>// Copyright 2015 The present-tex 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"html/template"
	"log"
	"os"
	"path"
	"strings"

	"golang.org/x/tools/present"
)

func printf(format string, args ...interface{}) (int, error) {
	return fmt.Fprintf(os.Stderr, format, args...)
}

func main() {
	flag.Parse()
	input := flag.Arg(0)
	output := input
	if flag.NArg() > 1 {
		output = flag.Arg(1)
	} else {
		output = input
		if strings.HasSuffix(output, ".slide") {
			output = output[:len(output)-len(".slide")] + ".pdf"
		} else {
			output += ".pdf"
		}
	}
	printf("input:  [%s]...\n", input)
	printf("output: [%s]...\n", output)

	f, err := os.Open(input)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	doc, err := present.Parse(f, input, 0)
	if err != nil {
		log.Fatal(err)
	}

	printf("doc:\ntitle: %q\nsub: %q\ntime: %v\nauthors: %v\ntags: %v\n",
		doc.Title, doc.Subtitle, doc.Time, doc.Authors,
		doc.Tags,
	)
	/*
		for _, section := range doc.Sections {
			printf("--- section %v %q---\n", section.Number, section.Title)
			for _, elem := range section.Elem {
				switch elem := elem.(type) {
				default:
					printf("%#v\n", elem)
				case present.Code:
					printf("code: %s\n", string(elem.Raw))
				}
			}
		}
	*/

	tmpl, err := initTemplates("templates")
	if err != nil {
		log.Fatal(err)
	}

	buf := new(bytes.Buffer)
	err = doc.Render(buf, tmpl)
	if err != nil {
		log.Fatal(err)
	}

	out := unescapeHTML(buf.Bytes())

	switch output {
	case "":
		os.Stdout.Write(out)
	default:
		tex, err := os.Create(output)
		if err != nil {
			log.Fatalf("could not create output file [%s]: %v\n", err)
		}
		defer tex.Close()

		_, err = tex.Write(out)
		if err != nil {
			log.Fatalf("could not fill output file [%s]: %v\n", err)
		}
		err = tex.Close()
		if err != nil {
			log.Fatalf("could not close output file [%s]: %v\n", err)
		}
	}
}

func unescapeHTML(data []byte) []byte {
	out := make([]byte, len(data))
	copy(out, data)
	for _, r := range []struct {
		old string
		new string
	}{
		{
			old: "<",
			new: "<",
		},
		{
			old: ">",
			new: ">",
		},
		{
			old: """,
			new: `"`,
		},
		{
			old: """,
			new: `"`,
		},
		{
			old: "&",
			new: "&",
		},
		{
			old: " ",
			new: " ",
		},
	} {
		out = bytes.Replace(out, []byte(r.old), []byte(r.new), -1)
	}
	return out
}

func initTemplates(base string) (*template.Template, error) {
	fname := path.Join(base, "beamer.tmpl")
	tmpl := template.New("").Funcs(funcs).Delims("<<", ">>")
	_, err := tmpl.ParseFiles(fname)
	if err != nil {
		return nil, err
	}

	return tmpl, err
}

// renderElem implements the elem template function, used to render
// sub-templates.
func renderElem(t *template.Template, e present.Elem) (template.HTML, error) {
	var data interface{} = e
	if s, ok := e.(present.Section); ok {
		data = struct {
			present.Section
			Template *template.Template
		}{s, t}
	}
	return execTemplate(t, e.TemplateName(), data)
}

var (
	funcs = template.FuncMap{}
)

func init() {
	funcs["elem"] = renderElem
	funcs["stringFromBytes"] = func(raw []byte) string { return string(raw) }
	funcs["join"] = func(lines []string) string { return strings.Join(lines, "\n") }
	funcs["nodot"] = func(s string) string {
		if strings.HasPrefix(s, ".") {
			return s[1:]
		}
		return s
	}
}

// execTemplate is a helper to execute a template and return the output as a
// template.HTML value.
func execTemplate(t *template.Template, name string, data interface{}) (template.HTML, error) {
	b := new(bytes.Buffer)
	err := t.ExecuteTemplate(b, name, data)
	if err != nil {
		return "", err
	}
	return template.HTML(b.String()), nil
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2017, UPMC Enterprises
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name UPMC Enterprises nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL UPMC ENTERPRISES BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*/

package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ecr"
	flag "github.com/spf13/pflag"
	"github.com/upmc-enterprises/registry-creds/k8sutil"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"k8s.io/client-go/pkg/api/v1"
)

const (
	dockerCfgTemplate  = `{"%s":{"username":"oauth2accesstoken","password":"%s","email":"none"}}`
	dockerJSONTemplate = `{"auths":{"%s":{"auth":"%s","email":"none"}}}`
)

var (
	flags               = flag.NewFlagSet("", flag.ContinueOnError)
	argKubecfgFile      = flags.String("kubecfg-file", "", `Location of kubecfg file for access to kubernetes master service; --kube_master_url overrides the URL part of this; if neither this nor --kube_master_url are provided, defaults to service account tokens`)
	argKubeMasterURL    = flags.String("kube-master-url", "", `URL to reach kubernetes master. Env variables in this flag will be expanded.`)
	argAWSSecretName    = flags.String("aws-secret-name", "awsecr-cred", `Default aws secret name`)
	argGCRSecretName    = flags.String("gcr-secret-name", "gcr-secret", `Default gcr secret name`)
	argDefaultNamespace = flags.String("default-namespace", "default", `Default namespace`)
	argGCRURL           = flags.String("gcr-url", "https://gcr.io", `Default GCR URL`)
	argAWSRegion        = flags.String("aws-region", "us-east-1", `Default AWS region`)
	argRefreshMinutes   = flags.Int("refresh-mins", 60, `Default time to wait before refreshing (60 minutes)`)
	argSkipKubeSystem   = flags.Bool("skip-kube-system", true, `If true, will not attempt to set ImagePullSecrets on the kube-system namespace`)
)

var (
	awsAccountID string
)

type controller struct {
	k8sutil   *k8sutil.K8sutilInterface
	ecrClient ecrInterface
	gcrClient gcrInterface
}

type ecrInterface interface {
	GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
}

type gcrInterface interface {
	DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error)
}

func newEcrClient() ecrInterface {
	return ecr.New(session.New(), aws.NewConfig().WithRegion(*argAWSRegion))
}

type gcrClient struct{}

func (gcr gcrClient) DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
	return google.DefaultTokenSource(ctx, scope...)
}

func newGcrClient() gcrInterface {
	return gcrClient{}
}

func (c *controller) getGCRAuthorizationKey() (AuthToken, error) {
	ts, err := c.gcrClient.DefaultTokenSource(context.TODO(), "https://www.googleapis.com/auth/cloud-platform")
	if err != nil {
		return AuthToken{}, err
	}

	token, err := ts.Token()
	if err != nil {
		return AuthToken{}, err
	}

	if !token.Valid() {
		return AuthToken{}, fmt.Errorf("token was invalid")
	}

	if token.Type() != "Bearer" {
		return AuthToken{}, fmt.Errorf(fmt.Sprintf("expected token type \"Bearer\" but got \"%s\"", token.Type()))
	}

	return AuthToken{
		AccessToken: token.AccessToken,
		Endpoint:    *argGCRURL}, nil
}

func (c *controller) getECRAuthorizationKey() (AuthToken, error) {
	params := &ecr.GetAuthorizationTokenInput{
		RegistryIds: []*string{
			aws.String(awsAccountID),
		},
	}

	resp, err := c.ecrClient.GetAuthorizationToken(params)

	if err != nil {
		// Print the error, cast err to awserr.Error to get the Code and
		// Message from an error.
		fmt.Println(err.Error())
		return AuthToken{}, err
	}

	token := resp.AuthorizationData[0]

	return AuthToken{
		AccessToken: *token.AuthorizationToken,
		Endpoint:    *token.ProxyEndpoint}, err
}

func generateSecretObj(token string, endpoint string, isJSONCfg bool, secretName string) *v1.Secret {
	secret := &v1.Secret{
		ObjectMeta: v1.ObjectMeta{
			Name: secretName,
		},
	}
	if isJSONCfg {
		secret.Data = map[string][]byte{
			".dockerconfigjson": []byte(fmt.Sprintf(dockerJSONTemplate, endpoint, token))}
		secret.Type = "kubernetes.io/dockerconfigjson"
	} else {
		secret.Data = map[string][]byte{
			".dockercfg": []byte(fmt.Sprintf(dockerCfgTemplate, endpoint, token))}
		secret.Type = "kubernetes.io/dockercfg"
	}
	return secret
}

type AuthToken struct {
	AccessToken string
	Endpoint    string
}

type SecretGenerator struct {
	TokenGenFxn func() (AuthToken, error)
	IsJSONCfg   bool
	SecretName  string
}

func getSecretGenerators(c *controller) []SecretGenerator {
	secretGenerators := []SecretGenerator{}

	secretGenerators = append(secretGenerators, SecretGenerator{
		TokenGenFxn: c.getGCRAuthorizationKey,
		IsJSONCfg:   false,
		SecretName:  *argGCRSecretName,
	})

	secretGenerators = append(secretGenerators, SecretGenerator{
		TokenGenFxn: c.getECRAuthorizationKey,
		IsJSONCfg:   true,
		SecretName:  *argAWSSecretName,
	})

	return secretGenerators
}

func (c *controller) process() error {
	secretGenerators := getSecretGenerators(c)

	for _, secretGenerator := range secretGenerators {
		newToken, err := secretGenerator.TokenGenFxn()
		if err != nil {
			fmt.Printf("Error getting secret for provider %s. Skipping secret provider! [Err: %s]", secretGenerator.SecretName, err)
			continue
		}
		newSecret := generateSecretObj(newToken.AccessToken, newToken.Endpoint, secretGenerator.IsJSONCfg, secretGenerator.SecretName)

		namespaces, err := c.k8sutil.GetNamespaces()

		if err != nil {
			fmt.Println("-------> ERROR getting namespaces! Skipping secret provider!", err)
			continue
		}

		for _, namespace := range namespaces.Items {

			if *argSkipKubeSystem && namespace.GetName() == "kube-system" {
				continue
			}

			// Check if the secret exists for the namespace
			_, err := c.k8sutil.GetSecret(namespace.GetName(), secretGenerator.SecretName)

			if err != nil {
				// Secret not found, create
				err := c.k8sutil.CreateSecret(namespace.GetName(), newSecret)
				if err != nil {
					return err
				}
			} else {
				// Existing secret needs updated
				err := c.k8sutil.UpdateSecret(namespace.GetName(), newSecret)
				if err != nil {
					return err
				}
			}

			// Check if ServiceAccount exists
			serviceAccount, err := c.k8sutil.GetServiceAccount(namespace.GetName(), "default")

			if err != nil {
				return err
			}

			// Update existing one if image pull secrets already exists for aws ecr token
			imagePullSecretFound := false
			for i, imagePullSecret := range serviceAccount.ImagePullSecrets {
				if imagePullSecret.Name == secretGenerator.SecretName {
					serviceAccount.ImagePullSecrets[i] = v1.LocalObjectReference{Name: secretGenerator.SecretName}
					imagePullSecretFound = true
					break
				}
			}

			// Append to list of existing service accounts if there isn't one already
			if !imagePullSecretFound {
				serviceAccount.ImagePullSecrets = append(serviceAccount.ImagePullSecrets, v1.LocalObjectReference{Name: secretGenerator.SecretName})
			}

			err = c.k8sutil.UpdateServiceAccount(namespace.GetName(), serviceAccount)
			if err != nil {
				return err
			}
		}
		log.Print("Finished processing secret for: ", secretGenerator.SecretName)
	}

	return nil
}

func validateParams() {
	// Allow environment variables to overwrite args
	awsAccountIDEnv := os.Getenv("awsaccount")
	awsRegionEnv := os.Getenv("awsregion")

	if len(awsRegionEnv) > 0 {
		argAWSRegion = &awsRegionEnv
	}

	if len(awsAccountIDEnv) > 0 {
		awsAccountID = awsAccountIDEnv
	}
}

func main() {
	log.Print("Starting up...")
	flags.Parse(os.Args)

	validateParams()

	log.Print("Using AWS Account: ", awsAccountID)
	log.Printf("Using AWS Region: %s", *argAWSRegion)
	log.Print("Refresh Interval (minutes): ", *argRefreshMinutes)

	util, err := k8sutil.New(*argKubecfgFile, *argKubeMasterURL)

	if err != nil {
		logrus.Error("Could not create k8s client!!", err)
	}

	ecrClient := newEcrClient()
	gcrClient := newGcrClient()
	c := &controller{util, ecrClient, gcrClient}

	tick := time.Tick(time.Duration(*argRefreshMinutes) * time.Minute)

	// Process once now, then wait for tick
	c.process()

	for {
		select {
		case <-tick:
			log.Print("Refreshing credentials...")
			if err := c.process(); err != nil {
				log.Fatalf("Failed to load ecr credentials: %v", err)
			}
		}
	}
}
<commit_msg>Add logging<commit_after>/*
Copyright (c) 2017, UPMC Enterprises
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name UPMC Enterprises nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL UPMC ENTERPRISES BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
*/

package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ecr"
	flag "github.com/spf13/pflag"
	"github.com/upmc-enterprises/registry-creds/k8sutil"
	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"k8s.io/client-go/pkg/api/v1"
)

const (
	dockerCfgTemplate  = `{"%s":{"username":"oauth2accesstoken","password":"%s","email":"none"}}`
	dockerJSONTemplate = `{"auths":{"%s":{"auth":"%s","email":"none"}}}`
)

var (
	flags               = flag.NewFlagSet("", flag.ContinueOnError)
	argKubecfgFile      = flags.String("kubecfg-file", "", `Location of kubecfg file for access to kubernetes master service; --kube_master_url overrides the URL part of this; if neither this nor --kube_master_url are provided, defaults to service account tokens`)
	argKubeMasterURL    = flags.String("kube-master-url", "", `URL to reach kubernetes master. Env variables in this flag will be expanded.`)
	argAWSSecretName    = flags.String("aws-secret-name", "awsecr-cred", `Default aws secret name`)
	argGCRSecretName    = flags.String("gcr-secret-name", "gcr-secret", `Default gcr secret name`)
	argDefaultNamespace = flags.String("default-namespace", "default", `Default namespace`)
	argGCRURL           = flags.String("gcr-url", "https://gcr.io", `Default GCR URL`)
	argAWSRegion        = flags.String("aws-region", "us-east-1", `Default AWS region`)
	argRefreshMinutes   = flags.Int("refresh-mins", 60, `Default time to wait before refreshing (60 minutes)`)
	argSkipKubeSystem   = flags.Bool("skip-kube-system", true, `If true, will not attempt to set ImagePullSecrets on the kube-system namespace`)
)

var (
	awsAccountID string
)

type controller struct {
	k8sutil   *k8sutil.K8sutilInterface
	ecrClient ecrInterface
	gcrClient gcrInterface
}

type ecrInterface interface {
	GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
}

type gcrInterface interface {
	DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error)
}

func newEcrClient() ecrInterface {
	return ecr.New(session.New(), aws.NewConfig().WithRegion(*argAWSRegion))
}

type gcrClient struct{}

func (gcr gcrClient) DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
	return google.DefaultTokenSource(ctx, scope...)
}

func newGcrClient() gcrInterface {
	return gcrClient{}
}

func (c *controller) getGCRAuthorizationKey() (AuthToken, error) {
	ts, err := c.gcrClient.DefaultTokenSource(context.TODO(), "https://www.googleapis.com/auth/cloud-platform")
	if err != nil {
		return AuthToken{}, err
	}

	token, err := ts.Token()
	if err != nil {
		return AuthToken{}, err
	}

	if !token.Valid() {
		return AuthToken{}, fmt.Errorf("token was invalid")
	}

	if token.Type() != "Bearer" {
		return AuthToken{}, fmt.Errorf(fmt.Sprintf("expected token type \"Bearer\" but got \"%s\"", token.Type()))
	}

	return AuthToken{
		AccessToken: token.AccessToken,
		Endpoint:    *argGCRURL}, nil
}

func (c *controller) getECRAuthorizationKey() (AuthToken, error) {
	params := &ecr.GetAuthorizationTokenInput{
		RegistryIds: []*string{
			aws.String(awsAccountID),
		},
	}

	resp, err := c.ecrClient.GetAuthorizationToken(params)

	if err != nil {
		// Print the error, cast err to awserr.Error to get the Code and
		// Message from an error.
		fmt.Println(err.Error())
		return AuthToken{}, err
	}

	token := resp.AuthorizationData[0]

	return AuthToken{
		AccessToken: *token.AuthorizationToken,
		Endpoint:    *token.ProxyEndpoint}, err
}

func generateSecretObj(token string, endpoint string, isJSONCfg bool, secretName string) *v1.Secret {
	secret := &v1.Secret{
		ObjectMeta: v1.ObjectMeta{
			Name: secretName,
		},
	}
	if isJSONCfg {
		secret.Data = map[string][]byte{
			".dockerconfigjson": []byte(fmt.Sprintf(dockerJSONTemplate, endpoint, token))}
		secret.Type = "kubernetes.io/dockerconfigjson"
	} else {
		secret.Data = map[string][]byte{
			".dockercfg": []byte(fmt.Sprintf(dockerCfgTemplate, endpoint, token))}
		secret.Type = "kubernetes.io/dockercfg"
	}
	return secret
}

type AuthToken struct {
	AccessToken string
	Endpoint    string
}

type SecretGenerator struct {
	TokenGenFxn func() (AuthToken, error)
	IsJSONCfg   bool
	SecretName  string
}

func getSecretGenerators(c *controller) []SecretGenerator {
	secretGenerators := []SecretGenerator{}

	secretGenerators = append(secretGenerators, SecretGenerator{
		TokenGenFxn: c.getGCRAuthorizationKey,
		IsJSONCfg:   false,
		SecretName:  *argGCRSecretName,
	})

	secretGenerators = append(secretGenerators, SecretGenerator{
		TokenGenFxn: c.getECRAuthorizationKey,
		IsJSONCfg:   true,
		SecretName:  *argAWSSecretName,
	})

	return secretGenerators
}

func (c *controller) process() error {
	secretGenerators := getSecretGenerators(c)

	for _, secretGenerator := range secretGenerators {

		fmt.Printf("------------------ [%s] ----------------------\n", secretGenerator.SecretName)

		newToken, err := secretGenerator.TokenGenFxn()
		if err != nil {
			fmt.Printf("Error getting secret for provider %s. Skipping secret provider! [Err: %s]", secretGenerator.SecretName, err)
			continue
		}
		newSecret := generateSecretObj(newToken.AccessToken, newToken.Endpoint, secretGenerator.IsJSONCfg, secretGenerator.SecretName)

		namespaces, err := c.k8sutil.GetNamespaces()

		if err != nil {
			fmt.Println("-------> ERROR getting namespaces! Skipping secret provider!", err)
			continue
		}

		for _, namespace := range namespaces.Items {

			if *argSkipKubeSystem && namespace.GetName() == "kube-system" {
				continue
			}

			// Check if the secret exists for the namespace
			_, err := c.k8sutil.GetSecret(namespace.GetName(), secretGenerator.SecretName)

			if err != nil {
				// Secret not found, create
				err := c.k8sutil.CreateSecret(namespace.GetName(), newSecret)
				if err != nil {
					fmt.Println("Could not create Secret!", err)
					continue
				}
			} else {
				// Existing secret needs updated
				err := c.k8sutil.UpdateSecret(namespace.GetName(), newSecret)
				if err != nil {
					fmt.Println("Could not update Secret!", err)
					continue
				}
			}

			// Check if ServiceAccount exists
			serviceAccount, err := c.k8sutil.GetServiceAccount(namespace.GetName(), "default")

			if err != nil {
				fmt.Println("Could not get ServiceAccounts!", err)
				continue
			}

			// Update existing one if image pull secrets already exists for aws ecr token
			imagePullSecretFound := false
			for i, imagePullSecret := range serviceAccount.ImagePullSecrets {
				if imagePullSecret.Name == secretGenerator.SecretName {
					serviceAccount.ImagePullSecrets[i] = v1.LocalObjectReference{Name: secretGenerator.SecretName}
					imagePullSecretFound = true
					break
				}
			}

			// Append to list of existing service accounts if there isn't one already
			if !imagePullSecretFound {
				serviceAccount.ImagePullSecrets = append(serviceAccount.ImagePullSecrets, v1.LocalObjectReference{Name: secretGenerator.SecretName})
			}

			err = c.k8sutil.UpdateServiceAccount(namespace.GetName(), serviceAccount)
			if err != nil {
				fmt.Println("Could not update ServiceAccount!", err)
				continue
			}
		}
		fmt.Println("Finished processing secret for: ", secretGenerator.SecretName)
	}

	return nil
}

func validateParams() {
	// Allow environment variables to overwrite args
	awsAccountIDEnv := os.Getenv("awsaccount")
	awsRegionEnv := os.Getenv("awsregion")

	if len(awsRegionEnv) > 0 {
		argAWSRegion = &awsRegionEnv
	}

	if len(awsAccountIDEnv) > 0 {
		awsAccountID = awsAccountIDEnv
	}
}

func main() {
	log.Print("Starting up...")
	flags.Parse(os.Args)

	validateParams()

	log.Print("Using AWS Account: ", awsAccountID)
	log.Printf("Using AWS Region: %s", *argAWSRegion)
	log.Print("Refresh Interval (minutes): ", *argRefreshMinutes)

	util, err := k8sutil.New(*argKubecfgFile, *argKubeMasterURL)

	if err != nil {
		logrus.Error("Could not create k8s client!!", err)
	}

	ecrClient := newEcrClient()
	gcrClient := newGcrClient()
	c := &controller{util, ecrClient, gcrClient}

	tick := time.Tick(time.Duration(*argRefreshMinutes) * time.Minute)

	// Process once now, then wait for tick
	c.process()

	for {
		select {
		case <-tick:
			log.Print("Refreshing credentials...")
			if err := c.process(); err != nil {
				log.Fatalf("Failed to load ecr credentials: %v", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/base64"
	"log"
	"net/http"

	"goshot/utility"

	"errors"

	"github.com/charles-d-burton/gphoto2go"
	"github.com/gin-gonic/gin"
)

func main() {
	mdnsServer := utility.BroadcastServer()
	defer mdnsServer.Shutdown()
	camera := new(gphoto2go.Camera)
	err := camera.Init()
	if err > 0 {
		log.Println(err)
	}
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})

	r.GET("/shot", func(c *gin.Context) {
		camera.Interrupt()
		cameraFilePath, err := camera.TriggerCaptureToFile()
		if err == 0 {

			cameraFileReader := camera.FileReader(cameraFilePath.Folder, cameraFilePath.Name)
			defer cameraFileReader.Close()
			buf := new(bytes.Buffer)
			buf.WriteString("\"")
			buf.ReadFrom(cameraFileReader)
			buf.WriteString("\"")
			camera.DeleteFile(cameraFilePath.Folder, cameraFilePath.Name)
			encodedImage := base64.StdEncoding.EncodeToString(buf.Bytes())
			c.JSON(200, gin.H{
				"image": encodedImage,
			})
			//c.Data(200, "image/jpeg", buf.Bytes())

		} else {
			log.Println(gphoto2go.CameraResultToString(err))
			c.Error(errors.New(gphoto2go.CameraResultToString(err)))
			c.JSON(http.StatusInternalServerError, gphoto2go.CameraResultToString(err))
			return
		}

	})
	r.Run()
}
<commit_msg>removed quotes<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"log"
	"net/http"

	"goshot/utility"

	"errors"

	"github.com/charles-d-burton/gphoto2go"
	"github.com/gin-gonic/gin"
)

func main() {
	mdnsServer := utility.BroadcastServer()
	defer mdnsServer.Shutdown()
	camera := new(gphoto2go.Camera)
	err := camera.Init()
	if err > 0 {
		log.Println(err)
	}
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})

	r.GET("/shot", func(c *gin.Context) {
		camera.Interrupt()
		cameraFilePath, err := camera.TriggerCaptureToFile()
		if err == 0 {

			cameraFileReader := camera.FileReader(cameraFilePath.Folder, cameraFilePath.Name)
			defer cameraFileReader.Close()
			buf := new(bytes.Buffer)
			//buf.WriteString("\"")
			buf.ReadFrom(cameraFileReader)
			//buf.WriteString("\"")
			camera.DeleteFile(cameraFilePath.Folder, cameraFilePath.Name)
			encodedImage := base64.StdEncoding.EncodeToString(buf.Bytes())
			c.JSON(200, gin.H{
				"image": encodedImage,
			})
			//c.Data(200, "image/jpeg", buf.Bytes())

		} else {
			log.Println(gphoto2go.CameraResultToString(err))
			c.Error(errors.New(gphoto2go.CameraResultToString(err)))
			c.JSON(http.StatusInternalServerError, gphoto2go.CameraResultToString(err))
			return
		}

	})
	r.Run()
}
<|endoftext|>


<commit_before>package main

import (
	ps "github.com/mitchellh/go-ps"
	"log"
	"os/exec"
	"regexp"
	"strconv"
	"github.com/andlabs/ui"
	"time"
	"net/http"
	"os"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"syscall"
)

// Telegram and program settings (config.yml)
// Guide: http://sweetohm.net/article/go-yaml-parsers.en.html
type Config struct {
	Token string
	Botid string
	Chatid string
	Message string
	StayAlive bool
	Process string
	TimeBetweenChecksInS int
	KillOnDC bool
	ShutdownOnDC bool
	KillCoherentUI bool
}

// Variables
var STATUS bool = false
var CONNECTION bool = false
var PID int

func main() {

	////  SETTINGS
	//--------------------------------------------------------------------------------------------------------------
	// YAML PARSING
	var config Config
	source, err := ioutil.ReadFile("config.yml")
	if err != nil {
		// in theory, using yml.Marshal() would be more elegant, but we want to preserve the yaml comments
		// as well as set some default values/hints
		defconf :=
				"## Telegram Bot Settings\r\n" +
				"token: \r\n" +
				"botid: \r\n" +
				"chatid: \r\n" +
				"message: BDO disconnected \r\n" +
				"\r\n" +
				"## Program Settings\r\n" +
				"stayalive: false\r\n" +
				"process: BlackDesert64.exe\r\n" +
				"timebetweenchecksins: 60\r\n" +
				"\r\n" +
				"# These settings require the .exe to be run with admin rights! \r\n" +
				"killondc: true\r\n" +
				"shutdownondc: false\r\n" +
				"killcoherentui: false"
		ioutil.WriteFile("config.yml", []byte(defconf), os.FileMode(int(0666)))
		panic(err)
	}
	err = yaml.Unmarshal(source, &config)
	if err != nil {
		panic(err)
	}

	//// GUI
	//--------------------------------------------------------------------------------------------------------------
	ui := ui.Main(func() {
		window := ui.NewWindow("BDO Watchdog", 300, 80, false)

		label_Process := ui.NewLabel("  Process: " + config.Process)
		label_Status := ui.NewLabel("  Initializing...")
		label_PID := ui.NewLabel("-")
		label_Connection := ui.NewLabel("-")
		label_Update := ui.NewLabel("")

		box := ui.NewVerticalBox()
		sep := ui.NewHorizontalSeparator()
		pb := ui.NewProgressBar()

		// Append all UI elements to the box container
		box.Append(label_Process, false)
		box.Append(label_Status, false)
		box.Append(label_PID, false)
		box.Append(label_Connection, false)
		box.Append(label_Update, false)
		box.Append(sep, false)
		box.Append(pb, true)

		window.SetChild(box)
		window.OnClosing(func(*ui.Window) bool {
			ui.Quit()
			return true
		})
		window.Show()
		go observer(config, label_Status, label_PID, label_Connection, label_Update, pb)
	})
	if ui != nil {
		panic(ui)
	}
}

//--------------------------------------------------------------------------------------------------------------
// PROCESS
//--------------------------------------------------------------------------------------------------------------
func observer(
	config Config,
	label_Status *ui.Label,
	label_PID *ui.Label,
	label_Connection *ui.Label,
	label_Update *ui.Label,
	pb *ui.ProgressBar) {

	// KILL CoherentUI_Host.exe
	if config.KillCoherentUI {

		// Find process(es)
		chp, err := ps.Processes()
		if err != nil {
			log.Fatal(err)
		}

		// Find PID and kill
		for _, v := range chp {
			if v.Executable() == "CoherentUI_Host.exe" {
				proc, err := os.FindProcess(v.Pid())

				if err != nil {
					log.Println(err)
				}
				// Kill the process
				kill_err := proc.Kill()
				if kill_err != nil {
					log.Println(err)
				}
			}
		}
	}

	// INFINITE MAIN LOOP
	for {
		label_Update.SetText("")

		//// EXIT CONDITION
		//-----------------
		// If the process is running, but no longer connected we trigger the following actions
		if STATUS && !CONNECTION {

			// Use the Telegram API to send a message
			send_TelegramMessage(config)

			// Optional: shutdown the computer if the monitored process is disconnected
			if config.ShutdownOnDC {
				exec.Command("cmd", "/C", "shutdown", "/s").Run()
			}

			// Optional: kill the monitored process if it is disconnected
			// requires elevated rights --> start .exe as administrator
			if config.KillOnDC {

				proc, err := os.FindProcess(PID)

				if err != nil {
					log.Println(err)
				}
				// Kill the process
				kill_err := proc.Kill()
				if kill_err != nil {
					log.Println(err)
				}

				time.Sleep(5 * time.Second)
			}

			// Optional (YAML file, default: false): keep this program open even if
			// the process is disconnected
			if !config.StayAlive {
				os.Exit(1)
			}
		}

		//// PROCESS
		//----------
		p, err := ps.Processes()
		if err != nil {
			log.Fatal(err)
		}

		//// PID
		//------
		for _, v := range p {
			if v.Executable() == config.Process {
				PID = v.Pid()
			}
		}
		if (PID == 0) {
			ui.QueueMain(func () {
				STATUS = false
				label_Status.SetText("  Status: not running")
				label_PID.SetText("  PID: -")
				label_Connection.SetText("  Connection: -" )
			})

			wait(config, label_Update, pb)
			continue
		} else {

			ui.QueueMain(func () {
				STATUS = true
				label_Status.SetText("  Status: running")
				label_PID.SetText("  PID: " + strconv.Itoa(PID))
			})
		}

		//// CONNECTION STATUS
		//--------------------
		// NETSTAT
		// the syscall.SysProcAttr trick found here:
		// https://www.reddit.com/r/golang/comments/2c1g3x/build_golang_app_reverse_shell_to_run_in_windows/
		cmd := exec.Command("cmd.exe", "/C netstat -aon")
		cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
		out, err := cmd.Output()
		if err != nil {
			log.Fatal(err)
		}

		// RegEx matching; try to find the PID in the netstat output
		re := regexp.MustCompile(strconv.Itoa(PID))
		byteIndex := re.FindIndex([]byte(out))

		if (len(byteIndex) == 0) {
			ui.QueueMain(func () {
				CONNECTION = false
				label_Connection.SetText("  Connection: Offline" )
			})
		} else {
			// Update labels
			ui.QueueMain(func () {
				CONNECTION = true
				label_Connection.SetText("  Connection: online")
			})
		}

		// Wait x seconds before next iteration
		wait(config, label_Update, pb)
	}
}

// ---------------------------------------------------------------------------------------------------------------------
// A wrapper for time.Sleep() that also updates the UI label and progressbar
func wait(config Config, label_Update *ui.Label, pb *ui.ProgressBar) {
	tstep := config.TimeBetweenChecksInS
	if tstep <= 0 {
		tstep = 1
	} // otherwise division by 0
	for i := 0; i <= tstep; i++ {
		pb.SetValue(int(100/float32(tstep) * float32(i + 1)))
		label_Update.SetText("  Next update in... " + strconv.Itoa(tstep - i) + " s")
		time.Sleep(1 * time.Second)
	}
	pb.SetValue(0)
}

// ---------------------------------------------------------------------------------------------------------------------
// Send a telegram message using a query URL
func send_TelegramMessage(config Config) {
	// Learn how to setup a telegram bot: https://core.telegram.org/bots
	resp, _ := http.Get("https://api.telegram.org/bot" + config.Botid +
		":" + config.Token +
		"/sendMessage?chat_id=" + config.Chatid +
		"&text=" + config.Message)
	defer resp.Body.Close()
}
<commit_msg>continue after process kill panic<commit_after>package main

import (
	ps "github.com/mitchellh/go-ps"
	"log"
	"os/exec"
	"regexp"
	"strconv"
	"github.com/andlabs/ui"
	"time"
	"net/http"
	"os"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"syscall"
	"fmt"
)

// Telegram and program settings (config.yml)
// Guide: http://sweetohm.net/article/go-yaml-parsers.en.html
type Config struct {
	Token string
	Botid string
	Chatid string
	Message string
	StayAlive bool
	Process string
	TimeBetweenChecksInS int
	KillOnDC bool
	ShutdownOnDC bool
	KillCoherentUI bool
}

// Variables
var STATUS bool = false
var CONNECTION bool = false
var PID int

func main() {

	////  SETTINGS
	//--------------------------------------------------------------------------------------------------------------
	// YAML PARSING
	var config Config
	source, err := ioutil.ReadFile("./config.yml")
	if err != nil {
		// in theory, using yml.Marshal() would be more elegant, but we want to preserve the yaml comments
		// as well as set some default values/hints
		defconf :=
				"## Telegram Bot Settings\r\n" +
				"token: \r\n" +
				"botid: \r\n" +
				"chatid: \r\n" +
				"message: BDO disconnected \r\n" +
				"\r\n" +
				"## Program Settings\r\n" +
				"stayalive: false\r\n" +
				"process: BlackDesert64.exe\r\n" +
				"timebetweenchecksins: 60\r\n" +
				"\r\n" +
				"# These settings require the .exe to be run with admin rights! \r\n" +
				"killondc: true\r\n" +
				"shutdownondc: false\r\n" +
				"killcoherentui: false"
		ioutil.WriteFile("config.yml", []byte(defconf), os.FileMode(int(0777)))
		panic(err)
	}
	err = yaml.Unmarshal(source, &config)
	if err != nil {
		panic(err)
	}

	//// GUI
	//--------------------------------------------------------------------------------------------------------------
	ui := ui.Main(func() {
		window := ui.NewWindow("BDO Watchdog", 300, 80, false)

		label_Process := ui.NewLabel("  Process: " + config.Process)
		label_Status := ui.NewLabel("  Initializing...")
		label_PID := ui.NewLabel("-")
		label_Connection := ui.NewLabel("-")
		label_Update := ui.NewLabel("")

		box := ui.NewVerticalBox()
		sep := ui.NewHorizontalSeparator()
		pb := ui.NewProgressBar()

		// Append all UI elements to the box container
		box.Append(label_Process, false)
		box.Append(label_Status, false)
		box.Append(label_PID, false)
		box.Append(label_Connection, false)
		box.Append(label_Update, false)
		box.Append(sep, false)
		box.Append(pb, true)

		window.SetChild(box)
		window.OnClosing(func(*ui.Window) bool {
			ui.Quit()
			return true
		})
		window.Show()
		go observer(config, label_Status, label_PID, label_Connection, label_Update, pb)
	})
	if ui != nil {
		panic(ui)
	}
}

//--------------------------------------------------------------------------------------------------------------
// PROCESS
//--------------------------------------------------------------------------------------------------------------
func observer(
	config Config,
	label_Status *ui.Label,
	label_PID *ui.Label,
	label_Connection *ui.Label,
	label_Update *ui.Label,
	pb *ui.ProgressBar) {

	// KILL CoherentUI_Host.exe
	if config.KillCoherentUI {

		// Find process(es)
		chp, err := ps.Processes()
		if err != nil {
			log.Fatal(err)
		}

		// Find PID and kill
		for _, v := range chp {
			if v.Executable() == "CoherentUI_Host.exe" {
				proc, err := os.FindProcess(v.Pid())

				if err != nil {
					log.Println(err)
				}
				// Kill the process
				defer proc.Kill()
			}
		}
	}

	// INFINITE MAIN LOOP
	for {
		label_Update.SetText("")

		//// EXIT CONDITION
		//-----------------
		// If the process is running, but no longer connected we trigger the following actions
		if STATUS && !CONNECTION {

			// Use the Telegram API to send a message
			send_TelegramMessage(config)

			// Optional: shutdown the computer if the monitored process is disconnected
			if config.ShutdownOnDC {
				exec.Command("cmd", "/C", "shutdown", "/s").Run()
			}

			// Optional: kill the monitored process if it is disconnected
			// requires elevated rights --> start .exe as administrator
			if config.KillOnDC {

				proc, err := os.FindProcess(PID)

				if err != nil {
					log.Println(err)
				}
				// Kill the process
				defer proc.Kill()

				time.Sleep(5 * time.Second)
			}

			// Optional (YAML file, default: false): keep this program open even if
			// the process is disconnected
			if !config.StayAlive {
				os.Exit(1)
			}
		}

		//// PROCESS
		//----------
		p, err := ps.Processes()
		if err != nil {
			log.Fatal(err)
		}

		//// PID
		//------
		for _, v := range p {
			if v.Executable() == config.Process {
				PID = v.Pid()
			}
		}
		if (PID == 0) {
			ui.QueueMain(func () {
				STATUS = false
				label_Status.SetText("  Status: not running")
				label_PID.SetText("  PID: -")
				label_Connection.SetText("  Connection: -" )
			})

			wait(config, label_Update, pb)
			continue
		} else {

			ui.QueueMain(func () {
				STATUS = true
				label_Status.SetText("  Status: running")
				label_PID.SetText("  PID: " + strconv.Itoa(PID))
			})
		}

		//// CONNECTION STATUS
		//--------------------
		// NETSTAT
		// the syscall.SysProcAttr trick found here:
		// https://www.reddit.com/r/golang/comments/2c1g3x/build_golang_app_reverse_shell_to_run_in_windows/
		cmd := exec.Command("cmd.exe", "/C netstat -aon")
		cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
		out, err := cmd.Output()
		if err != nil {
			log.Fatal(err)
		}

		// RegEx matching; try to find the PID in the netstat output
		re := regexp.MustCompile(strconv.Itoa(PID))
		byteIndex := re.FindIndex([]byte(out))

		if (len(byteIndex) == 0) {
			ui.QueueMain(func () {
				CONNECTION = false
				label_Connection.SetText("  Connection: Offline" )
			})
		} else {
			// Update labels
			ui.QueueMain(func () {
				CONNECTION = true
				label_Connection.SetText("  Connection: online")
			})
		}

		// Wait x seconds before next iteration
		wait(config, label_Update, pb)
	}
}

// ---------------------------------------------------------------------------------------------------------------------
// A wrapper for time.Sleep() that also updates the UI label and progressbar
func wait(config Config, label_Update *ui.Label, pb *ui.ProgressBar) {
	tstep := config.TimeBetweenChecksInS
	if tstep <= 0 {
		tstep = 1
	} // otherwise division by 0
	for i := 0; i <= tstep; i++ {
		pb.SetValue(int(100/float32(tstep) * float32(i + 1)))
		fmt.Println(tstep, ", ", i, " = ", int32(100/float32(tstep) * float32(i + 1)))
		label_Update.SetText("  Next update in... " + strconv.Itoa(tstep - i) + " s")
		time.Sleep(1 * time.Second)
	}
	pb.SetValue(0)
}

// ---------------------------------------------------------------------------------------------------------------------
// Send a telegram message using a query URL
func send_TelegramMessage(config Config) {
	// Learn how to setup a telegram bot: https://core.telegram.org/bots
	resp, _ := http.Get("https://api.telegram.org/bot" + config.Botid +
		":" + config.Token +
		"/sendMessage?chat_id=" + config.Chatid +
		"&text=" + config.Message)
	defer resp.Body.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

type testRunner struct {
	wg          sync.WaitGroup
	semaphore   chan int
	summaryInfo *summary
	errors      []error

	sync.Mutex
	stepsInLine int
}

type summary struct {
	sync.Mutex
	scenarios        int
	scenariosPassed  int
	scenariosFailed  int
	scenariosSkipped int
	steps            int
	stepsPassed      int
	stepsFailed      int
	stepsSkipped     int
}

var cfg config

func main() {
	flag.Parse()
	if err := cfg.Validate(); err != nil {
		log.Fatal(err)
	}
	t := NewTestRunner()
	t.Run()
}

func NewTestRunner() *testRunner {
	return &testRunner{
		wg:          sync.WaitGroup{},
		stepsInLine: 0,
		errors:      make([]error, 0),
		semaphore:   make(chan int, cfg.concurrencyLevel),
		summaryInfo: &summary{
			scenarios:        0,
			scenariosPassed:  0,
			scenariosFailed:  0,
			scenariosSkipped: 0,
			steps:            0,
			stepsPassed:      0,
			stepsFailed:      0,
			stepsSkipped:     0,
		},
	}
}

func (t *testRunner) Run() {
	start := time.Now()
	features := t.features()
	for _, feature := range features {
		t.wg.Add(1)
		go t.executeTest(feature)
	}
	t.wg.Wait()
	t.summary()
	fmt.Printf("Tests ran in: %s\n", time.Since(start))
}

func (t *testRunner) summary() {
	fmt.Println()
	for _, e := range t.errors {
		fmt.Println(e)
	}
	fmt.Println(t.summaryInfo)
}

func (t *testRunner) executeTest(test string) {
	t.semaphore <- 1
	behat := exec.Command(cfg.binPath, "-f", "progress", test)
	stdout, err := behat.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	err = behat.Start()
	if err != nil {
		log.Fatal(err)
	}
	t.proccessOutput(stdout)
	err = behat.Wait()
	if err != nil {
		t.errors = append(t.errors, fmt.Errorf("TODO: handle std err output from behat: %s", err))
	}
	<-t.semaphore
	t.wg.Done()
}

func (t *testRunner) proccessOutput(out io.Reader) {
	colorMap := map[byte]func(string) string{
		'.': green,
		'-': cyan,
		'F': red,
		'U': yellow,
	}
	reader := bufio.NewReader(out)
	for {
		c, err := reader.ReadByte()
		switch c {
		case '\n':
			// if we encounted two new lines in a row - steps have finished
			// and we try to parse information about runned scenarios and steps
			nextByte, err := reader.Peek(1)
			if err != nil {
				break
			}
			if nextByte[0] == '\n' {
				_, err = reader.ReadByte()
				for {
					line, err := reader.ReadBytes('\n')
					if err != nil {
						break
					}
					t.summaryInfo.parseTestSummary(line)
				}
			}
			break
		case '.', '-', 'F', 'U':
			t.Lock()
			if t.stepsInLine%70 == 0 && t.stepsInLine > 0 {
				fmt.Printf(" %d\n", t.stepsInLine)
			}
			fmt.Print(colorMap[c](string(c)))
			t.stepsInLine += 1
			t.Unlock()
			break
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("Unknown error while proccessing output: %s", err)
			break
		}
	}
}

// TODO: add undefined steps
func (s *summary) parseTestSummary(line []byte) {
	if n, matched := parseSuiteInfo("scenario", line); matched {
		s.Lock()
		s.scenarios += n
		s.Unlock()
		if n, matched = parseSuiteInfo("passed", line); matched {
			s.Lock()
			s.scenariosPassed += n
			s.Unlock()
		}
		if n, matched = parseSuiteInfo("failed", line); matched {
			s.Lock()
			s.scenariosFailed += n
			s.Unlock()
		}
		if n, matched = parseSuiteInfo("skipped", line); matched {
			s.Lock()
			s.scenariosSkipped += n
			s.Unlock()
		}
	}

	if n, matched := parseSuiteInfo("step", line); matched {
		s.Lock()
		s.steps += n
		s.Unlock()
		if n, matched = parseSuiteInfo("passed", line); matched {
			s.Lock()
			s.stepsPassed += n
			s.Unlock()
		}
		if n, matched = parseSuiteInfo("failed", line); matched {
			s.Lock()
			s.stepsFailed += n
			s.Unlock()
		}
		if n, matched = parseSuiteInfo("skipped", line); matched {
			s.Lock()
			s.stepsSkipped += n
			s.Unlock()
		}
	}
}

func parseSuiteInfo(s string, buf []byte) (n int, matched bool) {
	re := regexp.MustCompile("([0-9]+) " + s)
	match := re.FindString(string(buf))
	if match != "" {
		splitted := strings.Split(match, " ")
		n, _ := strconv.Atoi(splitted[0])
		return n, true
	}
	return 0, false
}

func (t *testRunner) features() []string {
	var features []string
	err := filepath.Walk(cfg.featuresPath, func(path string, file os.FileInfo, err error) error {
		if err == nil && !file.IsDir() {
			features = append(features, path)
		}
		return err
	})
	if err != nil {
		panic("failed to walk directory: " + err.Error())
	}
	return features
}

func (s *summary) String() string {
	res := fmt.Sprintf("%d scenarios (%s", s.scenarios, green(fmt.Sprintf("%d passed", s.scenariosPassed)))
	if s.scenariosFailed > 0 {
		res += fmt.Sprintf(", %s", red(fmt.Sprintf("%d failed", s.scenariosFailed)))
	}
	if s.scenariosSkipped > 0 {
		res += fmt.Sprintf(", %s", cyan(fmt.Sprintf("%d skipped", s.scenariosSkipped)))
	}
	res += fmt.Sprintf(")\n")
	res += fmt.Sprintf("%d steps (%s", s.steps, green(fmt.Sprintf("%d passed", s.stepsPassed)))
	if s.stepsFailed > 0 {
		res += fmt.Sprintf(", %s", red(fmt.Sprintf("%d failed", s.stepsFailed)))
	}
	if s.stepsSkipped > 0 {
		res += fmt.Sprintf(", %s", cyan(fmt.Sprintf("%d skipped", s.stepsSkipped)))
	}
	res += fmt.Sprintf(")\n")
	return res
}
<commit_msg>show failed step information<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

type testRunner struct {
	wg          sync.WaitGroup
	semaphore   chan int
	summaryInfo *summary
	errors      []error
	extras      []string // failed, undefined step information

	sync.Mutex
	stepsInLine int
}

type summary struct {
	sync.Mutex
	scenarios        int
	scenariosPassed  int
	scenariosFailed  int
	scenariosSkipped int
	steps            int
	stepsPassed      int
	stepsFailed      int
	stepsSkipped     int
}

var cfg config

func main() {
	flag.Parse()
	if err := cfg.Validate(); err != nil {
		log.Fatal(err)
	}
	t := NewTestRunner()
	t.Run()
}

func NewTestRunner() *testRunner {
	return &testRunner{
		wg:          sync.WaitGroup{},
		stepsInLine: 0,
		errors:      make([]error, 0),
		semaphore:   make(chan int, cfg.concurrencyLevel),
		summaryInfo: &summary{
			scenarios:        0,
			scenariosPassed:  0,
			scenariosFailed:  0,
			scenariosSkipped: 0,
			steps:            0,
			stepsPassed:      0,
			stepsFailed:      0,
			stepsSkipped:     0,
		},
	}
}

func (t *testRunner) Run() {
	start := time.Now()
	features := t.features()
	for _, feature := range features {
		t.wg.Add(1)
		go t.executeTest(feature)
	}
	t.wg.Wait()
	t.summary()
	fmt.Printf("Tests ran in: %s\n", time.Since(start))
}

func (t *testRunner) summary() {
	fmt.Println("\n")
	for _, extra := range t.extras {
		fmt.Println(extra)
	}
	// for _, e := range t.errors {
	// 	fmt.Println(e)
	// }
	fmt.Println(t.summaryInfo)
}

func (t *testRunner) executeTest(test string) {
	t.semaphore <- 1
	behat := exec.Command(cfg.binPath, "-f", "progress", test)
	stdout, err := behat.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	err = behat.Start()
	if err != nil {
		log.Fatal(err)
	}
	t.proccessOutput(stdout)
	err = behat.Wait()
	if err != nil {
		t.errors = append(t.errors, fmt.Errorf("TODO: handle std err output from behat: %s", err))
	}
	<-t.semaphore
	t.wg.Done()
}

func (t *testRunner) proccessOutput(out io.Reader) {
	colorMap := map[byte]func(string) string{
		'.': green,
		'-': cyan,
		'F': red,
		'U': yellow,
	}
	reader := bufio.NewReader(out)
	for {
		c, err := reader.ReadByte()
		switch c {
		case '\n':
			// if we encounted two new lines in a row - steps have finished
			// and we try to parse information about runned scenarios and steps
			nextByte, err := reader.Peek(1)
			if err != nil {
				break
			}
			if nextByte[0] == '\n' {
				_, err = reader.ReadByte()
				t.parseExtras(reader)
				for {
					line, err := reader.ReadBytes('\n')
					if err != nil {
						break
					}
					t.summaryInfo.parseTestSummary(line)
				}
			}
			break
		case '.', '-', 'F', 'U':
			t.Lock()
			if t.stepsInLine%70 == 0 && t.stepsInLine > 0 {
				fmt.Printf(" %d\n", t.stepsInLine)
			}
			fmt.Print(colorMap[c](string(c)))
			t.stepsInLine += 1
			t.Unlock()
			break
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("Unknown error while proccessing output: %s", err)
			break
		}
	}
}

func (t *testRunner) parseExtras(reader *bufio.Reader) {
	next, err := reader.Peek(1)
	if err != nil {
		return
	}
	if next[0] != '-' {
		return
	}

	var lines []string
	for {
		next, err = reader.Peek(1)
		if err != nil {
			break
		}
		// check if extras
		if next[0] != ' ' && next[0] != '-' {
			break
		}

		line, err := reader.ReadBytes('\n')
		if err != nil {
			break
		}
		lines = append(lines, red(string(line)))
		_, _ = reader.ReadByte()
	}

	if len(lines) > 0 {
		t.Lock()
		t.extras = append(t.extras, strings.Join(lines, "\n"))
		t.Unlock()
	}
}

// TODO: add undefined steps
func (s *summary) parseTestSummary(line []byte) {
	if n, matched := parseSuiteInfo("scenario", line); matched {
		s.Lock()
		s.scenarios += n
		s.Unlock()
		if n, matched = parseSuiteInfo("passed", line); matched {
			s.Lock()
			s.scenariosPassed += n
			s.Unlock()
		}
		if n, matched = parseSuiteInfo("failed", line); matched {
			s.Lock()
			s.scenariosFailed += n
			s.Unlock()
		}
		if n, matched = parseSuiteInfo("skipped", line); matched {
			s.Lock()
			s.scenariosSkipped += n
			s.Unlock()
		}
	}

	if n, matched := parseSuiteInfo("step", line); matched {
		s.Lock()
		s.steps += n
		s.Unlock()
		if n, matched = parseSuiteInfo("passed", line); matched {
			s.Lock()
			s.stepsPassed += n
			s.Unlock()
		}
		if n, matched = parseSuiteInfo("failed", line); matched {
			s.Lock()
			s.stepsFailed += n
			s.Unlock()
		}
		if n, matched = parseSuiteInfo("skipped", line); matched {
			s.Lock()
			s.stepsSkipped += n
			s.Unlock()
		}
	}
}

func parseSuiteInfo(s string, buf []byte) (n int, matched bool) {
	re := regexp.MustCompile("([0-9]+) " + s)
	match := re.FindString(string(buf))
	if match != "" {
		splitted := strings.Split(match, " ")
		n, _ := strconv.Atoi(splitted[0])
		return n, true
	}
	return 0, false
}

func (t *testRunner) features() []string {
	var features []string
	err := filepath.Walk(cfg.featuresPath, func(path string, file os.FileInfo, err error) error {
		if err == nil && !file.IsDir() {
			features = append(features, path)
		}
		return err
	})
	if err != nil {
		panic("failed to walk directory: " + err.Error())
	}
	return features
}

func (s *summary) String() string {
	res := fmt.Sprintf("%d scenarios (%s", s.scenarios, green(fmt.Sprintf("%d passed", s.scenariosPassed)))
	if s.scenariosFailed > 0 {
		res += fmt.Sprintf(", %s", red(fmt.Sprintf("%d failed", s.scenariosFailed)))
	}
	if s.scenariosSkipped > 0 {
		res += fmt.Sprintf(", %s", cyan(fmt.Sprintf("%d skipped", s.scenariosSkipped)))
	}
	res += fmt.Sprintf(")\n")
	res += fmt.Sprintf("%d steps (%s", s.steps, green(fmt.Sprintf("%d passed", s.stepsPassed)))
	if s.stepsFailed > 0 {
		res += fmt.Sprintf(", %s", red(fmt.Sprintf("%d failed", s.stepsFailed)))
	}
	if s.stepsSkipped > 0 {
		res += fmt.Sprintf(", %s", cyan(fmt.Sprintf("%d skipped", s.stepsSkipped)))
	}
	res += fmt.Sprintf(")\n")
	return res
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go-uuid/uuid"
	"code.google.com/p/rsc/qr"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	v := flag.Bool("v", false, "show version")
	flag.Parse()

	if *v {
		fmt.Println("version:0.1.0")
		os.Exit(0)
	}

	text := uuid.New()
	data, _ := qr.Encode(text, qr.H)
	content := data.PNG()
	fmt.Println(text)
	ioutil.WriteFile(text+".png", content, 0644)
}
<commit_msg>0.1.1.dev<commit_after>package main

import (
	"code.google.com/p/go-uuid/uuid"
	"code.google.com/p/rsc/qr"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	v := flag.Bool("v", false, "show version")
	flag.Parse()

	if *v {
		fmt.Println("version:0.1.1.dev")
		os.Exit(0)
	}

	text := uuid.New()
	data, _ := qr.Encode(text, qr.H)
	content := data.PNG()
	fmt.Println(text)
	ioutil.WriteFile(text+".png", content, 0644)
}
<|endoftext|>