<commit_before>package main

import (
	"bytes"
	"errors"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"

	"github.com/nlopes/slack"
)

const (
	iconPath = "/static/icon.png"
	memePath = "/static/spongemock.jpg"

	username = "Spongebob"
	fallback = "*Spongebob mocking meme*"
)

var (
	atk     = os.Getenv("AUTHENTICATION_TOKEN")
	vtk     = os.Getenv("VERIFICATION_TOKEN")
	appURL  = os.Getenv("APP_URL")
	iconURL string
	memeURL string
	api     = slack.New(atk)

	textRegexp = regexp.MustCompile("&|<|>|<.+?>|.?")
	userRegexp = regexp.MustCompile("^<@(U\\w+)\\|.+?>$")
)

func transformText(m string) string {
	var buffer bytes.Buffer
	letters := textRegexp.FindAllString(m, -1)
	for _, ch := range letters {
		// ignore html escaped entities
		if len(ch) > 1 {
			buffer.WriteString(ch)
			continue
		}
		if rand.Int()%2 == 0 {
			ch = strings.ToUpper(ch)
		} else {
			ch = strings.ToLower(ch)
		}
		buffer.WriteString(ch)
	}
	return buffer.String()
}

func isValidSlackRequest(r *http.Request) bool {
	if r.Method != "POST" {
		log.Printf("want method POST, got %s\n", r.Method)
		return false
	}
	err := r.ParseForm()
	if err != nil {
		log.Printf("invalid form data: %s\n", err)
		return false
	}
	if cmd := r.PostFormValue("command"); cmd != "/spongemock" {
		log.Printf("want command /spongemock, got %s\n", cmd)
		return false
	}
	if tk := r.PostFormValue("token"); tk != vtk {
		log.Printf("received invalid token %s\n", tk)
		return false
	}
	if url := r.PostFormValue("response_url"); url == "" {
		log.Println("did not receive response url")
		return false
	}
	return true
}

func getLastSlackMessage(c string, u string) (string, error) {
	if u != "" {
		log.Printf("searching for messages by user %s\n", u)
	}
	h, err := api.GetChannelHistory(c, slack.NewHistoryParameters())
	if err != nil {
		log.Printf("history API request error: %s", err)
		return "", err
	}

	for _, msg := range h.Messages {
		log.Printf("message: %v\n", msg)
		// don't support message subtypes for now
		if msg.SubType != "" || msg.Text == "" {
			continue
		}
		// if a user is supplied, search for the last message by a user
		if u != "" && msg.User != u {
			continue
		}
		return msg.Text, nil
	}

	err = errors.New("no last message found")
	log.Println(err)
	return "", err
}

func handleSlack(w http.ResponseWriter, r *http.Request) {
	status := http.StatusOK
	defer func() {
		w.WriteHeader(status)
	}()
	if !isValidSlackRequest(r) {
		status = http.StatusBadRequest
		return
	}
	channel := r.PostFormValue("channel_id")
	reqText := r.PostFormValue("text")
	log.Printf("command: %s %s\n", r.PostFormValue("command"), reqText)
	var message string
	var err error
	if reqText == "" {
		message, err = getLastSlackMessage(channel, "")
		if err != nil {
			status = http.StatusInternalServerError
			return
		}
	} else if userRegexp.MatchString(reqText) {
		message, err = getLastSlackMessage(channel, userRegexp.FindStringSubmatch(reqText)[1])
		if err != nil {
			status = http.StatusInternalServerError
			return
		}
	} else {
		message = reqText
	}

	mockedText := transformText(message)
	if mockedText == "" {
		status = http.StatusInternalServerError
		return
	}
	params := slack.NewPostMessageParameters()
	params.Username = username
	params.Attachments = []slack.Attachment{{
		Text:     mockedText,
		Fallback: fallback,
		ImageURL: memeURL,
	}}
	params.IconURL = iconURL
	_, _, err = api.PostMessage(channel, "", params)
	if err != nil {
		status = http.StatusInternalServerError
	}
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		log.Fatal("$PORT must be set!")
	}
	if atk == "" {
		log.Fatal("$AUTHENTICATION_TOKEN must be set!")
	}
	if vtk == "" {
		log.Fatal("$VERIFICATION_TOKEN must be set!")
	}
	if appURL == "" {
		log.Fatal("$APP_URL must be set!")
	}
	u, err := url.Parse(appURL)
	if err != nil {
		log.Fatal("invalid $APP_URL %s", appURL)
	}
	icon, _ := url.Parse(iconPath)
	iconURL = u.ResolveReference(icon).String()
	meme, _ := url.Parse(memePath)
	memeURL = u.ResolveReference(meme).String()

	fs := http.FileServer(http.Dir("static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))
	http.HandleFunc("/slack", handleSlack)

	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<commit_msg>Add private channel support<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"

	"github.com/nlopes/slack"
)

const (
	iconPath = "/static/icon.png"
	memePath = "/static/spongemock.jpg"

	username = "Spongebob"
	fallback = "*Spongebob mocking meme*"
)

var (
	atk     = os.Getenv("AUTHENTICATION_TOKEN")
	vtk     = os.Getenv("VERIFICATION_TOKEN")
	appURL  = os.Getenv("APP_URL")
	iconURL string
	memeURL string
	api     = slack.New(atk)

	textRegexp = regexp.MustCompile("&|<|>|<.+?>|.?")
	userRegexp = regexp.MustCompile("^<@(U\\w+)\\|.+?>$")
)

func transformText(m string) string {
	var buffer bytes.Buffer
	letters := textRegexp.FindAllString(m, -1)
	for _, ch := range letters {
		// ignore html escaped entities
		if len(ch) > 1 {
			buffer.WriteString(ch)
			continue
		}
		if rand.Int()%2 == 0 {
			ch = strings.ToUpper(ch)
		} else {
			ch = strings.ToLower(ch)
		}
		buffer.WriteString(ch)
	}
	return buffer.String()
}

func isValidSlackRequest(r *http.Request) bool {
	if r.Method != "POST" {
		log.Printf("want method POST, got %s\n", r.Method)
		return false
	}
	err := r.ParseForm()
	if err != nil {
		log.Printf("invalid form data: %s\n", err)
		return false
	}
	if cmd := r.PostFormValue("command"); cmd != "/spongemock" {
		log.Printf("want command /spongemock, got %s\n", cmd)
		return false
	}
	if tk := r.PostFormValue("token"); tk != vtk {
		log.Printf("received invalid token %s\n", tk)
		return false
	}
	if url := r.PostFormValue("response_url"); url == "" {
		log.Println("did not receive response url")
		return false
	}
	return true
}

func getLastSlackMessage(c string, u string) (string, error) {
	if u != "" {
		log.Printf("searching for messages by user %s\n", u)
	}
	histParams := slack.NewHistoryParameters()
	var h *slack.History
	var err error
	if c[0] == 'C' {
		h, err = api.GetChannelHistory(c, histParams)
	} else if c[0] == 'G' {
		// handle private channels
		h, err = api.GetGroupHistory(c, histParams)
	} else {
		err = fmt.Errorf("unknown channel type, channel_id = %s", c)
	}
	if err != nil {
		log.Printf("history API request error: %s\n", err)
		return "", err
	}

	for _, msg := range h.Messages {
		log.Printf("message: %v\n", msg)
		// don't support message subtypes for now
		if msg.SubType != "" || msg.Text == "" {
			continue
		}
		// if a user is supplied, search for the last message by a user
		if u != "" && msg.User != u {
			continue
		}
		return msg.Text, nil
	}

	err = errors.New("no last message found")
	log.Println(err)
	return "", err
}

func handleSlack(w http.ResponseWriter, r *http.Request) {
	status := http.StatusOK
	defer func() {
		w.WriteHeader(status)
	}()
	if !isValidSlackRequest(r) {
		status = http.StatusBadRequest
		return
	}
	channel := r.PostFormValue("channel_id")
	reqText := r.PostFormValue("text")
	log.Printf("command: %s %s\n", r.PostFormValue("command"), reqText)
	var message string
	var err error
	if reqText == "" {
		message, err = getLastSlackMessage(channel, "")
		if err != nil {
			status = http.StatusInternalServerError
			return
		}
	} else if userRegexp.MatchString(reqText) {
		message, err = getLastSlackMessage(channel, userRegexp.FindStringSubmatch(reqText)[1])
		if err != nil {
			status = http.StatusInternalServerError
			return
		}
	} else {
		message = reqText
	}

	mockedText := transformText(message)
	if mockedText == "" {
		status = http.StatusInternalServerError
		return
	}
	params := slack.NewPostMessageParameters()
	params.Username = username
	params.Attachments = []slack.Attachment{{
		Text:     mockedText,
		Fallback: fallback,
		ImageURL: memeURL,
	}}
	params.IconURL = iconURL
	_, _, err = api.PostMessage(channel, "", params)
	if err != nil {
		status = http.StatusInternalServerError
	}
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		log.Fatal("$PORT must be set!")
	}
	if atk == "" {
		log.Fatal("$AUTHENTICATION_TOKEN must be set!")
	}
	if vtk == "" {
		log.Fatal("$VERIFICATION_TOKEN must be set!")
	}
	if appURL == "" {
		log.Fatal("$APP_URL must be set!")
	}
	u, err := url.Parse(appURL)
	if err != nil {
		log.Fatal("invalid $APP_URL %s", appURL)
	}
	icon, _ := url.Parse(iconPath)
	iconURL = u.ResolveReference(icon).String()
	meme, _ := url.Parse(memePath)
	memeURL = u.ResolveReference(meme).String()

	fs := http.FileServer(http.Dir("static"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))
	http.HandleFunc("/slack", handleSlack)

	log.Fatal(http.ListenAndServe(":"+port, nil))
}
<|endoftext|>


<commit_before>package sendcloud

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

type Event struct {
	name   string
	time   time.Time
	rcpt   string
	msgid  string
	reason string
}

func (e *Event) Name() string    { return e.name }
func (e *Event) Time() time.Time { return e.time }
func (e *Event) Rcpt() string    { return e.rcpt }
func (e *Event) MsgId() string   { return e.msgid }
func (e *Event) Reason() string  { return e.reason }

var (
	ErrMethodNotAllowed = fmt.Errorf("method not allowed")
	ErrBadSignature     = fmt.Errorf("bad signature")
	ErrInvalidTimestamp = fmt.Errorf("invalid timestamp")
	ErrInvalidForm      = fmt.Errorf("invalid form data")
)

type Webhook struct {
	key string
}

func NewWebhook(key string) *Webhook {
	return &Webhook{key}
}

func (wh *Webhook) Handle(w http.ResponseWriter, req *http.Request) (evt *Event, err error) {
	if req.Method != "POST" {
		err = ErrMethodNotAllowed
		w.Header().Set("Allow", "POST")
		http.Error(w, "only POST method is allowed", http.StatusMethodNotAllowed)
		return
	}

	if err = req.ParseForm(); err != nil {
		err = ErrInvalidForm
		http.Error(w, "invalid form", http.StatusBadRequest)
		return
	}

	ts := req.Form.Get("timestamp")
	if !wh.Verify(ts, req.Form.Get("token"), req.Form.Get("signature")) {
		err = ErrBadSignature
		http.Error(w, "bad signature", http.StatusForbidden)
		return
	}

	unix, err := strconv.ParseInt(ts, 10, 64) // millisecond since Unix epoch
	if err != nil {
		err = ErrInvalidTimestamp
		http.Error(w, "invalid timestamp", http.StatusBadRequest)
		return
	}
	evt = &Event{
		time:   time.Unix(0, unix*1e6), // 1 ms = 1e6 ns
		name:   req.Form.Get("event"),
		rcpt:   req.Form.Get("recipient"),
		msgid:  req.Form.Get("emailId"),
		reason: req.Form.Get("reason"),
	}
	return
}

func (wh *Webhook) Verify(timestamp, token, signature string) bool {
	h := hmac.New(sha256.New, []byte(wh.key))
	io.WriteString(h, timestamp)
	io.WriteString(h, token)
	return signature == hex.EncodeToString(h.Sum(nil))
}
<commit_msg>Increased security of webhook verification<commit_after>package sendcloud

import (
	"crypto/hmac"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/hex"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"time"
)

type Event struct {
	name   string
	time   time.Time
	rcpt   string
	msgid  string
	reason string
}

func (e *Event) Name() string    { return e.name }
func (e *Event) Time() time.Time { return e.time }
func (e *Event) Rcpt() string    { return e.rcpt }
func (e *Event) MsgId() string   { return e.msgid }
func (e *Event) Reason() string  { return e.reason }

var (
	ErrMethodNotAllowed = fmt.Errorf("method not allowed")
	ErrBadSignature     = fmt.Errorf("bad signature")
	ErrInvalidTimestamp = fmt.Errorf("invalid timestamp")
	ErrInvalidForm      = fmt.Errorf("invalid form data")
)

type Webhook struct {
	key string
}

func NewWebhook(key string) *Webhook {
	return &Webhook{key}
}

func (wh *Webhook) Handle(w http.ResponseWriter, req *http.Request) (evt *Event, err error) {
	if req.Method != "POST" {
		err = ErrMethodNotAllowed
		w.Header().Set("Allow", "POST")
		http.Error(w, "only POST method is allowed", http.StatusMethodNotAllowed)
		return
	}

	if err = req.ParseForm(); err != nil {
		err = ErrInvalidForm
		http.Error(w, "invalid form", http.StatusBadRequest)
		return
	}

	ts := req.Form.Get("timestamp")
	if !wh.Verify(ts, req.Form.Get("token"), req.Form.Get("signature")) {
		err = ErrBadSignature
		http.Error(w, "bad signature", http.StatusForbidden)
		return
	}

	unix, err := strconv.ParseInt(ts, 10, 64) // millisecond since Unix epoch
	if err != nil {
		err = ErrInvalidTimestamp
		http.Error(w, "invalid timestamp", http.StatusBadRequest)
		return
	}
	evt = &Event{
		time:   time.Unix(0, unix*1e6), // 1 ms = 1e6 ns
		name:   req.Form.Get("event"),
		rcpt:   req.Form.Get("recipient"),
		msgid:  req.Form.Get("emailId"),
		reason: req.Form.Get("reason"),
	}
	return
}

func (wh *Webhook) Verify(timestamp, token, signature string) bool {
	h := hmac.New(sha256.New, []byte(wh.key))
	io.WriteString(h, timestamp)
	io.WriteString(h, token)
	calcSig := h.Sum(nil)
	sig, err := hex.DecodeString(signature)
	if err != nil {
		return false
	}
	if len(sig) != len(calcSig) {
		return false
	}
	return subtle.ConstantTimeCompare(sig, calcSig) == 1
}
<|endoftext|>


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

// boot allows to handover a system running linuxboot/u-root
// to a legacy preinstalled operating system by replacing the traditional
// bootloader path

//
// Synopsis:
//	boot [-v][-dry-run]
//
// Description:
//	If returns to u-root shell, the code didn't found a local bootable option
//
//      -v prints messages
//      -dry-run doesn't really boot
//
// Notes:
//	The code is looking for boot/grub/grub.cfg file as to identify the
//	boot option.
//	The first bootable device found in the block device tree is the one used
//	Windows is not supported (that is a work in progress)
//
// Example:
//	boot -v 	- Start the script in verbose mode for debugging purpose

package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/u-root/u-root/pkg/boot"
	"github.com/u-root/u-root/pkg/boot/bls"
	"github.com/u-root/u-root/pkg/boot/grub"
	"github.com/u-root/u-root/pkg/boot/syslinux"
	"github.com/u-root/u-root/pkg/cmdline"
	"github.com/u-root/u-root/pkg/mount"
	"github.com/u-root/u-root/pkg/mount/block"
	"github.com/u-root/u-root/pkg/ulog"
)

const (
	bootableMBR     = 0xaa55
	signatureOffset = 510
)

type bootEntry struct {
	kernel  string
	initrd  string
	cmdline string
}

var (
	verbose     = flag.Bool("v", false, "Print debug messages")
	debug       = func(string, ...interface{}) {}
	dryRun      = flag.Bool("dry-run", false, "load kernel, but don't kexec it")
	defaultBoot = flag.String("boot", "", "entry to boot (default to the configuration file default)")
	list        = flag.Bool("list", false, "list found configurations")

	removeCmdlineItem = flag.String("remove", "console", "comma separated list of kernel params value to remove from parsed kernel configuration (default to console)")
	reuseCmdlineItem  = flag.String("reuse", "console", "comma separated list of kernel params value to reuse from current kernel (default to console)")
	appendCmdline     = flag.String("append", "", "Additional kernel params")
)

// updateBootCmdline get the kernel command line parameters and filter it:
// it removes parameters listed in 'remove' and append extra parameters from
// the 'append' and 'reuse' flags
func updateBootCmdline(cl string) string {
	f := cmdline.NewUpdateFilter(*appendCmdline, strings.Split(*removeCmdlineItem, ","), strings.Split(*reuseCmdlineItem, ","))
	return f.Update(cl)
}

func mountAndBoot(device *block.BlockDev, mountDir string) {
	os.MkdirAll(mountDir, 0777)

	mp, err := device.Mount(mountDir, mount.ReadOnly)
	if err != nil {
		return
	}
	defer mp.Unmount(mount.MNT_DETACH)

	imgs, err := bls.ScanBLSEntries(ulog.Log, mountDir)
	if err != nil {
		log.Printf("Failed to parse systemd-boot BootLoaderSpec configs, trying another format...: %v", err)
	}

	grubImgs, err := grub.ParseLocalConfig(context.Background(), mountDir)
	if err != nil {
		log.Printf("Failed to parse GRUB configs from %s, trying another format...: %v", device, err)
	}
	imgs = append(imgs, grubImgs...)

	syslinuxImgs, err := syslinux.ParseLocalConfig(context.Background(), mountDir)
	if err != nil {
		log.Printf("Failed to parse syslinux configs from %s: %v", device, err)
	}
	imgs = append(imgs, syslinuxImgs...)

	if len(imgs) == 0 {
		return
	}

	// Boot just the first image.
	img := imgs[0]

	// Make changes to the kernel command line based on our cmdline.
	if li, ok := img.(*boot.LinuxImage); ok {
		li.Cmdline = updateBootCmdline(li.Cmdline)
	}

	log.Printf("BootImage: %s", img)
	if err := img.Load(*verbose); err != nil {
		log.Printf("kexec load of %v failed: %v", img, err)
		return
	}

	if err := mp.Unmount(mount.MNT_DETACH); err != nil {
		log.Printf("Can't unmount %v: %v", mp, err)
	}
	if *dryRun {
		return
	}

	if err := boot.Execute(); err != nil {
		log.Printf("boot.Execute of %v failed: %v", img, err)
	}

	// kexec was successful. kexec should have taken over. What happened?
	log.Fatalf("kexec boot returned success, but new kernel is not running...")
}

// Localboot tries to boot from any local filesystem by parsing grub configuration
func Localboot() error {
	blockDevs, err := block.GetBlockDevices()
	if err != nil {
		return errors.New("no available block devices to boot from")
	}

	// Try to only boot from "good" block devices.
	blockDevs = blockDevs.FilterZeroSize()
	debug("Booting from the following block devices: %v", blockDevs)

	mountPoints, err := ioutil.TempDir("", "u-root-boot")
	if err != nil {
		return fmt.Errorf("Can't create tmpdir: %v", err)
	}
	defer os.RemoveAll(mountPoints)

	for _, device := range blockDevs {
		dir := filepath.Join(mountPoints, device.Name)
		mountAndBoot(device, dir)
	}
	return fmt.Errorf("Sorry no bootable device found")
}

func main() {
	flag.Parse()

	if *verbose {
		debug = log.Printf
	}

	if err := Localboot(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>cmds/boot: introduce boot menu<commit_after>// Copyright 2012-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// boot allows to handover a system running linuxboot/u-root
// to a legacy preinstalled operating system by replacing the traditional
// bootloader path

//
// Synopsis:
//	boot [-v][-dry-run]
//
// Description:
//	If returns to u-root shell, the code didn't found a local bootable option
//
//      -v prints messages
//      -dry-run doesn't really boot
//
// Notes:
//	The code is looking for boot/grub/grub.cfg file as to identify the
//	boot option.
//	The first bootable device found in the block device tree is the one used
//	Windows is not supported (that is a work in progress)
//
// Example:
//	boot -v 	- Start the script in verbose mode for debugging purpose

package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/u-root/u-root/pkg/boot"
	"github.com/u-root/u-root/pkg/boot/bls"
	"github.com/u-root/u-root/pkg/boot/grub"
	"github.com/u-root/u-root/pkg/boot/menu"
	"github.com/u-root/u-root/pkg/boot/syslinux"
	"github.com/u-root/u-root/pkg/cmdline"
	"github.com/u-root/u-root/pkg/mount"
	"github.com/u-root/u-root/pkg/mount/block"
	"github.com/u-root/u-root/pkg/ulog"
)

const (
	bootableMBR     = 0xaa55
	signatureOffset = 510
)

type bootEntry struct {
	kernel  string
	initrd  string
	cmdline string
}

var (
	verbose     = flag.Bool("v", false, "Print debug messages")
	debug       = func(string, ...interface{}) {}
	dryRun      = flag.Bool("dry-run", false, "load kernel, but don't kexec it")
	defaultBoot = flag.String("boot", "", "entry to boot (default to the configuration file default)")
	list        = flag.Bool("list", false, "list found configurations")

	removeCmdlineItem = flag.String("remove", "console", "comma separated list of kernel params value to remove from parsed kernel configuration (default to console)")
	reuseCmdlineItem  = flag.String("reuse", "console", "comma separated list of kernel params value to reuse from current kernel (default to console)")
	appendCmdline     = flag.String("append", "", "Additional kernel params")
)

// updateBootCmdline get the kernel command line parameters and filter it:
// it removes parameters listed in 'remove' and append extra parameters from
// the 'append' and 'reuse' flags
func updateBootCmdline(cl string) string {
	f := cmdline.NewUpdateFilter(*appendCmdline, strings.Split(*removeCmdlineItem, ","), strings.Split(*reuseCmdlineItem, ","))
	return f.Update(cl)
}

func parse(device *block.BlockDev, mountDir string) []boot.OSImage {
	imgs, err := bls.ScanBLSEntries(ulog.Log, mountDir)
	if err != nil {
		log.Printf("Failed to parse systemd-boot BootLoaderSpec configs, trying another format...: %v", err)
	}

	grubImgs, err := grub.ParseLocalConfig(context.Background(), mountDir)
	if err != nil {
		log.Printf("Failed to parse GRUB configs from %s, trying another format...: %v", device, err)
	}
	imgs = append(imgs, grubImgs...)

	syslinuxImgs, err := syslinux.ParseLocalConfig(context.Background(), mountDir)
	if err != nil {
		log.Printf("Failed to parse syslinux configs from %s: %v", device, err)
	}
	imgs = append(imgs, syslinuxImgs...)

	return imgs
}

// Localboot tries to boot from any local filesystem by parsing grub configuration
func Localboot() ([]boot.OSImage, []*mount.MountPoint, error) {
	blockDevs, err := block.GetBlockDevices()
	if err != nil {
		return nil, nil, errors.New("no available block devices to boot from")
	}

	// Try to only boot from "good" block devices.
	blockDevs = blockDevs.FilterZeroSize()
	debug("Booting from the following block devices: %v", blockDevs)

	mountPoints, err := ioutil.TempDir("", "u-root-boot")
	if err != nil {
		return nil, nil, fmt.Errorf("cannot create tmpdir: %v", err)
	}

	var images []boot.OSImage
	var mps []*mount.MountPoint
	for _, device := range blockDevs {
		dir := filepath.Join(mountPoints, device.Name)

		os.MkdirAll(dir, 0777)
		mp, err := device.Mount(dir, mount.ReadOnly)
		if err != nil {
			continue
		}

		imgs := parse(device, dir)
		images = append(images, imgs...)
		mps = append(mps, mp)
	}
	return images, mps, nil
}

func main() {
	flag.Parse()

	if *verbose {
		debug = log.Printf
	}

	images, mps, err := Localboot()
	if err != nil {
		log.Fatal(err)
	}
	for _, img := range images {
		// Make changes to the kernel command line based on our cmdline.
		if li, ok := img.(*boot.LinuxImage); ok {
			li.Cmdline = updateBootCmdline(li.Cmdline)
		}
	}

	menuEntries := menu.OSImages(*dryRun, images...)
	menuEntries = append(menuEntries, menu.Reboot{})
	menuEntries = append(menuEntries, menu.StartShell{})

	chosenEntry := menu.ShowMenuAndLoad(os.Stdin, menuEntries...)

	// Clean up.
	for _, mp := range mps {
		if err := mp.Unmount(mount.MNT_DETACH); err != nil {
			debug("Failed to unmount %s: %v", mp, err)
		}
	}
	if chosenEntry == nil {
		log.Fatalf("Nothing to boot.")
	}
	if *dryRun {
		log.Printf("Chosen menu entry: %s", chosenEntry)
		os.Exit(0)
	}
	// Exec should either return an error or not return at all.
	if err := chosenEntry.Exec(); err != nil {
		log.Fatalf("Failed to exec %s: %v", chosenEntry, err)
	}

	// Kexec should either return an error or not return.
	panic("unreachable")
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cluster

import (
	"errors"
	"fmt"
	"sync"

	"github.com/m3db/m3/src/dbnode/client"
	"github.com/m3db/m3/src/dbnode/generated/thrift/rpc"
	"github.com/m3db/m3/src/dbnode/network/server/tchannelthrift"
	"github.com/m3db/m3/src/dbnode/network/server/tchannelthrift/convert"
	tterrors "github.com/m3db/m3/src/dbnode/network/server/tchannelthrift/errors"
	"github.com/m3db/m3/src/dbnode/storage/index"
	"github.com/m3db/m3/src/x/checked"
	xerrors "github.com/m3db/m3/src/x/errors"
	"github.com/m3db/m3/src/x/ident"
	xtime "github.com/m3db/m3/src/x/time"

	"github.com/uber/tchannel-go/thrift"
)

type service struct {
	sync.RWMutex

	client client.Client
	active client.Session
	opts   client.Options
	idPool ident.Pool
	health *rpc.HealthResult_
}

// NewService creates a new cluster TChannel Thrift service
func NewService(client client.Client) rpc.TChanCluster {
	s := &service{
		client: client,
		opts:   client.Options(),
		idPool: client.Options().IdentifierPool(),
		health: &rpc.HealthResult_{Ok: true, Status: "up"},
	}
	return s
}

func (s *service) session() (client.Session, error) {
	s.RLock()
	session := s.active
	s.RUnlock()
	if session != nil {
		return session, nil
	}

	s.Lock()
	if s.active != nil {
		session := s.active
		s.Unlock()
		return session, nil
	}
	session, err := s.client.DefaultSession()
	if err != nil {
		s.Unlock()
		return nil, err
	}
	s.active = session
	s.Unlock()

	return session, nil
}

func (s *service) Close() error {
	var err error
	s.Lock()
	if s.active != nil {
		err = s.active.Close()
	}
	s.Unlock()
	return err
}

func (s *service) Health(ctx thrift.Context) (*rpc.HealthResult_, error) {
	s.RLock()
	health := s.health
	s.RUnlock()
	return health, nil
}

func (s *service) Query(tctx thrift.Context, req *rpc.QueryRequest) (*rpc.QueryResult_, error) {
	start, rangeStartErr := convert.ToTime(req.RangeStart, req.RangeType)
	end, rangeEndErr := convert.ToTime(req.RangeEnd, req.RangeType)

	if rangeStartErr != nil || rangeEndErr != nil {
		return nil, tterrors.NewBadRequestError(xerrors.FirstError(rangeStartErr, rangeEndErr))
	}

	q, err := convert.FromRPCQuery(req.Query)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	nsID := ident.StringID(req.NameSpace)
	opts := index.QueryOptions{
		StartInclusive: start,
		EndExclusive:   end,
	}

	if l := req.Limit; l != nil {
		opts.SeriesLimit = int(*l)
	}
	if len(req.Source) > 0 {
		opts.Source = req.Source
	}

	session, err := s.session()
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	if req.NoData != nil && *req.NoData {
		results, metadata, err := session.FetchTaggedIDs(tctx, nsID,
			index.Query{Query: q}, opts)
		if err != nil {
			return nil, convert.ToRPCError(err)
		}

		result := &rpc.QueryResult_{
			Exhaustive: metadata.Exhaustive,
		}

		for results.Next() {
			_, tsID, tags := results.Current()
			curr := &rpc.QueryResultElement{
				ID: tsID.String(),
			}
			result.Results = append(result.Results, curr)
			for tags.Next() {
				t := tags.Current()
				curr.Tags = append(curr.Tags, &rpc.Tag{
					Name:  t.Name.String(),
					Value: t.Value.String(),
				})
			}
			if err := tags.Err(); err != nil {
				return nil, convert.ToRPCError(err)
			}
			tags.Close()
		}

		if err := results.Err(); err != nil {
			return nil, convert.ToRPCError(err)
		}

		return result, nil
	}

	results, metadata, err := session.FetchTagged(tctx, nsID,
		index.Query{Query: q}, opts)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	result := &rpc.QueryResult_{
		Results:    make([]*rpc.QueryResultElement, 0, results.Len()),
		Exhaustive: metadata.Exhaustive,
	}

	for _, series := range results.Iters() {
		curr := &rpc.QueryResultElement{
			ID: series.ID().String(),
		}
		result.Results = append(result.Results, curr)
		tags := series.Tags()
		for tags.Next() {
			t := tags.Current()
			curr.Tags = append(curr.Tags, &rpc.Tag{
				Name:  t.Name.String(),
				Value: t.Value.String(),
			})
		}
		if err := tags.Err(); err != nil {
			return nil, convert.ToRPCError(err)
		}
		tags.Close()

		var datapoints []*rpc.Datapoint
		for series.Next() {
			dp, _, annotation := series.Current()

			timestamp, timestampErr := convert.ToValue(dp.TimestampNanos, req.ResultTimeType)
			if timestampErr != nil {
				return nil, xerrors.NewInvalidParamsError(timestampErr)
			}

			datapoints = append(datapoints, &rpc.Datapoint{
				Timestamp:  timestamp,
				Value:      dp.Value,
				Annotation: annotation,
			})
		}
		if err := series.Err(); err != nil {
			return nil, convert.ToRPCError(err)
		}
		curr.Datapoints = datapoints
	}

	results.Close()
	return result, nil
}

func (s *service) Fetch(tctx thrift.Context, req *rpc.FetchRequest) (*rpc.FetchResult_, error) {
	session, err := s.session()
	if err != nil {
		return nil, tterrors.NewInternalError(err)
	}

	start, rangeStartErr := convert.ToTime(req.RangeStart, req.RangeType)
	end, rangeEndErr := convert.ToTime(req.RangeEnd, req.RangeType)
	if rangeStartErr != nil || rangeEndErr != nil {
		return nil, tterrors.NewBadRequestError(xerrors.FirstError(rangeStartErr, rangeEndErr))
	}

	ctx := tchannelthrift.Context(tctx)
	nsID := s.idPool.GetStringID(ctx, req.NameSpace)
	tsID := s.idPool.GetStringID(ctx, req.ID)

	it, err := session.Fetch(nsID, tsID, start, end)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	defer it.Close()

	result := rpc.NewFetchResult_()
	// Make datapoints an initialized empty array for JSON serialization as empty array than null
	result.Datapoints = make([]*rpc.Datapoint, 0)

	for it.Next() {
		dp, _, annotation := it.Current()
		ts, tsErr := convert.ToValue(dp.TimestampNanos, req.ResultTimeType)
		if tsErr != nil {
			return nil, tterrors.NewBadRequestError(tsErr)
		}

		datapoint := rpc.NewDatapoint()
		datapoint.Timestamp = ts
		datapoint.Value = dp.Value
		datapoint.Annotation = annotation
		result.Datapoints = append(result.Datapoints, datapoint)
	}
	if err := it.Err(); err != nil {
		return nil, tterrors.NewInternalError(err)
	}

	return result, nil
}

func (s *service) Aggregate(ctx thrift.Context, req *rpc.AggregateQueryRequest) (*rpc.AggregateQueryResult_, error) {
	session, err := s.session()
	if err != nil {
		return nil, tterrors.NewInternalError(err)
	}

	ns, query, opts, err := convert.FromRPCAggregateQueryRequest(req)
	if err != nil {
		return nil, tterrors.NewBadRequestError(err)
	}

	if len(req.Source) > 0 {
		opts.Source = req.Source
	}

	iter, metadata, err := session.Aggregate(ctx, ns, query, opts)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}
	defer iter.Finalize()

	response := &rpc.AggregateQueryResult_{
		Exhaustive: metadata.Exhaustive,
	}
	for iter.Next() {
		name, values := iter.Current()
		responseElem := &rpc.AggregateQueryResultTagNameElement{
			TagName: name.String(),
		}
		responseElem.TagValues = make([]*rpc.AggregateQueryResultTagValueElement, 0, values.Remaining())
		for values.Next() {
			value := values.Current()
			responseElem.TagValues = append(responseElem.TagValues, &rpc.AggregateQueryResultTagValueElement{
				TagValue: value.String(),
			})
		}
		if err := values.Err(); err != nil {
			return nil, convert.ToRPCError(err)
		}
		response.Results = append(response.Results, responseElem)
	}
	if err := iter.Err(); err != nil {
		return nil, convert.ToRPCError(err)
	}
	return response, nil
}

func (s *service) Write(tctx thrift.Context, req *rpc.WriteRequest) error {
	session, err := s.session()
	if err != nil {
		return tterrors.NewInternalError(err)
	}
	if req.Datapoint == nil {
		return tterrors.NewBadRequestError(fmt.Errorf("requires datapoint"))
	}
	dp := req.Datapoint
	unit, unitErr := convert.ToUnit(dp.TimestampTimeType)
	if unitErr != nil {
		return tterrors.NewBadRequestError(unitErr)
	}
	d, err := unit.Value()
	if err != nil {
		return tterrors.NewBadRequestError(err)
	}
	ts := xtime.FromNormalizedTime(dp.Timestamp, d)

	ctx := tchannelthrift.Context(tctx)
	nsID := s.idPool.GetStringID(ctx, req.NameSpace)
	tsID := s.idPool.GetStringID(ctx, req.ID)
	err = session.Write(nsID, tsID, ts, dp.Value, unit, dp.Annotation)
	if err != nil {
		return convert.ToRPCError(err)
	}
	return nil
}

func (s *service) WriteTagged(tctx thrift.Context, req *rpc.WriteTaggedRequest) error {
	session, err := s.session()
	if err != nil {
		return tterrors.NewInternalError(err)
	}
	if req.Datapoint == nil {
		return tterrors.NewBadRequestError(fmt.Errorf("requires datapoint"))
	}
	dp := req.Datapoint
	unit, unitErr := convert.ToUnit(dp.TimestampTimeType)
	if unitErr != nil {
		return tterrors.NewBadRequestError(unitErr)
	}
	d, err := unit.Value()
	if err != nil {
		return tterrors.NewBadRequestError(err)
	}
	ts := xtime.FromNormalizedTime(dp.Timestamp, d)

	ctx := tchannelthrift.Context(tctx)
	nsID := s.idPool.GetStringID(ctx, req.NameSpace)
	tsID := s.idPool.GetStringID(ctx, req.ID)
	var tags ident.Tags
	for _, tag := range req.Tags {
		tags.Append(s.idPool.GetStringTag(ctx, tag.Name, tag.Value))
	}
	err = session.WriteTagged(nsID, tsID, ident.NewTagsIterator(tags),
		ts, dp.Value, unit, dp.Annotation)
	if err != nil {
		return convert.ToRPCError(err)
	}
	return nil
}

func (s *service) Truncate(tctx thrift.Context, req *rpc.TruncateRequest) (*rpc.TruncateResult_, error) {
	session, err := s.session()
	if err != nil {
		return nil, tterrors.NewInternalError(err)
	}

	adminSession, ok := session.(client.AdminSession)
	if !ok {
		return nil, tterrors.NewInternalError(errors.New("unable to get an admin session"))
	}

	nsID := ident.BinaryID(checked.NewBytes(req.NameSpace, nil))
	truncated, err := adminSession.Truncate(nsID)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	res := rpc.NewTruncateResult_()
	res.NumSeries = truncated
	return res, nil
}
<commit_msg>Do not close tags iterator in Cluster.Query (#3752)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package cluster

import (
	"errors"
	"fmt"
	"sync"

	"github.com/m3db/m3/src/dbnode/client"
	"github.com/m3db/m3/src/dbnode/generated/thrift/rpc"
	"github.com/m3db/m3/src/dbnode/network/server/tchannelthrift"
	"github.com/m3db/m3/src/dbnode/network/server/tchannelthrift/convert"
	tterrors "github.com/m3db/m3/src/dbnode/network/server/tchannelthrift/errors"
	"github.com/m3db/m3/src/dbnode/storage/index"
	"github.com/m3db/m3/src/x/checked"
	xerrors "github.com/m3db/m3/src/x/errors"
	"github.com/m3db/m3/src/x/ident"
	xtime "github.com/m3db/m3/src/x/time"

	"github.com/uber/tchannel-go/thrift"
)

type service struct {
	sync.RWMutex

	client client.Client
	active client.Session
	opts   client.Options
	idPool ident.Pool
	health *rpc.HealthResult_
}

// NewService creates a new cluster TChannel Thrift service
func NewService(client client.Client) rpc.TChanCluster {
	s := &service{
		client: client,
		opts:   client.Options(),
		idPool: client.Options().IdentifierPool(),
		health: &rpc.HealthResult_{Ok: true, Status: "up"},
	}
	return s
}

func (s *service) session() (client.Session, error) {
	s.RLock()
	session := s.active
	s.RUnlock()
	if session != nil {
		return session, nil
	}

	s.Lock()
	if s.active != nil {
		session := s.active
		s.Unlock()
		return session, nil
	}
	session, err := s.client.DefaultSession()
	if err != nil {
		s.Unlock()
		return nil, err
	}
	s.active = session
	s.Unlock()

	return session, nil
}

func (s *service) Close() error {
	var err error
	s.Lock()
	if s.active != nil {
		err = s.active.Close()
	}
	s.Unlock()
	return err
}

func (s *service) Health(ctx thrift.Context) (*rpc.HealthResult_, error) {
	s.RLock()
	health := s.health
	s.RUnlock()
	return health, nil
}

func (s *service) Query(tctx thrift.Context, req *rpc.QueryRequest) (*rpc.QueryResult_, error) {
	start, rangeStartErr := convert.ToTime(req.RangeStart, req.RangeType)
	end, rangeEndErr := convert.ToTime(req.RangeEnd, req.RangeType)

	if rangeStartErr != nil || rangeEndErr != nil {
		return nil, tterrors.NewBadRequestError(xerrors.FirstError(rangeStartErr, rangeEndErr))
	}

	q, err := convert.FromRPCQuery(req.Query)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	nsID := ident.StringID(req.NameSpace)
	opts := index.QueryOptions{
		StartInclusive: start,
		EndExclusive:   end,
	}

	if l := req.Limit; l != nil {
		opts.SeriesLimit = int(*l)
	}
	if len(req.Source) > 0 {
		opts.Source = req.Source
	}

	session, err := s.session()
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	if req.NoData != nil && *req.NoData {
		results, metadata, err := session.FetchTaggedIDs(tctx, nsID,
			index.Query{Query: q}, opts)
		if err != nil {
			return nil, convert.ToRPCError(err)
		}

		defer results.Finalize()

		result := &rpc.QueryResult_{
			Exhaustive: metadata.Exhaustive,
		}

		for results.Next() {
			_, tsID, tags := results.Current()
			curr := &rpc.QueryResultElement{
				ID: tsID.String(),
			}
			result.Results = append(result.Results, curr)
			for tags.Next() {
				t := tags.Current()
				curr.Tags = append(curr.Tags, &rpc.Tag{
					Name:  t.Name.String(),
					Value: t.Value.String(),
				})
			}
			if err := tags.Err(); err != nil {
				return nil, convert.ToRPCError(err)
			}
		}

		if err := results.Err(); err != nil {
			return nil, convert.ToRPCError(err)
		}

		return result, nil
	}

	results, metadata, err := session.FetchTagged(tctx, nsID,
		index.Query{Query: q}, opts)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}
	defer results.Close()

	result := &rpc.QueryResult_{
		Results:    make([]*rpc.QueryResultElement, 0, results.Len()),
		Exhaustive: metadata.Exhaustive,
	}

	for _, series := range results.Iters() {
		curr := &rpc.QueryResultElement{
			ID: series.ID().String(),
		}
		result.Results = append(result.Results, curr)
		tags := series.Tags()
		for tags.Next() {
			t := tags.Current()
			curr.Tags = append(curr.Tags, &rpc.Tag{
				Name:  t.Name.String(),
				Value: t.Value.String(),
			})
		}
		if err := tags.Err(); err != nil {
			return nil, convert.ToRPCError(err)
		}

		var datapoints []*rpc.Datapoint
		for series.Next() {
			dp, _, annotation := series.Current()

			timestamp, timestampErr := convert.ToValue(dp.TimestampNanos, req.ResultTimeType)
			if timestampErr != nil {
				return nil, xerrors.NewInvalidParamsError(timestampErr)
			}

			datapoints = append(datapoints, &rpc.Datapoint{
				Timestamp:  timestamp,
				Value:      dp.Value,
				Annotation: annotation,
			})
		}
		if err := series.Err(); err != nil {
			return nil, convert.ToRPCError(err)
		}
		curr.Datapoints = datapoints
	}

	return result, nil
}

func (s *service) Fetch(tctx thrift.Context, req *rpc.FetchRequest) (*rpc.FetchResult_, error) {
	session, err := s.session()
	if err != nil {
		return nil, tterrors.NewInternalError(err)
	}

	start, rangeStartErr := convert.ToTime(req.RangeStart, req.RangeType)
	end, rangeEndErr := convert.ToTime(req.RangeEnd, req.RangeType)
	if rangeStartErr != nil || rangeEndErr != nil {
		return nil, tterrors.NewBadRequestError(xerrors.FirstError(rangeStartErr, rangeEndErr))
	}

	ctx := tchannelthrift.Context(tctx)
	nsID := s.idPool.GetStringID(ctx, req.NameSpace)
	tsID := s.idPool.GetStringID(ctx, req.ID)

	it, err := session.Fetch(nsID, tsID, start, end)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	defer it.Close()

	result := rpc.NewFetchResult_()
	// Make datapoints an initialized empty array for JSON serialization as empty array than null
	result.Datapoints = make([]*rpc.Datapoint, 0)

	for it.Next() {
		dp, _, annotation := it.Current()
		ts, tsErr := convert.ToValue(dp.TimestampNanos, req.ResultTimeType)
		if tsErr != nil {
			return nil, tterrors.NewBadRequestError(tsErr)
		}

		datapoint := rpc.NewDatapoint()
		datapoint.Timestamp = ts
		datapoint.Value = dp.Value
		datapoint.Annotation = annotation
		result.Datapoints = append(result.Datapoints, datapoint)
	}
	if err := it.Err(); err != nil {
		return nil, tterrors.NewInternalError(err)
	}

	return result, nil
}

func (s *service) Aggregate(ctx thrift.Context, req *rpc.AggregateQueryRequest) (*rpc.AggregateQueryResult_, error) {
	session, err := s.session()
	if err != nil {
		return nil, tterrors.NewInternalError(err)
	}

	ns, query, opts, err := convert.FromRPCAggregateQueryRequest(req)
	if err != nil {
		return nil, tterrors.NewBadRequestError(err)
	}

	if len(req.Source) > 0 {
		opts.Source = req.Source
	}

	iter, metadata, err := session.Aggregate(ctx, ns, query, opts)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}
	defer iter.Finalize()

	response := &rpc.AggregateQueryResult_{
		Exhaustive: metadata.Exhaustive,
	}
	for iter.Next() {
		name, values := iter.Current()
		responseElem := &rpc.AggregateQueryResultTagNameElement{
			TagName: name.String(),
		}
		responseElem.TagValues = make([]*rpc.AggregateQueryResultTagValueElement, 0, values.Remaining())
		for values.Next() {
			value := values.Current()
			responseElem.TagValues = append(responseElem.TagValues, &rpc.AggregateQueryResultTagValueElement{
				TagValue: value.String(),
			})
		}
		if err := values.Err(); err != nil {
			return nil, convert.ToRPCError(err)
		}
		response.Results = append(response.Results, responseElem)
	}
	if err := iter.Err(); err != nil {
		return nil, convert.ToRPCError(err)
	}
	return response, nil
}

func (s *service) Write(tctx thrift.Context, req *rpc.WriteRequest) error {
	session, err := s.session()
	if err != nil {
		return tterrors.NewInternalError(err)
	}
	if req.Datapoint == nil {
		return tterrors.NewBadRequestError(fmt.Errorf("requires datapoint"))
	}
	dp := req.Datapoint
	unit, unitErr := convert.ToUnit(dp.TimestampTimeType)
	if unitErr != nil {
		return tterrors.NewBadRequestError(unitErr)
	}
	d, err := unit.Value()
	if err != nil {
		return tterrors.NewBadRequestError(err)
	}
	ts := xtime.FromNormalizedTime(dp.Timestamp, d)

	ctx := tchannelthrift.Context(tctx)
	nsID := s.idPool.GetStringID(ctx, req.NameSpace)
	tsID := s.idPool.GetStringID(ctx, req.ID)
	err = session.Write(nsID, tsID, ts, dp.Value, unit, dp.Annotation)
	if err != nil {
		return convert.ToRPCError(err)
	}
	return nil
}

func (s *service) WriteTagged(tctx thrift.Context, req *rpc.WriteTaggedRequest) error {
	session, err := s.session()
	if err != nil {
		return tterrors.NewInternalError(err)
	}
	if req.Datapoint == nil {
		return tterrors.NewBadRequestError(fmt.Errorf("requires datapoint"))
	}
	dp := req.Datapoint
	unit, unitErr := convert.ToUnit(dp.TimestampTimeType)
	if unitErr != nil {
		return tterrors.NewBadRequestError(unitErr)
	}
	d, err := unit.Value()
	if err != nil {
		return tterrors.NewBadRequestError(err)
	}
	ts := xtime.FromNormalizedTime(dp.Timestamp, d)

	ctx := tchannelthrift.Context(tctx)
	nsID := s.idPool.GetStringID(ctx, req.NameSpace)
	tsID := s.idPool.GetStringID(ctx, req.ID)
	var tags ident.Tags
	for _, tag := range req.Tags {
		tags.Append(s.idPool.GetStringTag(ctx, tag.Name, tag.Value))
	}
	err = session.WriteTagged(nsID, tsID, ident.NewTagsIterator(tags),
		ts, dp.Value, unit, dp.Annotation)
	if err != nil {
		return convert.ToRPCError(err)
	}
	return nil
}

func (s *service) Truncate(tctx thrift.Context, req *rpc.TruncateRequest) (*rpc.TruncateResult_, error) {
	session, err := s.session()
	if err != nil {
		return nil, tterrors.NewInternalError(err)
	}

	adminSession, ok := session.(client.AdminSession)
	if !ok {
		return nil, tterrors.NewInternalError(errors.New("unable to get an admin session"))
	}

	nsID := ident.BinaryID(checked.NewBytes(req.NameSpace, nil))
	truncated, err := adminSession.Truncate(nsID)
	if err != nil {
		return nil, convert.ToRPCError(err)
	}

	res := rpc.NewTruncateResult_()
	res.NumSeries = truncated
	return res, nil
}
<|endoftext|>


<commit_before>package service

import (
	"golang.org/x/net/html"
	curl "github.com/andelf/go-curl"
	"bytes"
	"github.com/PuerkitoBio/goquery"
	"net/url"
	"fmt"
)

type AhrefsService interface {
	SignIn(string, string) string
}

type ahrefsService struct{}

func (ahrefsService) SignIn(email, password string) string {
	easy := curl.EasyInit()
	token := ""
	defer easy.Cleanup()

	fooTest := func(body []byte, userdata interface{}) bool {
		data, exists := getToken(body)
		if exists {
			token = data
		}

		return true
	}

	//first call
	easy.Setopt(curl.OPT_URL, "https://ahrefs.com/user/login")
	easy.Setopt(curl.OPT_SSL_VERIFYPEER, 1)
	easy.Setopt(curl.OPT_WRITEFUNCTION, fooTest)
	easy.Setopt(curl.OPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
	easy.Setopt(curl.OPT_VERBOSE, 1)
	easy.Setopt(curl.OPT_FOLLOWLOCATION, 1)
	easy.Setopt(curl.OPT_COOKIEJAR, "./cookiejar")
	easy.Setopt(curl.OPT_COOKIEFILE, "./cookiejar")
	easy.Setopt(curl.OPT_NOBODY, 0)
	easy.Perform()

	//second call

	easy.Setopt(curl.OPT_URL, "https://ahrefs.com/user/login")
	easy.Setopt(curl.OPT_HTTPHEADER, []string{
		"Referer: https://ahrefs.com/user/login",
		"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
		"Accept-Language: en-US,en;q=0.5",
		"Accept-Encoding: gzip, deflate",
		"Connection: keep-alive",
	})
	easy.Setopt(curl.OPT_POST, 1)
	form := url.Values{}
	form.Add("_token",    token)
	form.Add("email",     email)
	form.Add("password",  password)
	form.Add("return_to", "https://ahrefs.com/")
	postFields := form.Encode()
	fmt.Print(postFields + "\n")
	easy.Setopt(curl.OPT_POSTFIELDSIZE, len(postFields))
	easy.Setopt(curl.OPT_POSTFIELDS, postFields)
	easy.Perform()

	return "true"
}

func getToken(body []byte) (string, bool) {
	rootNode, err := html.Parse(bytes.NewReader(body))
	if err != nil {
		return "", false
	}

	meta := goquery.NewDocumentFromNode(rootNode).Find("meta[name=_token]")
	token, exists := meta.Attr("content")
	if exists == false {
		return "", false
	}

	return token, true

}

func NewService() ahrefsService {
	return ahrefsService{}
}
<commit_msg>parsing html after login to know where we are: dashboard or login form<commit_after>package service

import (
	"golang.org/x/net/html"
	curl "github.com/andelf/go-curl"
	"bytes"
	"github.com/PuerkitoBio/goquery"
	"net/url"
	"fmt"
	"strings"
)

type AhrefsService interface {
	SignIn(string, string) string
}

type ahrefsService struct{}

func (ahrefsService) SignIn(email, password string) string {
	easy := curl.EasyInit()
	token := ""
	receivedHTML := ""
	defer easy.Cleanup()

	fooTest := func(body []byte, userdata interface{}) bool {
		receivedHTML += string(body)
		data, exists := getToken(body)
		if exists {
			token = data
		}

		return true
	}

	//first call
	easy.Setopt(curl.OPT_URL, "https://ahrefs.com/user/login")
	easy.Setopt(curl.OPT_SSL_VERIFYPEER, 1)
	easy.Setopt(curl.OPT_WRITEFUNCTION, fooTest)
	easy.Setopt(curl.OPT_USERAGENT, "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
	easy.Setopt(curl.OPT_VERBOSE, 1)
	easy.Setopt(curl.OPT_FOLLOWLOCATION, 1)
	easy.Setopt(curl.OPT_COOKIEJAR, "./cookiejar")
	easy.Setopt(curl.OPT_COOKIEFILE, "./cookiejar")
	easy.Setopt(curl.OPT_NOBODY, 0)
	easy.Perform()

	// lame and stupid method, but we have only callback for receiving data from curl_easy
	if strings.Contains(receivedHTML, "<strong>Dashboard") {
		return "true"
	}
	receivedHTML = ""

	//second call
	easy.Setopt(curl.OPT_URL, "https://ahrefs.com/user/login")
	easy.Setopt(curl.OPT_HTTPHEADER, []string{
		"Referer: https://ahrefs.com/user/login",
		"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
		"Accept-Language: en-US,en;q=0.5",
		"Accept-Encoding: gzip, deflate",
		"Connection: keep-alive",
	})
	easy.Setopt(curl.OPT_POST, 1)
	form := url.Values{}
	form.Add("_token",    token)
	form.Add("email",     email)
	form.Add("password",  password)
	form.Add("return_to", "https://ahrefs.com/")
	postFields := form.Encode()
	fmt.Print(postFields + "\n")
	easy.Setopt(curl.OPT_POSTFIELDSIZE, len(postFields))
	easy.Setopt(curl.OPT_POSTFIELDS, postFields)
	easy.Perform()

	return "true"
}

func getToken(body []byte) (string, bool) {
	rootNode, err := html.Parse(bytes.NewReader(body))
	if err != nil {
		return "", false
	}

	meta := goquery.NewDocumentFromNode(rootNode).Find("meta[name=_token]")
	token, exists := meta.Attr("content")
	if exists == false {
		return "", false
	}

	return token, true

}

func NewService() ahrefsService {
	return ahrefsService{}
}
<|endoftext|>


<commit_before>package fake

import (
	"bytes"
	"math/rand"
	"io"
	"archive/tar"
	"os/exec"
	"github.com/kr/pty"
)

func FakeTar() (io.Reader, error) {
	content := []byte("Hello world!\n")
	buf := new(bytes.Buffer)
	tw := tar.NewWriter(buf)
	for _, name := range []string {"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres", "/var/log/postgres/postgres.conf"} {
		hdr := new(tar.Header)
		hdr.Size = int64(len(content))
		hdr.Name = name
		if err := tw.WriteHeader(hdr); err != nil {
			return nil, err
		}
		tw.Write([]byte(content))
	}
	tw.Close()
	return buf, nil
}

func WriteFakeTar(dst io.Writer) error {
	if data, err := FakeTar(); err != nil {
		return err
	} else if _, err := io.Copy(dst, data); err != nil {
		return err
	}
	return nil
}

func RandomBytesChanged() uint {
	return uint(rand.Int31n(24 * 1024 * 1024))
}

func RandomFilesChanged() uint {
	return uint(rand.Int31n(42))
}

func RandomContainerSize() uint {
	return uint(rand.Int31n(142 * 1024 * 1024))
}

func ContainerRunning() bool {
	return false
}

func StartCommand(cmd *exec.Cmd, interactive bool) (io.WriteCloser, io.ReadCloser, error) {
	if interactive {
		term, err := pty.Start(cmd)
		if err != nil {
			return nil, nil, err
		}
		return term, term, nil
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, nil, err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}
	return stdin, stdout, nil
}

<commit_msg>FakeTar: /var/log/postgres can't be both a folder and a file<commit_after>package fake

import (
	"bytes"
	"math/rand"
	"io"
	"archive/tar"
	"os/exec"
	"github.com/kr/pty"
)

func FakeTar() (io.Reader, error) {
	content := []byte("Hello world!\n")
	buf := new(bytes.Buffer)
	tw := tar.NewWriter(buf)
	for _, name := range []string {"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
		hdr := new(tar.Header)
		hdr.Size = int64(len(content))
		hdr.Name = name
		if err := tw.WriteHeader(hdr); err != nil {
			return nil, err
		}
		tw.Write([]byte(content))
	}
	tw.Close()
	return buf, nil
}

func WriteFakeTar(dst io.Writer) error {
	if data, err := FakeTar(); err != nil {
		return err
	} else if _, err := io.Copy(dst, data); err != nil {
		return err
	}
	return nil
}

func RandomBytesChanged() uint {
	return uint(rand.Int31n(24 * 1024 * 1024))
}

func RandomFilesChanged() uint {
	return uint(rand.Int31n(42))
}

func RandomContainerSize() uint {
	return uint(rand.Int31n(142 * 1024 * 1024))
}

func ContainerRunning() bool {
	return false
}

func StartCommand(cmd *exec.Cmd, interactive bool) (io.WriteCloser, io.ReadCloser, error) {
	if interactive {
		term, err := pty.Start(cmd)
		if err != nil {
			return nil, nil, err
		}
		return term, term, nil
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, nil, err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}
	return stdin, stdout, nil
}

<|endoftext|>


<commit_before>// Binary winpulse captures Audio from Windows and streams it to a PulseAudio server over SSH.
package main

import (
  "bufio"
  "context"
  "errors"
  "flag"
  "fmt"
  "io"
  "log"
  "os"
  "regexp"
  "time"

  "github.com/StalkR/misc/windows/audio"
  "github.com/StalkR/misc/windows/cygwin"
  "golang.org/x/crypto/ssh"
  "golang.org/x/crypto/ssh/agent"
  "golang.org/x/sync/errgroup"
)

var flagServer = flag.String("server", "", "PulseAudio server to connect to via SSH + pacat (user@host[:port]).")

func main() {
  flag.Parse()
  if err := launch(context.Background()); err != nil {
    log.Fatal(err)
  }
}

func launch(ctx context.Context) error {
  if *flagServer == "" {
    flag.PrintDefaults()
    return nil
  }
  userHost := regexp.MustCompile(`^([^@]*)@(.*)$`).FindStringSubmatch(*flagServer)
  if len(userHost) == 0 {
    return fmt.Errorf("invalid host")
  }

  g, ctx := errgroup.WithContext(ctx)
  g.Go(func() error {
    log.Print("Press enter to stop")
    bufio.NewScanner(os.Stdin).Scan()
    return errStop
  })

  r, w := io.Pipe()
  g.Go(func() error {
    defer w.Close()
    return audio.Capture(ctx, w)
  })

  g.Go(func() error {
    defer r.Close()
    for ; ; time.Sleep(time.Second) {
      if err := play(userHost[1], userHost[2], r); err != nil && err != io.EOF {
        log.Printf("error: %v", err)
      }
      select {
      case <-ctx.Done():
        return nil
      default:
      }
    }
  })

  if err := g.Wait(); err != nil && err != errStop {
    return err
  }
  return nil
}

var errStop = errors.New("stop")

func play(user, host string, stream io.Reader) error {
  client, err := connect(user, host)
  if err != nil {
    return err
  }
  log.Print("Connected to server")
  session, err := client.NewSession()
  if err != nil {
    return err
  }
  session.Stdin = stream
  if err := session.Start("pacat -p --format float32le"); err != nil {
    return err
  }
  log.Print("Connected to PulseAudio")
  return session.Wait()
}

func connect(user, host string) (*ssh.Client, error) {
  ag, err := cygwin.SSHAgent()
  if err != nil {
    return nil, err
  }
  defer ag.Close()
  config := &ssh.ClientConfig{
    User: user,
    Auth: []ssh.AuthMethod{
      ssh.PublicKeysCallback(agent.NewClient(ag).Signers),
    },
    HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  }
  return ssh.Dial("tcp", host, config)
}
<commit_msg>pulseaudio/winpulsessh: systray app<commit_after>// Binary winpulse captures Audio from Windows and streams it to a PulseAudio server over SSH.
// It shows in systray with PulseAudio icon, right-click to exit.
// Build with `-ldflags -H=windowsgui` to avoid launching a console window.
package main

import (
  "context"
  "errors"
  "flag"
  "io"
  "log"
  "regexp"
  "time"

  "github.com/StalkR/misc/pulseaudio/icon"
  "github.com/StalkR/misc/windows/audio"
  "github.com/StalkR/misc/windows/cygwin"
  "github.com/getlantern/systray"
  "golang.org/x/crypto/ssh"
  "golang.org/x/crypto/ssh/agent"
  "golang.org/x/sync/errgroup"
)

var flagServer = flag.String("server", "", "PulseAudio server to connect to via SSH + pacat (user@host[:port]).")

func main() {
  ctx := context.Background()
  flag.Parse()
  if *flagServer == "" {
    flag.PrintDefaults()
    return
  }
  userHost := regexp.MustCompile(`^([^@]*)@(.*)$`).FindStringSubmatch(*flagServer)
  if len(userHost) == 0 {
    log.Fatal("invalid host")
  }
  user, host := userHost[1], userHost[2]

  ctx, cancel := context.WithCancel(ctx)
  systray.Run(func() { onReady(ctx, user, host) }, cancel)
}

func onReady(ctx context.Context, user, host string) {
  systray.SetIcon(icon.Data)
  systray.SetTitle("WinPulse")
  exit := systray.AddMenuItem("Exit", "Exit")
  go func() {
    <-exit.ClickedCh
    systray.Quit()
  }()
  if err := launch(ctx, user, host); err != nil {
    log.Fatal(err)
  }
}

func launch(ctx context.Context, user, host string) error {
  gui := ctx
  g, ctx := errgroup.WithContext(ctx)
  g.Go(func() error {
    <-gui.Done()
    return errStop
  })

  r, w := io.Pipe()
  g.Go(func() error {
    defer w.Close()
    return audio.Capture(ctx, w)
  })

  g.Go(func() error {
    defer r.Close()
    for ; ; time.Sleep(time.Second) {
      systray.SetTooltip("Connecting to PulseAudio...")
      if err := play(user, host, r); err != nil && err != io.EOF {
        log.Printf("error: %v", err)
      }
      select {
      case <-ctx.Done():
        return nil
      default:
      }
    }
  })

  if err := g.Wait(); err != nil && err != errStop {
    return err
  }
  return nil
}

var errStop = errors.New("stop")

func play(user, host string, stream io.Reader) error {
  client, err := connect(user, host)
  if err != nil {
    return err
  }
  log.Print("Connected to server")
  session, err := client.NewSession()
  if err != nil {
    return err
  }
  session.Stdin = stream
  if err := session.Start("pacat -p --format float32le"); err != nil {
    return err
  }
  log.Print("Connected to PulseAudio")
  systray.SetTooltip("Connected to PulseAudio")
  return session.Wait()
}

func connect(user, host string) (*ssh.Client, error) {
  ag, err := cygwin.SSHAgent()
  if err != nil {
    return nil, err
  }
  defer ag.Close()
  config := &ssh.ClientConfig{
    User: user,
    Auth: []ssh.AuthMethod{
      ssh.PublicKeysCallback(agent.NewClient(ag).Signers),
    },
    HostKeyCallback: ssh.InsecureIgnoreHostKey(),
  }
  return ssh.Dial("tcp", host, config)
}
<|endoftext|>


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

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

	"github.com/wellington/wellington/context"

	wt "github.com/wellington/wellington"
	_ "github.com/wellington/wellington/handlers"
)

const version = `v0.4.0`

var (
	font, dir, gen, includes  string
	mainFile, style           string
	comments, watch           bool
	cpuprofile, buildDir      string
	ishttp, help, showVersion bool
)

func init() {
	flag.BoolVar(&showVersion, "version", false, "Show the app version")

	flag.BoolVar(&help, "help", false, "this help")
	flag.BoolVar(&help, "h", false, "this help")

	flag.StringVar(&buildDir, "b", "", "Build Directory")
	flag.StringVar(&gen, "gen", ".", "Directory for generated images")

	flag.StringVar(&includes, "p", "", "SASS import path")
	flag.StringVar(&dir, "dir", "", "Image directory")
	flag.StringVar(&dir, "d", "", "Image directory")
	flag.StringVar(&font, "font", ".", "Font Directory")

	flag.StringVar(&style, "style", "nested", "CSS nested style")
	flag.StringVar(&style, "s", "nested", "CSS nested style")
	flag.BoolVar(&comments, "comment", true, "Turn on source comments")
	flag.BoolVar(&comments, "c", true, "Turn on source comments")

	flag.BoolVar(&ishttp, "http", false, "Listen for http connections")
	flag.BoolVar(&watch, "watch", false, "File watcher that will rebuild css on file changes")
	flag.BoolVar(&watch, "w", false, "File watcher that will rebuild css on file changes")

	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
}

func main() {
	flag.Parse()

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

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

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

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

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

	style, ok := context.Style[style]

	if !ok {
		style = context.NESTED_STYLE
	}

	gba := wt.NewBuildArgs()

	gba.Dir = dir
	gba.BuildDir = buildDir
	gba.Includes = includes
	gba.Font = font
	gba.Style = style
	gba.Gen = gen
	gba.Comments = comments

	pMap := wt.NewPartialMap()
	// FIXME: Copy pasta with LoadAndBuild
	ctx := &context.Context{
		Sprites:      gba.Sprites,
		Imgs:         gba.Imgs,
		OutputStyle:  gba.Style,
		ImageDir:     gba.Dir,
		FontDir:      gba.Font,
		GenImgDir:    gba.Gen,
		Comments:     gba.Comments,
		IncludePaths: []string{gba.Includes},
	}

	if ishttp {
		err := http.ListenAndServe(":12345", httpHandler(ctx))

		if err != nil {
			log.Fatal("ListenAndServe: ", err)
		}

		return
	}

	if len(flag.Args()) == 0 {

		// Read from stdin
		fmt.Println("Reading from stdin, -h for help")
		out := os.Stdout
		in := os.Stdin

		var pout bytes.Buffer
		_, err := wt.StartParser(ctx, in, &pout, wt.NewPartialMap())
		if err != nil {
			log.Println(err)
		}
		err = ctx.Compile(&pout, out)

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

	sassPaths := make([]string, len(flag.Args()))
	for i, f := range flag.Args() {
		sassPaths[i] = filepath.Dir(f)
		err := wt.LoadAndBuild(f, gba, pMap)
		if err != nil {
			log.Println(err)
		}
	}

	if watch {
		w := wt.NewWatcher()
		w.PartialMap = pMap
		w.Dirs = sassPaths
		w.BArgs = gba
		w.Watch()

		fmt.Println("File watcher started use `ctrl+d` to exit")
		in := bufio.NewReader(os.Stdin)
		for {
			_, err := in.ReadString(' ')
			if err != nil {
				if err == io.EOF {
					os.Exit(0)
				}
				fmt.Println("error", err)
			}
		}
	}
}

func httpHandler(ctx *context.Context) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var pout bytes.Buffer

		// Set headers
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
		}
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		_, err := wt.StartParser(ctx, r.Body, &pout, wt.NewPartialMap())
		if err != nil {
			io.WriteString(w, err.Error())
			return
		}

		err = ctx.Compile(&pout, w)
		if err != nil {
			io.WriteString(w, err.Error())
		}
	})
}
<commit_msg>fix version declaration<commit_after>// Main package wraps sprite_sass tool for use with the command line
// See -h for list of available options
package main

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

	"github.com/wellington/wellington/context"

	wt "github.com/wellington/wellington"
	_ "github.com/wellington/wellington/handlers"
)

const version = `v0.6.0`

var (
	font, dir, gen, includes  string
	mainFile, style           string
	comments, watch           bool
	cpuprofile, buildDir      string
	ishttp, help, showVersion bool
)

func init() {
	flag.BoolVar(&showVersion, "version", false, "Show the app version")

	flag.BoolVar(&help, "help", false, "this help")
	flag.BoolVar(&help, "h", false, "this help")

	flag.StringVar(&buildDir, "b", "", "Build Directory")
	flag.StringVar(&gen, "gen", ".", "Directory for generated images")

	flag.StringVar(&includes, "p", "", "SASS import path")
	flag.StringVar(&dir, "dir", "", "Image directory")
	flag.StringVar(&dir, "d", "", "Image directory")
	flag.StringVar(&font, "font", ".", "Font Directory")

	flag.StringVar(&style, "style", "nested", "CSS nested style")
	flag.StringVar(&style, "s", "nested", "CSS nested style")
	flag.BoolVar(&comments, "comment", true, "Turn on source comments")
	flag.BoolVar(&comments, "c", true, "Turn on source comments")

	flag.BoolVar(&ishttp, "http", false, "Listen for http connections")
	flag.BoolVar(&watch, "watch", false, "File watcher that will rebuild css on file changes")
	flag.BoolVar(&watch, "w", false, "File watcher that will rebuild css on file changes")

	flag.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
}

func main() {
	flag.Parse()

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

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

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

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

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

	style, ok := context.Style[style]

	if !ok {
		style = context.NESTED_STYLE
	}

	gba := wt.NewBuildArgs()

	gba.Dir = dir
	gba.BuildDir = buildDir
	gba.Includes = includes
	gba.Font = font
	gba.Style = style
	gba.Gen = gen
	gba.Comments = comments

	pMap := wt.NewPartialMap()
	// FIXME: Copy pasta with LoadAndBuild
	ctx := &context.Context{
		Sprites:      gba.Sprites,
		Imgs:         gba.Imgs,
		OutputStyle:  gba.Style,
		ImageDir:     gba.Dir,
		FontDir:      gba.Font,
		GenImgDir:    gba.Gen,
		Comments:     gba.Comments,
		IncludePaths: []string{gba.Includes},
	}

	if ishttp {
		err := http.ListenAndServe(":12345", httpHandler(ctx))

		if err != nil {
			log.Fatal("ListenAndServe: ", err)
		}

		return
	}

	if len(flag.Args()) == 0 {

		// Read from stdin
		fmt.Println("Reading from stdin, -h for help")
		out := os.Stdout
		in := os.Stdin

		var pout bytes.Buffer
		_, err := wt.StartParser(ctx, in, &pout, wt.NewPartialMap())
		if err != nil {
			log.Println(err)
		}
		err = ctx.Compile(&pout, out)

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

	sassPaths := make([]string, len(flag.Args()))
	for i, f := range flag.Args() {
		sassPaths[i] = filepath.Dir(f)
		err := wt.LoadAndBuild(f, gba, pMap)
		if err != nil {
			log.Println(err)
		}
	}

	if watch {
		w := wt.NewWatcher()
		w.PartialMap = pMap
		w.Dirs = sassPaths
		w.BArgs = gba
		w.Watch()

		fmt.Println("File watcher started use `ctrl+d` to exit")
		in := bufio.NewReader(os.Stdin)
		for {
			_, err := in.ReadString(' ')
			if err != nil {
				if err == io.EOF {
					os.Exit(0)
				}
				fmt.Println("error", err)
			}
		}
	}
}

func httpHandler(ctx *context.Context) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var pout bytes.Buffer

		// Set headers
		if origin := r.Header.Get("Origin"); origin != "" {
			w.Header().Set("Access-Control-Allow-Origin", origin)
		}
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		_, err := wt.StartParser(ctx, r.Body, &pout, wt.NewPartialMap())
		if err != nil {
			io.WriteString(w, err.Error())
			return
		}

		err = ctx.Compile(&pout, w)
		if err != nil {
			io.WriteString(w, err.Error())
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"koding/db/models"
	"koding/db/mongodb"
	helper "koding/db/mongodb/modelhelper"
	"koding/helpers"
	"koding/tools/config"
	"koding/tools/logger"
	"time"

	"labix.org/v2/mgo/bson"
)

var log = logger.New("Guest cleaner worker")

var (
	configProfile = flag.String("c", "", "Configuration profile from file")
	flagSkip      = flag.Int("s", 0, "Configuration profile from file")
	flagLimit     = flag.Int("l", 1000, "Configuration profile from file")
	mongo         *mongodb.MongoDB
)

func initialize() {
	flag.Parse()
	if *configProfile == "" {
		log.Fatal("Please define config file with -c")
	}

	conf := config.MustConfig(*configProfile)
	helper.Initialize(conf.Mongo)
	mongo = helper.Mongo

}

func main() {
	// init the package
	initialize()

	log.Info("Guest cleaner worker started")

	iterOptions := helpers.NewIterOptions()
	iterOptions.CollectionName = "jAccounts"
	iterOptions.F = deleteGuestAccounts
	iterOptions.Filter = createFilter()
	iterOptions.DataType = &models.Account{}
	iterOptions.Limit = *flagLimit
	iterOptions.Skip = *flagSkip
	iterOptions.Log = log

	log.SetLevel(logger.DEBUG)

	err := helpers.Iter(mongo, iterOptions)
	if err != nil {
		log.Fatal("Error while iter: %v", err)
	}
	log.Info("Guest cleaner worker finished")
}

func createFilter() helper.Selector {
	oneHourAgo := time.Now().Add(-time.Minute * 60).UTC()
	return helper.Selector{
		"type":           "unregistered",
		"status":         helper.Selector{"$ne": "tobedeleted"},
		"meta.createdAt": helper.Selector{"$lte": oneHourAgo},
	}
}

func deleteGuestAccounts(account interface{}) {
	acc := account.(*models.Account)
	log.Info("Deleting account: %v", acc.Profile.Nickname)
	selector := helper.Selector{"$or": []helper.Selector{
		helper.Selector{"sourceId": acc.Id},
		helper.Selector{"targetId": acc.Id},
	}}

	// Get all relationships for current acc
	rels, err := helper.GetAllRelationships(selector)
	if err != nil {
		log.Error("Error while getting the all relationships for deletion %v", err)
		return
	}

	// clear all sessions for acc
	clearAllSessions(acc)

	// clear all JNames for current acc
	clearAllNames(acc)

	// Delete documents with all their relationships
	deleteDocumentsWithRelationships(rels)

	// now time to delete acc itself
	if err := helper.RemoveAccount(acc.Id); err != nil {
		log.Error("Error while deleting the acc %v", err)
		return
	}
}

// clearAllSessions deletes related JSesssions from database
func clearAllSessions(account *models.Account) {
	selector := helper.Selector{"username": account.Profile.Nickname}
	if err := helper.RemoveAllDocuments("jSessions", selector); err != nil {
		log.Error("Error while deleting from JSession %v", err)
	}
}

// clearAllNames deletes related documents from JName collection
func clearAllNames(account *models.Account) {
	selector := helper.Selector{"name": account.Profile.Nickname}
	if err := helper.RemoveAllDocuments("jName", selector); err != nil {
		log.Error("Error while deleting from JName %v", err)
	}
}

// deleteDocumentsWithRelationships accepts a slice of Relationship
// and range over it, for each relationship checks if source or the target
// is eligible for deletion, at the end deletes the relationship itself
func deleteDocumentsWithRelationships(rels []models.Relationship) {
	if len(rels) == 0 {
		log.Info("no item to process")
		return
	}
	for _, rel := range rels {
		if checkIfEligibleToDelete(rel.SourceName) {
			deleteDocument(helper.GetCollectionName(rel.SourceName), rel.SourceId)
		}
		if checkIfEligibleToDelete(rel.TargetName) {
			deleteDocument(helper.GetCollectionName(rel.TargetName), rel.TargetId)
		}
		deleteDocument("relationships", rel.Id)
	}
}

// safe to delete those documents
var whiteListedModels = []string{"JSession", "JUser", "JVM", "JDomain", "JAppStorage", "JName"}

//checkIfEligibleToDelete ranges over whiteListedModels and returns bool as checking
// name is in that list or not
func checkIfEligibleToDelete(modelName string) bool {
	for _, name := range whiteListedModels {
		if name == modelName {
			return true
		}
	}

	log.Debug("not eligible %v", modelName)
	return false
}

// deleteDocument deletes the given id from given collection
func deleteDocument(collectionName string, id bson.ObjectId) {
	log.Debug("removing id: %v from collectionName: %v ", id.Hex(), collectionName)
	if err := helper.RemoveDocument(collectionName, id); err != nil {
		log.Error(
			"couldnt remove collectionId: %v from collectionName: %v  Error: %v",
			id.Hex(),
			collectionName,
			err,
		)
	}
}
<commit_msg>GuestCleanerWorker: remove unused lines and add documentation<commit_after>// This package is created for deleting the old guest accounts
package main

import (
	"flag"
	"koding/db/models"
	helper "koding/db/mongodb/modelhelper"
	"koding/helpers"
	"koding/tools/config"
	"koding/tools/logger"
	"time"

	"labix.org/v2/mgo/bson"
)

var log = logger.New("Guest cleaner worker")

var (
	configProfile = flag.String("c", "", "Configuration profile from file")
	flagSkip      = flag.Int("s", 0, "Configuration profile from file")
	flagLimit     = flag.Int("l", 1000, "Configuration profile from file")
)

func initialize() {
	flag.Parse()
	if *configProfile == "" {
		log.Fatal("Please define config file with -c")
	}

	conf := config.MustConfig(*configProfile)
	helper.Initialize(conf.Mongo)
}

func main() {
	// init the package
	initialize()

	log.Info("Guest cleaner worker started")

	iterOptions := helpers.NewIterOptions()
	iterOptions.CollectionName = "jAccounts"
	iterOptions.F = deleteGuestAccounts
	iterOptions.Filter = createFilter()
	iterOptions.DataType = &models.Account{}
	iterOptions.Limit = *flagLimit
	iterOptions.Skip = *flagSkip
	iterOptions.Log = log

	log.SetLevel(logger.DEBUG)

	err := helpers.Iter(helper.Mongo, iterOptions)
	if err != nil {
		log.Fatal("Error while iter: %v", err)
	}
	log.Info("Guest cleaner worker finished")
}

func createFilter() helper.Selector {
	oneHourAgo := time.Now().Add(-time.Minute * 60).UTC()
	return helper.Selector{
		"type":           "unregistered",
		"status":         helper.Selector{"$ne": "tobedeleted"},
		"meta.createdAt": helper.Selector{"$lte": oneHourAgo},
	}
}

// deleteGuestAccounts deletes all related documents and
// relationships for given account
func deleteGuestAccounts(account interface{}) {
	acc := account.(*models.Account)
	log.Info("Deleting account: %v", acc.Profile.Nickname)

	// clear all sessions for acc
	clearAllSessions(acc)

	// clear all JNames for current acc
	clearAllNames(acc)

	selector := helper.Selector{"$or": []helper.Selector{
		helper.Selector{"sourceId": acc.Id},
		helper.Selector{"targetId": acc.Id},
	}}

	// Get all relationships for current acc
	rels, err := helper.GetAllRelationships(selector)
	if err != nil {
		log.Error("Error while getting the all relationships for deletion %v", err)
		return
	}

	// Delete documents with all their relationships
	deleteDocumentsWithRelationships(rels)

	// now time to delete acc itself
	if err := helper.RemoveAccount(acc.Id); err != nil {
		log.Error("Error while deleting the acc %v", err)
		return
	}
}

// clearAllSessions deletes related JSesssions from database
func clearAllSessions(account *models.Account) {
	selector := helper.Selector{"username": account.Profile.Nickname}
	if err := helper.RemoveAllDocuments("jSessions", selector); err != nil {
		log.Error("Error while deleting from JSession %v", err)
	}
}

// clearAllNames deletes related documents from JName collection
func clearAllNames(account *models.Account) {
	selector := helper.Selector{"name": account.Profile.Nickname}
	if err := helper.RemoveAllDocuments("jName", selector); err != nil {
		log.Error("Error while deleting from JName %v", err)
	}
}

// deleteDocumentsWithRelationships accepts a slice of Relationship
// and range over it, for each relationship checks if source or the target
// is eligible for deletion, at the end deletes the relationship itself
func deleteDocumentsWithRelationships(rels []models.Relationship) {
	if len(rels) == 0 {
		log.Debug("no item to process")
		return
	}

	for _, rel := range rels {
		// delete source first
		if checkIfEligibleToDelete(rel.SourceName) {
			deleteDocument(helper.GetCollectionName(rel.SourceName), rel.SourceId)
		}
		// then delete target
		if checkIfEligibleToDelete(rel.TargetName) {
			deleteDocument(helper.GetCollectionName(rel.TargetName), rel.TargetId)
		}
		// delete relationship itself
		deleteDocument("relationships", rel.Id)
	}
}

// safe to delete these documents automatically
var whiteListedModels = []string{"JSession", "JUser", "JVM", "JDomain", "JAppStorage", "JName"}

//checkIfEligibleToDelete ranges over whiteListedModels and returns bool as checking
// name is in that list or not
func checkIfEligibleToDelete(modelName string) bool {
	for _, name := range whiteListedModels {
		if name == modelName {
			return true
		}
	}

	log.Debug("not eligible %v", modelName)
	return false
}

// deleteDocument deletes the given id from given collection
// this is here just for logging purposes
func deleteDocument(collectionName string, id bson.ObjectId) {
	log.Debug("removing id: %v from collectionName: %v ", id.Hex(), collectionName)
	if err := helper.RemoveDocument(collectionName, id); err != nil {
		log.Error(
			"couldnt remove collectionId: %v from collectionName: %v  Error: %v",
			id.Hex(),
			collectionName,
			err,
		)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"

	"code.google.com/p/gcfg"
)

const (
	// The file we read from
	CONFIG_FILE = "config.gcfg"
	// The example file kept in version control. We'll copy and load from this
	// by default.
	CONFIG_EXAMPLE = "config.example.gcfg"
)

type Configuration struct {
	Server struct {
		Address string
		Cache   string
		Logging string
		URL     string
		Ttl     int
	}

	Redis struct {
		Address  string
		Auth     string
		DB       int
		Prefix   string
		PoolSize int
	}
}

// Reads the configuration from the config file, copying a config into
// place from the example if one does not yet exist.
func (c *Configuration) load() error {
	err := c.ensureConfigExists()
	if err != nil {
		return err
	}

	return gcfg.ReadFileInto(c, CONFIG_FILE)
}

// Creates the config.json if it does not exist.
func (c *Configuration) ensureConfigExists() error {
	if _, err := os.Stat(CONFIG_FILE); os.IsNotExist(err) {
		return copyFile(CONFIG_EXAMPLE, CONFIG_FILE)
	} else {
		return nil
	}
}

// Copies *only the contents* of one file to a new path.
func copyFile(src string, dest string) error {
	original, err := os.Open(src)
	if err != nil {
		return err
	}
	defer original.Close()

	destination, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer destination.Close()

	// do the actual work
	_, err = io.Copy(destination, original)
	return err
}
<commit_msg>code.google.com is deprecated<commit_after>package main

import (
	"io"
	"os"

	"gopkg.in/gcfg.v1"
)

const (
	// The file we read from
	CONFIG_FILE = "config.gcfg"
	// The example file kept in version control. We'll copy and load from this
	// by default.
	CONFIG_EXAMPLE = "config.example.gcfg"
)

type Configuration struct {
	Server struct {
		Address string
		Cache   string
		Logging string
		URL     string
		Ttl     int
	}

	Redis struct {
		Address  string
		Auth     string
		DB       int
		Prefix   string
		PoolSize int
	}
}

// Reads the configuration from the config file, copying a config into
// place from the example if one does not yet exist.
func (c *Configuration) load() error {
	err := c.ensureConfigExists()
	if err != nil {
		return err
	}

	return gcfg.ReadFileInto(c, CONFIG_FILE)
}

// Creates the config.json if it does not exist.
func (c *Configuration) ensureConfigExists() error {
	if _, err := os.Stat(CONFIG_FILE); os.IsNotExist(err) {
		return copyFile(CONFIG_EXAMPLE, CONFIG_FILE)
	} else {
		return nil
	}
}

// Copies *only the contents* of one file to a new path.
func copyFile(src string, dest string) error {
	original, err := os.Open(src)
	if err != nil {
		return err
	}
	defer original.Close()

	destination, err := os.Create(dest)
	if err != nil {
		return err
	}
	defer destination.Close()

	// do the actual work
	_, err = io.Copy(destination, original)
	return err
}
<|endoftext|>


<commit_before>package config

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
)

// A ParseError is returned for parsing errors.
// The first line is 1. The first column is 0.
type ParseError struct {
	Line   int   // Line where the error occurred
	Column int   // Column (rune index) where the error occurred
	Err    error // The actual error
}

func (e *ParseError) Error() string {
	return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Column, e.Err)
}

// These are the errors that can be returned in ParseError.Error
var (
	ErrParse              = errors.New("generic parse error")
	ErrEmptySectionHeader = errors.New("empty section header")
)

type Reader struct {
	r              *bufio.Reader
	field          bytes.Buffer
	line           int
	column         int
	currentSection string
}

// NewReader returns a new Reader that reads from r.
func NewReader(r io.Reader) *Reader {
	return &Reader{
		r:              bufio.NewReader(r),
		currentSection: "default",
	}
}

// error creates a new ParseError based on err.
func (r *Reader) error(err error) error {
	return &ParseError{
		Line:   r.line,
		Column: r.column,
		Err:    err,
	}
}

func (r *Reader) readRune() (rune, error) {
	r1, _, err := r.r.ReadRune()

	// Handle \r\n here.  We make the simplifying assumption that
	// anytime \r is followed by \n that it can be folded to \n.
	// We will not detect text which contains both \r\n and bare \n.
	if r1 == '\r' {
		r1, _, err = r.r.ReadRune()
		if err == nil {
			if r1 != '\n' {
				r.r.UnreadRune()
				r1 = '\r'
			}
		}
	}
	r.column++
	return r1, err
}

// unreadRune puts the last rune read from r back.
func (r *Reader) unreadRune() {
	r.r.UnreadRune()
	r.column--
}

func (r *Reader) ReadAll() (sections map[string]map[string]string, err error) {
	sections = make(map[string]map[string]string)
	for {
		r.line++
		r.column = 0
		r1, err := r.readRune()
		if err == io.EOF {
			return sections, nil
		} else if err != nil {
			return nil, err
		}

		switch r1 {
		case '#', ';':
			r.skip('\n')
		case '[':
			section, err := r.parseHeader()
			if err != nil {
				return nil, err
			}
			if _, ok := sections[section]; !ok {
				sections[section] = make(map[string]string)
			}
			r.currentSection = section
		default:
			r.unreadRune()
			key, value, err := r.parseOption()
			if err != nil {
				return nil, err
			}
			key = strings.TrimSpace(key)

			if len(key) != 0 {
				if r.currentSection == "default" {
					if _, ok := sections["default"]; !ok {
						sections["default"] = make(map[string]string)
					}
				}
				sections[r.currentSection][key] = value
			}
		}
	}
	panic("unreachable")
}

func (r *Reader) skip(delim rune) error {
	for {
		r1, err := r.readRune()
		if err != nil {
			return err
		}
		if r1 == delim {
			return nil
		}
	}
	panic("unreachable")
}

func (r *Reader) parseHeader() (section string, err error) {
	r.field.Reset()
	for {
		r1, err := r.readRune()
		if err != nil {
			return section, err
		}

		switch r1 {
		case '#', ';':
			return section, r.error(ErrParse)
		case ']':
			section = r.field.String()
			if section == "" {
				return section, r.error(ErrEmptySectionHeader)
			}
			err = r.skip('\n')
			if err == nil || err == io.EOF {
				return section, nil
			} else {
				return section, err
			}
		default:
			r.field.WriteRune(r1)
		}
	}
	panic("unreachable")
}

func (r *Reader) parseOption() (key string, value string, err error) {
	r.field.Reset()
	var (
		lastRune   rune
		foundDelim bool
	)
	for {
		r1, err := r.readRune()
		if err == io.EOF {
			value = r.field.String()
			return key, value, nil
		}
		if err != nil {
			return key, value, err
		}

		if (lastRune == 0 || lastRune == ' ') && (r1 == '#' || r1 == ';') {
			value = r.field.String()
			return key, value[:len(value)-1], r.skip('\n')
		}

		switch r1 {
		case '=', ':':
			if !foundDelim {
				key = r.field.String()
				r.skip(' ')
				foundDelim = true
				r.field.Reset()
			} else {
				r.field.WriteRune(r1)
				lastRune = r1
			}
		case '\n':
			value = r.field.String()
			return key, value, nil
		default:
			r.field.WriteRune(r1)
			lastRune = r1
		}
	}
	panic("unreachable")
}
<commit_msg>Added package documentation<commit_after>// Package config reads INI configuration files.
//
// A configaration file contains zero or more sections of zero or more options
// per section. Each section is separated by the newline character. Options are
// also separated by the newline character.
//
// Carriage returns before newline characters are silently removed.
//
// Blank lines and lines with only whitespace characters are ignored. Lines
// beginning with comment characters ('#' and ';') are also ignored.
//
// Each section must have a header wrapped in square brackets. Any option
// appearing on the next line after a section header will belong to this section.
//
// An options consists of a name and a value separated with ':' or '=' characters.
// Leading and trailing spaces will be trimmed from options names. There might be
// options without a value.

package config

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
)

// A ParseError is returned for parsing errors.
// The first line is 1. The first column is 1.
type ParseError struct {
	Line   int   // Line where the error occurred
	Column int   // Column (rune index) where the error occurred
	Err    error // The actual error
}

func (e *ParseError) Error() string {
	return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Column, e.Err)
}

// These are the errors that can be returned in ParseError.Error.
var (
	ErrParse              = errors.New("generic parse error")
	ErrEmptySectionHeader = errors.New("empty section header")
)

// A Reader reads sections of options from a configation file.
type Reader struct {
	r              *bufio.Reader
	field          bytes.Buffer
	line           int
	column         int
	currentSection string
}

// NewReader returns a new Reader that reads from r.
func NewReader(r io.Reader) *Reader {
	return &Reader{
		r:              bufio.NewReader(r),
		currentSection: "default",
	}
}

// error creates a new ParseError based on err.
func (r *Reader) error(err error) error {
	return &ParseError{
		Line:   r.line,
		Column: r.column,
		Err:    err,
	}
}

// readRune reads one rune from r, folding \r\n to \n and keeping track
// of how far into the line we have read. r.column will point to the start
// of this rune, not the end of this rune.
func (r *Reader) readRune() (rune, error) {
	r1, _, err := r.r.ReadRune()

	// Handle \r\n here.  We make the simplifying assumption that
	// anytime \r is followed by \n that it can be folded to \n.
	// We will not detect text which contains both \r\n and bare \n.
	if r1 == '\r' {
		r1, _, err = r.r.ReadRune()
		if err == nil {
			if r1 != '\n' {
				r.r.UnreadRune()
				r1 = '\r'
			}
		}
	}
	r.column++
	return r1, err
}

// unreadRune puts the last rune read from r back.
func (r *Reader) unreadRune() {
	r.r.UnreadRune()
	r.column--
}

// ReadAll reads all the sections from r.
// Each section is a map.
// A successful call returns err == nil, not err == EOF. Because ReadAll is
// defined to read until EOF, it does not treat end of file as an error to be
// reported.
func (r *Reader) ReadAll() (sections map[string]map[string]string, err error) {
	sections = make(map[string]map[string]string)
	for {
		r.line++
		r.column = 0
		r1, err := r.readRune()
		if err == io.EOF {
			return sections, nil
		} else if err != nil {
			return nil, err
		}

		switch r1 {
		case '#', ';':
			r.skip('\n')
		case '[':
			section, err := r.parseHeader()
			if err != nil {
				return nil, err
			}
			if _, ok := sections[section]; !ok {
				sections[section] = make(map[string]string)
			}
			r.currentSection = section
		default:
			r.unreadRune()
			key, value, err := r.parseOption()
			if err != nil {
				return nil, err
			}
			key = strings.TrimSpace(key)

			if len(key) != 0 {
				if r.currentSection == "default" {
					if _, ok := sections["default"]; !ok {
						sections["default"] = make(map[string]string)
					}
				}
				sections[r.currentSection][key] = value
			}
		}
	}
	panic("unreachable")
}

// skip reads runes up to and including the rune delim or until error.
func (r *Reader) skip(delim rune) error {
	for {
		r1, err := r.readRune()
		if err != nil {
			return err
		}
		if r1 == delim {
			return nil
		}
	}
	panic("unreachable")
}

func (r *Reader) parseHeader() (section string, err error) {
	r.field.Reset()
	for {
		r1, err := r.readRune()
		if err != nil {
			return section, err
		}

		switch r1 {
		case '#', ';':
			return section, r.error(ErrParse)
		case ']':
			section = r.field.String()
			if section == "" {
				return section, r.error(ErrEmptySectionHeader)
			}
			err = r.skip('\n')
			if err == nil || err == io.EOF {
				return section, nil
			} else {
				return section, err
			}
		default:
			r.field.WriteRune(r1)
		}
	}
	panic("unreachable")
}

func (r *Reader) parseOption() (key string, value string, err error) {
	r.field.Reset()
	var (
		lastRune   rune
		foundDelim bool
	)
	for {
		r1, err := r.readRune()
		if err == io.EOF {
			value = r.field.String()
			return key, value, nil
		}
		if err != nil {
			return key, value, err
		}

		if (lastRune == 0 || lastRune == ' ') && (r1 == '#' || r1 == ';') {
			value = r.field.String()
			return key, value[:len(value)-1], r.skip('\n')
		}

		switch r1 {
		case '=', ':':
			if !foundDelim {
				key = r.field.String()
				r.skip(' ')
				foundDelim = true
				r.field.Reset()
			} else {
				r.field.WriteRune(r1)
				lastRune = r1
			}
		case '\n':
			value = r.field.String()
			return key, value, nil
		default:
			r.field.WriteRune(r1)
			lastRune = r1
		}
	}
	panic("unreachable")
}
<|endoftext|>