<commit_before>// Copyright 2013

/*
EVE Online API downloader/cacher.

Begin by initializing the default client.
	NewClient(NilCache)

Create a new request for the API page needed.
	req := NewRequest("eve/ConquerableStationList.xml.aspx")

Set any options you may need, including keyid/vcode.
	req.Set("keyid", "1234")
	req.Set("vcode", "abcd")
	req.Set("charactername", "innominate")
	req.Set("characterid", fmt.Sprintf("%d", 123))

Get your response.
	resp, err := req.Do()
	xml := resp.Data
*/
package apicache

import (
	"crypto/rand"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"
)

const sqlDateTime = "2006-01-02 15:04:05"

var DebugLog = log.New(ioutil.Discard, "apicache", log.Ldate|log.Ltime)

//
//  API Error Type, hopefully contains the error code and some useful information.
//  This is CCP so nothing is guaranteed.
//
type APIError struct {
	ErrorCode int    `xml:"code,attr"`
	ErrorText string `xml:",innerxml"`
}

// Error String Generator
func (e APIError) Error() string {
	return fmt.Sprintf("API Error %d: %s", e.ErrorCode, e.ErrorText)
}

// API Client structure.  Must be created using NewClient() function.
type Client struct {
	// Base URL, defaults to CCPs api but can be changed to a proxy
	BaseURL string

	// Default three retries, can be changed at will.
	Retries int

	timeout      time.Duration
	maxIdleConns int

	cacher     Cacher
	httpClient *http.Client

	panicUntil  time.Time
	panicCode   int
	panicReason string

	sync.RWMutex
}

// Default client, first client created lives here as well.
var client *Client

// Default BaseURL for new clients, change as necessary.  Must contain
// trailing slash or bad things happen.
var DefaultBaseURL = "https://api.eveonline.com/"

// Return the default client for
func GetDefaultClient() *Client {
	if client == nil {
		panic("Tried to get nonexistent client, must be initialized first.")
	}

	return client
}

// Create a new API Client.  The first time this is called will become
// the default client.  Requires a cacher.
func NewClient(cacher Cacher) *Client {
	var newClient Client

	newClient.BaseURL = DefaultBaseURL
	newClient.Retries = 5
	newClient.cacher = cacher
	newClient.maxIdleConns = 2

	// Also sets up our initial http client
	newClient.SetTimeout(60 * time.Second)

	if client == nil {
		client = &newClient
	}
	return &newClient
}

func (c *Client) SetMaxIdleConns(maxIdleConns int) {
	// Enforce some sanity.
	if maxIdleConns <= 0 || maxIdleConns >= 64 {
		maxIdleConns = 2
	}
	c.maxIdleConns = maxIdleConns
	c.newHttpClient()
}

// Set timeout for each API request.
func (c *Client) SetTimeout(timeout time.Duration) {
	if timeout.Seconds() <= 0 || timeout.Seconds() > 3600 {
		timeout = 60 * time.Second
	}

	c.timeout = timeout
	c.newHttpClient()
}

// Set max idle conns for the default client
func SetMaxIdleConns(maxIdleConns int) {
	client.SetMaxIdleConns(maxIdleConns)
}

// Set timeout for default client.
func SetTimeout(timeout time.Duration) {
	client.SetTimeout(timeout)
}

func (c *Client) newHttpClient() {
	c.httpClient = &http.Client{
		Transport: &http.Transport{
			Dial: func(netw, addr string) (net.Conn, error) {
				c, err := net.DialTimeout(netw, addr, c.timeout)
				if err != nil {
					return nil, err
				}
				//c.SetDeadline(deadline)
				return c, nil
			},
			ResponseHeaderTimeout: c.timeout,
			MaxIdleConnsPerHost:   c.maxIdleConns,
		},
	}
}

// Create a new request.
func (c *Client) NewRequest(url string) *Request {
	var r Request
	r.params = make(map[string]string)

	if url[0] == '/' {
		url = url[1:]
	}
	r.url = url
	r.client = c

	return &r
}

// Create a new request using default client.
func NewRequest(url string) *Request {
	return client.NewRequest(url)
}

var (
	ErrCannotConnect = fmt.Errorf("Error connecting to API.")
	ErrNetwork       = fmt.Errorf("Network error.")
	ErrHTTP          = fmt.Errorf("HTTP error.")
	ErrForbidden     = fmt.Errorf("HTTP Forbidden, invalid API provided.")
	ErrUnknown       = fmt.Errorf("Unknown Error.")
	ErrXML           = fmt.Errorf("Malformed XML Detected")
	ErrTime          = fmt.Errorf("Malformed cache directive.")
)

type cacheResp struct {
	Error       APIError `xml:"error"`
	CachedUntil string   `xml:"cachedUntil"`
}

// Data structure returned from the API.
type Response struct {
	// Raw XML data
	Data []byte

	// Signals if we used the cache or not
	FromCache bool

	// Data expiration time
	Expires time.Time

	// true if an error occured due to invalid API rather than server problems
	Invalidate bool

	// Contains API Error if one occured
	Error APIError

	//Pass on CCP's HTTP code because why not?
	HTTPCode int
}

func (c *Client) GetCached(r *Request) (retresp *Response, reterr error) {
	resp := &Response{}

	// Check for cached version
	cacheTag := r.cacheTag()
	httpCode, data, expires, err := c.cacher.Get(cacheTag)
	if err == nil && !r.Force && !r.NoCache {
		resp.Data = data
		resp.FromCache = true
		resp.Expires = expires
		resp.HTTPCode = httpCode

		var cR cacheResp
		err = xml.Unmarshal(data, &cR)
		if err != nil {
			DebugLog.Printf("XML Error: %s", err)
			return resp, ErrXML
		}

		// Handle extended expiration requests.
		if r.Expires.After(expires) {
			resp.Expires = r.Expires
		} else {
			resp.Expires = expires
		}

		// Pass on any API errors
		resp.Error = cR.Error

		return resp, nil
	}
	return nil, err
}

func MakeID() string {
	buf := make([]byte, 5)
	io.ReadFull(rand.Reader, buf)
	return fmt.Sprintf("%x", buf)
}

// Perform a request, usually called by the request itself.
// User friendly error is enclosed in the response, returned error should be
// for internal use only.
func (c *Client) Do(r *Request) (retresp *Response, reterr error) {
	var data []byte
	var expires time.Time

	cacheTag := r.cacheTag()

	// Check for cached version
	resp, err := c.GetCached(r)
	if err == nil {
		return resp, nil
	}

	// If we're panicking, bail out early and spit back a fake error
	c.RLock()
	if c.panicUntil.After(time.Now()) {
		DebugLog.Printf("Got Request, but we're currently panicing until %s", c.panicUntil.Format(sqlDateTime))
		data := SynthesizeAPIError(c.panicCode, c.panicReason, c.panicUntil.Sub(time.Now()))
		c.RUnlock()

		resp.Data = data
		resp.FromCache = true
		resp.Expires = c.panicUntil
		resp.HTTPCode = 418
		resp.Error = APIError{c.panicCode, c.panicReason}

		return resp, nil
	}
	c.RUnlock()

	// Build parameter list
	formValues := make(url.Values)
	for k, v := range r.params {
		formValues.Set(k, v)
	}

	// Use defer to cache so we can synthesize error pages if necessary
	defer func() {
		if reterr != nil {
			resp.HTTPCode = 504
			resp.Data = SynthesizeAPIError(500, "APIProxy Error: "+reterr.Error(), 5*time.Minute)
		} else if resp.Data == nil {
			resp.HTTPCode = 504
			resp.Data = SynthesizeAPIError(900, "This shouldn't happen.", 15*time.Minute)
		}
		if !r.NoCache {
			err := c.cacher.Store(cacheTag, resp.HTTPCode, resp.Data, resp.Expires)
			if err != nil {
				log.Printf("Cache Error: %s", err)
			}
		}
	}()

	//Post the shit, retry if necessary.

	tries := 0
	var httpResp *http.Response
	for tries < c.Retries {
		tries++

		httpResp, err = c.httpClient.PostForm(c.BaseURL+r.url, formValues)
		if err != nil {
			DebugLog.Printf("Error Connecting to API, retrying: %s", err)
			time.Sleep(3 * time.Second)
			continue
		}
		defer httpResp.Body.Close()

		resp.HTTPCode = httpResp.StatusCode

		// We're going to do this asynchronously so we can time it out, AAAAAAA
		type ioRead struct {
			body []byte
			err  error
		}

		readBodyChan := make(chan ioRead)
		go func() {
			bytes, err := ioutil.ReadAll(httpResp.Body)
			readBodyChan <- ioRead{bytes, err}
			close(readBodyChan)
		}()

		select {
		case readBody := <-readBodyChan:
			err = readBody.err
			data = readBody.body
		case <-time.After(c.timeout):
			data = nil
			err = fmt.Errorf("read timed out after %f seconds", c.timeout.Seconds())

			// if ioutil ever does come back, let's handle it.
			go func() {
				id := MakeID()
				DebugLog.Printf("zombie body read %s: %s ? %s", id, r.url, formValues)
				rb := <-readBodyChan
				DebugLog.Printf("zombie read completed %s: %s - %s ? %s\n%s", id, rb.err, r.url, formValues, rb.body)
			}()
		}
		if err != nil {
			DebugLog.Printf("Error Reading from API(%s), retrying...", err)
			time.Sleep(3 * time.Second)
			continue
		}

		break
		log.Printf("WARNING MAJOR REGRESSION: This should NEVER appear.")
	}
	if err != nil {
		DebugLog.Printf("Failed to access api, giving up: %s - %#v", c.BaseURL+r.url, formValues)
		return resp, ErrNetwork
	}

	//data = SynthesizeAPIError(904, "Your IP address has been temporarily blocked because it is causing too many errors. See the cacheUntil timestamp for when it will be opened again. IPs that continuall    y cause a lot of errors in the API will be permanently banned, please take measures to minimize problematic API calls from your application.", time.Second*30)

	// Get cache directive, bail with an error if anything is wrong with XML or
	// time format.  If these produce an error the rest of the data should be
	// considered worthless.
	var cR cacheResp
	err = xml.Unmarshal(data, &cR)
	if err != nil {
		DebugLog.Printf("XML Error: %s", err)
		return resp, ErrXML
	}

	// Get expiration
	expires, err = time.Parse(sqlDateTime, cR.CachedUntil)
	if err != nil {
		return resp, ErrTime
	}

	// Handle extended expiration requests.
	if r.Expires.After(expires) {
		resp.Expires = r.Expires
	} else {
		resp.Expires = expires
	}

	// Pass on any API errors
	resp.Error = cR.Error

	code := cR.Error.ErrorCode
	if code >= 901 && code <= 905 {
		log.Printf("Major API Error: %d - %s for %s %+v", cR.Error.ErrorCode, cR.Error.ErrorText, r.url, r.params)
		log.Printf("Pausing all API actions until %s...", resp.Expires.Format(sqlDateTime))
		c.Lock()
		c.panicUntil = resp.Expires
		c.panicCode = code
		c.panicReason = cR.Error.ErrorText
		c.Unlock()
	}
	if resp.HTTPCode == 403 || (code >= 100 && code <= 299) {
		resp.Invalidate = true
	}

	resp.Data = data
	return resp, nil
}
<commit_msg>bogus response isn't bogus!<commit_after>// Copyright 2013

/*
EVE Online API downloader/cacher.

Begin by initializing the default client.
	NewClient(NilCache)

Create a new request for the API page needed.
	req := NewRequest("eve/ConquerableStationList.xml.aspx")

Set any options you may need, including keyid/vcode.
	req.Set("keyid", "1234")
	req.Set("vcode", "abcd")
	req.Set("charactername", "innominate")
	req.Set("characterid", fmt.Sprintf("%d", 123))

Get your response.
	resp, err := req.Do()
	xml := resp.Data
*/
package apicache

import (
	"crypto/rand"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"
)

const sqlDateTime = "2006-01-02 15:04:05"

var DebugLog = log.New(ioutil.Discard, "apicache", log.Ldate|log.Ltime)

//
//  API Error Type, hopefully contains the error code and some useful information.
//  This is CCP so nothing is guaranteed.
//
type APIError struct {
	ErrorCode int    `xml:"code,attr"`
	ErrorText string `xml:",innerxml"`
}

// Error String Generator
func (e APIError) Error() string {
	return fmt.Sprintf("API Error %d: %s", e.ErrorCode, e.ErrorText)
}

// API Client structure.  Must be created using NewClient() function.
type Client struct {
	// Base URL, defaults to CCPs api but can be changed to a proxy
	BaseURL string

	// Default three retries, can be changed at will.
	Retries int

	timeout      time.Duration
	maxIdleConns int

	cacher     Cacher
	httpClient *http.Client

	panicUntil  time.Time
	panicCode   int
	panicReason string

	sync.RWMutex
}

// Default client, first client created lives here as well.
var client *Client

// Default BaseURL for new clients, change as necessary.  Must contain
// trailing slash or bad things happen.
var DefaultBaseURL = "https://api.eveonline.com/"

// Return the default client for
func GetDefaultClient() *Client {
	if client == nil {
		panic("Tried to get nonexistent client, must be initialized first.")
	}

	return client
}

// Create a new API Client.  The first time this is called will become
// the default client.  Requires a cacher.
func NewClient(cacher Cacher) *Client {
	var newClient Client

	newClient.BaseURL = DefaultBaseURL
	newClient.Retries = 5
	newClient.cacher = cacher
	newClient.maxIdleConns = 2

	// Also sets up our initial http client
	newClient.SetTimeout(60 * time.Second)

	if client == nil {
		client = &newClient
	}
	return &newClient
}

func (c *Client) SetMaxIdleConns(maxIdleConns int) {
	// Enforce some sanity.
	if maxIdleConns <= 0 || maxIdleConns >= 64 {
		maxIdleConns = 2
	}
	c.maxIdleConns = maxIdleConns
	c.newHttpClient()
}

// Set timeout for each API request.
func (c *Client) SetTimeout(timeout time.Duration) {
	if timeout.Seconds() <= 0 || timeout.Seconds() > 3600 {
		timeout = 60 * time.Second
	}

	c.timeout = timeout
	c.newHttpClient()
}

// Set max idle conns for the default client
func SetMaxIdleConns(maxIdleConns int) {
	client.SetMaxIdleConns(maxIdleConns)
}

// Set timeout for default client.
func SetTimeout(timeout time.Duration) {
	client.SetTimeout(timeout)
}

func (c *Client) newHttpClient() {
	c.httpClient = &http.Client{
		Transport: &http.Transport{
			Dial: func(netw, addr string) (net.Conn, error) {
				c, err := net.DialTimeout(netw, addr, c.timeout)
				if err != nil {
					return nil, err
				}
				//c.SetDeadline(deadline)
				return c, nil
			},
			ResponseHeaderTimeout: c.timeout,
			MaxIdleConnsPerHost:   c.maxIdleConns,
		},
	}
}

// Create a new request.
func (c *Client) NewRequest(url string) *Request {
	var r Request
	r.params = make(map[string]string)

	if url[0] == '/' {
		url = url[1:]
	}
	r.url = url
	r.client = c

	return &r
}

// Create a new request using default client.
func NewRequest(url string) *Request {
	return client.NewRequest(url)
}

var (
	ErrCannotConnect = fmt.Errorf("Error connecting to API.")
	ErrNetwork       = fmt.Errorf("Network error.")
	ErrHTTP          = fmt.Errorf("HTTP error.")
	ErrForbidden     = fmt.Errorf("HTTP Forbidden, invalid API provided.")
	ErrUnknown       = fmt.Errorf("Unknown Error.")
	ErrXML           = fmt.Errorf("Malformed XML Detected")
	ErrTime          = fmt.Errorf("Malformed cache directive.")
)

type cacheResp struct {
	Error       APIError `xml:"error"`
	CachedUntil string   `xml:"cachedUntil"`
}

// Data structure returned from the API.
type Response struct {
	// Raw XML data
	Data []byte

	// Signals if we used the cache or not
	FromCache bool

	// Data expiration time
	Expires time.Time

	// true if an error occured due to invalid API rather than server problems
	Invalidate bool

	// Contains API Error if one occured
	Error APIError

	//Pass on CCP's HTTP code because why not?
	HTTPCode int
}

func (c *Client) GetCached(r *Request) (retresp *Response, reterr error) {
	resp := &Response{}
	if r.Force {
		return resp, errors.New("cache bypass forced")
	}

	// Check for cached version
	cacheTag := r.cacheTag()
	httpCode, data, expires, err := c.cacher.Get(cacheTag)
	if err == nil && !r.Force {
		resp.Data = data
		resp.FromCache = true
		resp.Expires = expires
		resp.HTTPCode = httpCode

		var cR cacheResp
		err = xml.Unmarshal(data, &cR)
		if err != nil {
			DebugLog.Printf("XML Error: %s", err)
			return resp, ErrXML
		}

		// Handle extended expiration requests.
		if r.Expires.After(expires) {
			resp.Expires = r.Expires
		} else {
			resp.Expires = expires
		}

		// Pass on any API errors
		resp.Error = cR.Error

		return resp, nil
	}
	return resp, err
}

func MakeID() string {
	buf := make([]byte, 5)
	io.ReadFull(rand.Reader, buf)
	return fmt.Sprintf("%x", buf)
}

// Perform a request, usually called by the request itself.
// User friendly error is enclosed in the response, returned error should be
// for internal use only.
func (c *Client) Do(r *Request) (retresp *Response, reterr error) {
	var data []byte
	var expires time.Time

	cacheTag := r.cacheTag()

	// Check for cached version
	resp, err := c.GetCached(r)
	if err == nil {
		return resp, nil
	}

	// If we're panicking, bail out early and spit back a fake error
	c.RLock()
	if c.panicUntil.After(time.Now()) {
		DebugLog.Printf("Got Request, but we're currently panicing until %s", c.panicUntil.Format(sqlDateTime))
		data := SynthesizeAPIError(c.panicCode, c.panicReason, c.panicUntil.Sub(time.Now()))
		c.RUnlock()

		resp.Data = data
		resp.FromCache = true
		resp.Expires = c.panicUntil
		resp.HTTPCode = 418
		resp.Error = APIError{c.panicCode, c.panicReason}

		return resp, nil
	}
	c.RUnlock()

	// Build parameter list
	formValues := make(url.Values)
	for k, v := range r.params {
		formValues.Set(k, v)
	}

	// Use defer to cache so we can synthesize error pages if necessary
	defer func() {
		if reterr != nil {
			resp.HTTPCode = 504
			resp.Data = SynthesizeAPIError(500, "APIProxy Error: "+reterr.Error(), 5*time.Minute)
		} else if resp.Data == nil {
			resp.HTTPCode = 504
			resp.Data = SynthesizeAPIError(900, "This shouldn't happen.", 15*time.Minute)
		}
		if !r.NoCache {
			err := c.cacher.Store(cacheTag, resp.HTTPCode, resp.Data, resp.Expires)
			if err != nil {
				log.Printf("Cache Error: %s", err)
			}
		}
	}()

	//Post the shit, retry if necessary.

	tries := 0
	var httpResp *http.Response
	for tries < c.Retries {
		tries++

		httpResp, err = c.httpClient.PostForm(c.BaseURL+r.url, formValues)
		if err != nil {
			DebugLog.Printf("Error Connecting to API, retrying: %s", err)
			time.Sleep(3 * time.Second)
			continue
		}
		defer httpResp.Body.Close()

		resp.HTTPCode = httpResp.StatusCode

		// We're going to do this asynchronously so we can time it out, AAAAAAA
		type ioRead struct {
			body []byte
			err  error
		}

		readBodyChan := make(chan ioRead)
		go func() {
			bytes, err := ioutil.ReadAll(httpResp.Body)
			readBodyChan <- ioRead{bytes, err}
			close(readBodyChan)
		}()

		select {
		case readBody := <-readBodyChan:
			err = readBody.err
			data = readBody.body
		case <-time.After(c.timeout):
			data = nil
			err = fmt.Errorf("read timed out after %f seconds", c.timeout.Seconds())

			// if ioutil ever does come back, let's handle it.
			go func() {
				id := MakeID()
				DebugLog.Printf("zombie body read %s: %s ? %s", id, r.url, formValues)
				rb := <-readBodyChan
				DebugLog.Printf("zombie read completed %s: %s - %s ? %s\n%s", id, rb.err, r.url, formValues, rb.body)
			}()
		}
		if err != nil {
			DebugLog.Printf("Error Reading from API(%s), retrying...", err)
			time.Sleep(3 * time.Second)
			continue
		}

		break
		log.Printf("WARNING MAJOR REGRESSION: This should NEVER appear.")
	}
	if err != nil {
		DebugLog.Printf("Failed to access api, giving up: %s - %#v", c.BaseURL+r.url, formValues)
		return resp, ErrNetwork
	}

	//data = SynthesizeAPIError(904, "Your IP address has been temporarily blocked because it is causing too many errors. See the cacheUntil timestamp for when it will be opened again. IPs that continuall    y cause a lot of errors in the API will be permanently banned, please take measures to minimize problematic API calls from your application.", time.Second*30)

	// Get cache directive, bail with an error if anything is wrong with XML or
	// time format.  If these produce an error the rest of the data should be
	// considered worthless.
	var cR cacheResp
	err = xml.Unmarshal(data, &cR)
	if err != nil {
		DebugLog.Printf("XML Error: %s", err)
		return resp, ErrXML
	}

	// Get expiration
	expires, err = time.Parse(sqlDateTime, cR.CachedUntil)
	if err != nil {
		return resp, ErrTime
	}

	// Handle extended expiration requests.
	if r.Expires.After(expires) {
		resp.Expires = r.Expires
	} else {
		resp.Expires = expires
	}

	// Pass on any API errors
	resp.Error = cR.Error

	code := cR.Error.ErrorCode
	if code >= 901 && code <= 905 {
		log.Printf("Major API Error: %d - %s for %s %+v", cR.Error.ErrorCode, cR.Error.ErrorText, r.url, r.params)
		log.Printf("Pausing all API actions until %s...", resp.Expires.Format(sqlDateTime))
		c.Lock()
		c.panicUntil = resp.Expires
		c.panicCode = code
		c.panicReason = cR.Error.ErrorText
		c.Unlock()
	}
	if resp.HTTPCode == 403 || (code >= 100 && code <= 299) {
		resp.Invalidate = true
	}

	resp.Data = data
	return resp, nil
}
<|endoftext|>


<commit_before>//
//
//
//

package gdbm

// #cgo CFLAGS: -std=gnu99
// #cgo LDFLAGS: -lgdbm
// #include <stdlib.h>
// #include <gdbm.h>
// #include <string.h>
// inline datum mk_datum(char * s) {
//     datum d;
//     d.dptr = s;
//     d.dsize = strlen(s);
//     return d;
// }
import "C"

import (
    "errors"
    "unsafe"
)

//
type Database struct {
    dbf C.GDBM_FILE
    cfg DatabaseCfg
}

//
type DatabaseCfg struct {
    Mode string
    BlockSize int
    Permissions int
}

func lastError() error {
    return errors.New(C.GoString(C.gdbm_strerror(C.gdbm_errno)))
}

// Simple function to open a database file with default parameters (block size
// is default for the filesystem and file permissions are set to 0666).
func Open(filename string, mode string) (db * Database, err error) {
    return OpenWithCfg(filename, DatabaseCfg{mode, 0, 0666})
}

func OpenWithCfg(filename string, cfg DatabaseCfg) (db * Database, err error) {
    var m int
    switch cfg.Mode {
    case "r": m = C.GDBM_READER
    case "w": m = C.GDBM_WRITER
    case "c": m = C.GDBM_WRCREAT
    case "n": m = C.GDBM_NEWDB
    }

    cs := C.CString(filename)
    defer C.free(unsafe.Pointer(cs))

    db.cfg = cfg
    db.dbf = C.gdbm_open(cs, C.int(cfg.BlockSize), C.int(m), C.int(cfg.Permissions), nil)
    if db.dbf == nil {
        err = lastError()
    }
    return db, err
}

// Closes a database file.
func (db * Database) Close() {
    C.gdbm_close(db.dbf)
}

//
func (db * Database) update(key string, value string, flag C.int) (err error) {
    kcs := C.CString(key)
    vcs := C.CString(value)
    k := C.mk_datum(kcs)
    v := C.mk_datum(vcs)
    defer C.free(unsafe.Pointer(kcs))
    defer C.free(unsafe.Pointer(vcs))

    retv := C.gdbm_store(db.dbf, k, v, flag)
    if retv != 0 {
        err = lastError()
    }
    return err
}

//
func (db * Database) Insert(key string, value string) (err error) {
    return db.update(key, value, C.GDBM_INSERT)
}

//
func (db * Database) Replace(key string, value string) (err error) {
    return db.update(key, value, C.GDBM_REPLACE)
}

func (db * Database) Exists(key string) bool {
    kcs := C.CString(key)
    k := C.mk_datum(kcs)
    defer C.free(unsafe.Pointer(kcs))

    e := C.gdbm_exists(db.dbf, k)
    if e == 1 {
        return true
    }
    return false
}

//
func (db * Database) Fetch() {}

//
func (db * Database) Delete(key string) (err error) {
    kcs := C.CString(key)
    k := C.mk_datum(kcs)
    defer C.free(unsafe.Pointer(kcs))

    retv := C.gdbm_delete(db.dbf, k)
    if retv == -1 && db.cfg.Mode == "r" {
        err = lastError()
    }
    return err
}

//
func (db * Database) Reorganize() {
    C.gdbm_reorganize(db.dbf)
}

//
func (db * Database) Sync() {
    C.gdbm_sync(db.dbf)
}
<commit_msg>Add Fetch()<commit_after>//
//
//
//

package gdbm

// #cgo CFLAGS: -std=gnu99
// #cgo LDFLAGS: -lgdbm
// #include <stdlib.h>
// #include <gdbm.h>
// #include <string.h>
// inline datum mk_datum(char * s) {
//     datum d;
//     d.dptr = s;
//     d.dsize = strlen(s);
//     return d;
// }
import "C"

import (
    "errors"
    "unsafe"
)

//
type Database struct {
    dbf C.GDBM_FILE
    cfg DatabaseCfg
}

//
type DatabaseCfg struct {
    Mode string
    BlockSize int
    Permissions int
}

func lastError() error {
    return errors.New(C.GoString(C.gdbm_strerror(C.gdbm_errno)))
}

// Simple function to open a database file with default parameters (block size
// is default for the filesystem and file permissions are set to 0666).
func Open(filename string, mode string) (db * Database, err error) {
    return OpenWithCfg(filename, DatabaseCfg{mode, 0, 0666})
}

func OpenWithCfg(filename string, cfg DatabaseCfg) (db * Database, err error) {
    var m int
    switch cfg.Mode {
    case "r": m = C.GDBM_READER
    case "w": m = C.GDBM_WRITER
    case "c": m = C.GDBM_WRCREAT
    case "n": m = C.GDBM_NEWDB
    }

    cs := C.CString(filename)
    defer C.free(unsafe.Pointer(cs))

    db.cfg = cfg
    db.dbf = C.gdbm_open(cs, C.int(cfg.BlockSize), C.int(m), C.int(cfg.Permissions), nil)
    if db.dbf == nil {
        err = lastError()
    }
    return db, err
}

// Closes a database file.
func (db * Database) Close() {
    C.gdbm_close(db.dbf)
}

//
func (db * Database) update(key string, value string, flag C.int) (err error) {
    kcs := C.CString(key)
    vcs := C.CString(value)
    k := C.mk_datum(kcs)
    v := C.mk_datum(vcs)
    defer C.free(unsafe.Pointer(kcs))
    defer C.free(unsafe.Pointer(vcs))

    retv := C.gdbm_store(db.dbf, k, v, flag)
    if retv != 0 {
        err = lastError()
    }
    return err
}

//
func (db * Database) Insert(key string, value string) (err error) {
    return db.update(key, value, C.GDBM_INSERT)
}

//
func (db * Database) Replace(key string, value string) (err error) {
    return db.update(key, value, C.GDBM_REPLACE)
}

func (db * Database) Exists(key string) bool {
    kcs := C.CString(key)
    k := C.mk_datum(kcs)
    defer C.free(unsafe.Pointer(kcs))

    e := C.gdbm_exists(db.dbf, k)
    if e == 1 {
        return true
    }
    return false
}

//
func (db * Database) Fetch(key string) (value string, err error) {
    kcs := C.CString(key)
    k := C.mk_datum(kcs)
    defer C.free(unsafe.Pointer(kcs))

    vdatum := C.gdbm_fetch(db.dbf, k)
    if vdatum.dptr == nil {
        return "", lastError()
    }

    value = C.GoString(vdatum.dptr)
    defer C.free(unsafe.Pointer(vdatum.dptr))
    return value, nil
}

//
func (db * Database) Delete(key string) (err error) {
    kcs := C.CString(key)
    k := C.mk_datum(kcs)
    defer C.free(unsafe.Pointer(kcs))

    retv := C.gdbm_delete(db.dbf, k)
    if retv == -1 && db.cfg.Mode == "r" {
        err = lastError()
    }
    return err
}

//
func (db * Database) Reorganize() {
    C.gdbm_reorganize(db.dbf)
}

//
func (db * Database) Sync() {
    C.gdbm_sync(db.dbf)
}
<|endoftext|>


<commit_before>// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package compiler

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
)

const (
	// flair           = "Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015"
	flair           = ""
	languageVersion = "0"
)

var (
	versionRegExp = regexp.MustCompile("[0-9]+.[0-9]+.[0-9]+")
	params        = []string{
		"--binary",       // Request to output the contract in binary (hexadecimal).
		"file",           //
		"--json-abi",     // Request to output the contract's JSON ABI interface.
		"file",           //
		"--natspec-user", // Request to output the contract's Natspec user documentation.
		"file",           //
		"--natspec-dev",  // Request to output the contract's Natspec developer documentation.
		"file",
		"--add-std",
		"1",
	}
)

type Contract struct {
	Code string       `json:"code"`
	Info ContractInfo `json:"info"`
}

type ContractInfo struct {
	Source          string      `json:"source"`
	Language        string      `json:"language"`
	LanguageVersion string      `json:"languageVersion"`
	CompilerVersion string      `json:"compilerVersion"`
	AbiDefinition   interface{} `json:"abiDefinition"`
	UserDoc         interface{} `json:"userDoc"`
	DeveloperDoc    interface{} `json:"developerDoc"`
}

type Solidity struct {
	solcPath string
	version  string
}

func New(solcPath string) (sol *Solidity, err error) {
	// set default solc
	if len(solcPath) == 0 {
		solcPath = "solc"
	}
	solcPath, err = exec.LookPath(solcPath)
	if err != nil {
		return
	}

	cmd := exec.Command(solcPath, "--version")
	var out bytes.Buffer
	cmd.Stdout = &out
	err = cmd.Run()
	if err != nil {
		return
	}

	version := versionRegExp.FindString(out.String())
	sol = &Solidity{
		solcPath: solcPath,
		version:  version,
	}
	glog.V(logger.Info).Infoln(sol.Info())
	return
}

func (sol *Solidity) Info() string {
	return fmt.Sprintf("solc v%s\nSolidity Compiler: %s\n%s", sol.version, sol.solcPath, flair)
}

func (sol *Solidity) Version() string {
	return sol.version
}

func (sol *Solidity) Compile(source string) (contracts map[string]*Contract, err error) {

	if len(source) == 0 {
		err = fmt.Errorf("empty source")
		return
	}

	wd, err := ioutil.TempDir("", "solc")
	if err != nil {
		return
	}
	defer os.RemoveAll(wd)

	in := strings.NewReader(source)
	var out bytes.Buffer
	// cwd set to temp dir
	cmd := exec.Command(sol.solcPath, params...)
	cmd.Dir = wd
	cmd.Stdin = in
	cmd.Stdout = &out
	err = cmd.Run()
	if err != nil {
		err = fmt.Errorf("solc error: %v", err)
		return
	}

	matches, _ := filepath.Glob(wd + "/*.binary")
	if len(matches) < 1 {
		err = fmt.Errorf("solc error: missing code output")
		return
	}

	contracts = make(map[string]*Contract)
	for _, path := range matches {
		_, file := filepath.Split(path)
		base := strings.Split(file, ".")[0]

		codeFile := filepath.Join(wd, base+".binary")
		abiDefinitionFile := filepath.Join(wd, base+".abi")
		userDocFile := filepath.Join(wd, base+".docuser")
		developerDocFile := filepath.Join(wd, base+".docdev")

		var code, abiDefinitionJson, userDocJson, developerDocJson []byte
		code, err = ioutil.ReadFile(codeFile)
		if err != nil {
			err = fmt.Errorf("error reading compiler output for code: %v", err)
			return
		}
		abiDefinitionJson, err = ioutil.ReadFile(abiDefinitionFile)
		if err != nil {
			err = fmt.Errorf("error reading compiler output for abiDefinition: %v", err)
			return
		}
		var abiDefinition interface{}
		err = json.Unmarshal(abiDefinitionJson, &abiDefinition)

		userDocJson, err = ioutil.ReadFile(userDocFile)
		if err != nil {
			err = fmt.Errorf("error reading compiler output for userDoc: %v", err)
			return
		}
		var userDoc interface{}
		err = json.Unmarshal(userDocJson, &userDoc)

		developerDocJson, err = ioutil.ReadFile(developerDocFile)
		if err != nil {
			err = fmt.Errorf("error reading compiler output for developerDoc: %v", err)
			return
		}
		var developerDoc interface{}
		err = json.Unmarshal(developerDocJson, &developerDoc)

		contract := &Contract{
			Code: "0x" + string(code),
			Info: ContractInfo{
				Source:          source,
				Language:        "Solidity",
				LanguageVersion: languageVersion,
				CompilerVersion: sol.version,
				AbiDefinition:   abiDefinition,
				UserDoc:         userDoc,
				DeveloperDoc:    developerDoc,
			},
		}

		contracts[base] = contract
	}

	return
}

func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) {
	infojson, err := json.Marshal(info)
	if err != nil {
		return
	}
	contenthash = common.BytesToHash(crypto.Sha3(infojson))
	err = ioutil.WriteFile(filename, infojson, 0600)
	return
}
<commit_msg>common/compiler: fix #1598, expose solidity errors<commit_after>// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package compiler

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/logger/glog"
)

const (
	// flair           = "Christian <c@ethdev.com> and Lefteris <lefteris@ethdev.com> (c) 2014-2015"
	flair           = ""
	languageVersion = "0"
)

var (
	versionRegExp = regexp.MustCompile("[0-9]+.[0-9]+.[0-9]+")
	params        = []string{
		"--binary",       // Request to output the contract in binary (hexadecimal).
		"file",           //
		"--json-abi",     // Request to output the contract's JSON ABI interface.
		"file",           //
		"--natspec-user", // Request to output the contract's Natspec user documentation.
		"file",           //
		"--natspec-dev",  // Request to output the contract's Natspec developer documentation.
		"file",
		"--add-std",
		"1",
	}
)

type Contract struct {
	Code string       `json:"code"`
	Info ContractInfo `json:"info"`
}

type ContractInfo struct {
	Source          string      `json:"source"`
	Language        string      `json:"language"`
	LanguageVersion string      `json:"languageVersion"`
	CompilerVersion string      `json:"compilerVersion"`
	AbiDefinition   interface{} `json:"abiDefinition"`
	UserDoc         interface{} `json:"userDoc"`
	DeveloperDoc    interface{} `json:"developerDoc"`
}

type Solidity struct {
	solcPath string
	version  string
}

func New(solcPath string) (sol *Solidity, err error) {
	// set default solc
	if len(solcPath) == 0 {
		solcPath = "solc"
	}
	solcPath, err = exec.LookPath(solcPath)
	if err != nil {
		return
	}

	cmd := exec.Command(solcPath, "--version")
	var out bytes.Buffer
	cmd.Stdout = &out
	err = cmd.Run()
	if err != nil {
		return
	}

	version := versionRegExp.FindString(out.String())
	sol = &Solidity{
		solcPath: solcPath,
		version:  version,
	}
	glog.V(logger.Info).Infoln(sol.Info())
	return
}

func (sol *Solidity) Info() string {
	return fmt.Sprintf("solc v%s\nSolidity Compiler: %s\n%s", sol.version, sol.solcPath, flair)
}

func (sol *Solidity) Version() string {
	return sol.version
}

// Compile builds and returns all the contracts contained within a source string.
func (sol *Solidity) Compile(source string) (map[string]*Contract, error) {
	// Short circuit if no source code was specified
	if len(source) == 0 {
		return nil, errors.New("solc: empty source string")
	}
	// Create a safe place to dump compilation output
	wd, err := ioutil.TempDir("", "solc")
	if err != nil {
		return nil, fmt.Errorf("solc: failed to create temporary build folder: %v", err)
	}
	defer os.RemoveAll(wd)

	// Assemble the compiler command, change to the temp folder and capture any errors
	stderr := new(bytes.Buffer)

	cmd := exec.Command(sol.solcPath, params...)
	cmd.Dir = wd
	cmd.Stdin = strings.NewReader(source)
	cmd.Stderr = stderr

	if err := cmd.Run(); err != nil {
		return nil, fmt.Errorf("solc: %v\n%s", err, string(stderr.Bytes()))
	}
	// Sanity check that something was actually built
	matches, _ := filepath.Glob(wd + "/*.binary")
	if len(matches) < 1 {
		return nil, fmt.Errorf("solc: no build results found")
	}
	// Compilation succeeded, assemble and return the contracts
	contracts := make(map[string]*Contract)
	for _, path := range matches {
		_, file := filepath.Split(path)
		base := strings.Split(file, ".")[0]

		// Parse the individual compilation results (code binary, ABI definitions, user and dev docs)
		var binary []byte
		if binary, err = ioutil.ReadFile(filepath.Join(wd, base+".binary")); err != nil {
			return nil, fmt.Errorf("solc: error reading compiler output for code: %v", err)
		}

		var abi interface{}
		if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".abi")); err != nil {
			return nil, fmt.Errorf("solc: error reading abi definition: %v", err)
		} else if err = json.Unmarshal(blob, &abi); err != nil {
			return nil, fmt.Errorf("solc: error parsing abi definition: %v", err)
		}

		var userdoc interface{}
		if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".docuser")); err != nil {
			return nil, fmt.Errorf("solc: error reading user doc: %v", err)
		} else if err = json.Unmarshal(blob, &userdoc); err != nil {
			return nil, fmt.Errorf("solc: error parsing user doc: %v", err)
		}

		var devdoc interface{}
		if blob, err := ioutil.ReadFile(filepath.Join(wd, base+".docdev")); err != nil {
			return nil, fmt.Errorf("solc: error reading dev doc: %v", err)
		} else if err = json.Unmarshal(blob, &devdoc); err != nil {
			return nil, fmt.Errorf("solc: error parsing dev doc: %v", err)
		}
		// Assemble the final contract
		contracts[base] = &Contract{
			Code: "0x" + string(binary),
			Info: ContractInfo{
				Source:          source,
				Language:        "Solidity",
				LanguageVersion: languageVersion,
				CompilerVersion: sol.version,
				AbiDefinition:   abi,
				UserDoc:         userdoc,
				DeveloperDoc:    devdoc,
			},
		}
	}
	return contracts, nil
}

func SaveInfo(info *ContractInfo, filename string) (contenthash common.Hash, err error) {
	infojson, err := json.Marshal(info)
	if err != nil {
		return
	}
	contenthash = common.BytesToHash(crypto.Sha3(infojson))
	err = ioutil.WriteFile(filename, infojson, 0600)
	return
}
<|endoftext|>


<commit_before>package gumble_ffmpeg

import (
	"encoding/binary"
	"errors"
	"io"
	"os/exec"
	"strconv"
	"sync"
	"time"

	"github.com/layeh/gumble/gumble"
)

const (
	DefaultCommand = "ffmpeg"
)

type Stream struct {
	// Command to execute to play the file. Defaults to "ffmpeg".
	Command string
	// Playback volume. This value can be changed while the source is playing.
	Volume float32
	// Audio source. This value should not be closed until the stream is done
	// playing.
	Source Source

	client *gumble.Client
	cmd    *exec.Cmd
	pipe   io.ReadCloser

	stop          chan bool
	stopWaitGroup sync.WaitGroup
}

// New creates a new stream on the given gumble client.
func New(client *gumble.Client) *Stream {
	stream := &Stream{
		client:  client,
		Volume:  1.0,
		Command: DefaultCommand,
		stop:    make(chan bool),
	}
	return stream
}

// Play starts playing the stream to the gumble client. Returns non-nil if the
// stream could not be started.
func (s *Stream) Play() error {
	if s.IsPlaying() {
		return errors.New("already playing")
	}
	if s.Source == nil {
		return errors.New("nil source")
	}
	args := s.Source.arguments()
	args = append(args, []string{"-ac", "1", "-ar", strconv.Itoa(gumble.AudioSampleRate), "-f", "s16le", "-"}...)
	cmd := exec.Command(s.Command, args...)
	if pipe, err := cmd.StdoutPipe(); err != nil {
		return err
	} else {
		s.pipe = pipe
	}
	s.Source.start(cmd)
	if err := cmd.Start(); err != nil {
		s.Source.done()
		return err
	}
	s.stopWaitGroup.Add(1)
	s.cmd = cmd
	go s.sourceRoutine()
	return nil
}

// IsPlaying returns if a stream is playing.
func (s *Stream) IsPlaying() bool {
	return s.cmd != nil
}

// Wait returns once the stream has finished playing.
func (s *Stream) Wait() {
	s.stopWaitGroup.Wait()
}

// Stop stops the currently playing stream.
func (s *Stream) Stop() error {
	if !s.IsPlaying() {
		return errors.New("nothing playing")
	}

	s.stop <- true
	s.stopWaitGroup.Wait()
	return nil
}

func (s *Stream) sourceRoutine() {
	interval := s.client.Config.GetAudioInterval()
	frameSize := s.client.Config.GetAudioFrameSize()

	ticker := time.NewTicker(interval)

	defer func() {
		ticker.Stop()
		s.cmd.Process.Kill()
		s.cmd.Wait()
		s.cmd = nil
		s.Source.done()
		s.stopWaitGroup.Done()
	}()

	int16Buffer := make([]int16, frameSize)
	byteBuffer := make([]byte, frameSize*2)

	for {
		select {
		case <-s.stop:
			return
		case <-ticker.C:
			if _, err := io.ReadFull(s.pipe, byteBuffer); err != nil {
				return
			}
			for i := range int16Buffer {
				float := float32(int16(binary.LittleEndian.Uint16(byteBuffer[i*2 : (i+1)*2])))
				int16Buffer[i] = int16(s.Volume * float)
			}
			s.client.Send(gumble.AudioBuffer(int16Buffer))
		}
	}
}
<commit_msg>gumble_ffmpeg: allow starting offset to be set (fixes #14)<commit_after>package gumble_ffmpeg

import (
	"encoding/binary"
	"errors"
	"io"
	"os/exec"
	"strconv"
	"sync"
	"time"

	"github.com/layeh/gumble/gumble"
)

const (
	DefaultCommand = "ffmpeg"
)

type Stream struct {
	// Command to execute to play the file. Defaults to "ffmpeg".
	Command string
	// Playback volume. This value can be changed while the source is playing.
	Volume float32
	// Audio source. This value should not be closed until the stream is done
	// playing.
	Source Source
	// Starting offset.
	Offset time.Duration

	client *gumble.Client
	cmd    *exec.Cmd
	pipe   io.ReadCloser

	stop          chan bool
	stopWaitGroup sync.WaitGroup
}

// New creates a new stream on the given gumble client.
func New(client *gumble.Client) *Stream {
	stream := &Stream{
		client:  client,
		Volume:  1.0,
		Command: DefaultCommand,
		stop:    make(chan bool),
	}
	return stream
}

// Play starts playing the stream to the gumble client. Returns non-nil if the
// stream could not be started.
func (s *Stream) Play() error {
	if s.IsPlaying() {
		return errors.New("already playing")
	}
	if s.Source == nil {
		return errors.New("nil source")
	}
	args := s.Source.arguments()
	if secs := int(s.Offset.Seconds()); secs > 0 {
		args = append([]string{"-ss", strconv.Itoa(secs)}, args...)
	}
	args = append(args, []string{"-ac", "1", "-ar", strconv.Itoa(gumble.AudioSampleRate), "-f", "s16le", "-"}...)
	cmd := exec.Command(s.Command, args...)
	if pipe, err := cmd.StdoutPipe(); err != nil {
		return err
	} else {
		s.pipe = pipe
	}
	s.Source.start(cmd)
	if err := cmd.Start(); err != nil {
		s.Source.done()
		return err
	}
	s.stopWaitGroup.Add(1)
	s.cmd = cmd
	go s.sourceRoutine()
	return nil
}

// IsPlaying returns if a stream is playing.
func (s *Stream) IsPlaying() bool {
	return s.cmd != nil
}

// Wait returns once the stream has finished playing.
func (s *Stream) Wait() {
	s.stopWaitGroup.Wait()
}

// Stop stops the currently playing stream.
func (s *Stream) Stop() error {
	if !s.IsPlaying() {
		return errors.New("nothing playing")
	}

	s.stop <- true
	s.stopWaitGroup.Wait()
	return nil
}

func (s *Stream) sourceRoutine() {
	interval := s.client.Config.GetAudioInterval()
	frameSize := s.client.Config.GetAudioFrameSize()

	ticker := time.NewTicker(interval)

	defer func() {
		ticker.Stop()
		s.cmd.Process.Kill()
		s.cmd.Wait()
		s.cmd = nil
		s.Source.done()
		s.stopWaitGroup.Done()
	}()

	int16Buffer := make([]int16, frameSize)
	byteBuffer := make([]byte, frameSize*2)

	for {
		select {
		case <-s.stop:
			return
		case <-ticker.C:
			if _, err := io.ReadFull(s.pipe, byteBuffer); err != nil {
				return
			}
			for i := range int16Buffer {
				float := float32(int16(binary.LittleEndian.Uint16(byteBuffer[i*2 : (i+1)*2])))
				int16Buffer[i] = int16(s.Volume * float)
			}
			s.client.Send(gumble.AudioBuffer(int16Buffer))
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
    "bufio"
    "errors"
    "flag"
    "fmt"
    "log"
    "regexp"
    "strconv"
    "strings"
    "os"
    )

var (
    SPACES = regexp.MustCompile("\\s+")
    INVALID_POS = errors.New("invalid position")
    )

type Pos struct {
    Start, End *int
}

func (p Pos) String() (result string) {
    if p.Start != nil {
        result = strconv.Itoa(*p.Start)
    }

    result += ":"

    if p.End != nil {
        result += strconv.Itoa(*p.End)
    }

    return
}

func (p *Pos) Set(s string) error {
    p.Start = nil
    p.End = nil

    parts := strings.Split(s, ":")
    if len(parts) < 1 || len(parts) > 2 {
        return INVALID_POS
    }

    if len(parts[0]) > 0 {
        v, err := strconv.Atoi(parts[0])
        if err != nil {
            return err
        }

        p.Start = &v
    }

    if len(parts) == 1 {
        // not a slice
        if p.Start != nil {
            v := *p.Start + 1
            p.End = &v
        }
    } else if len(parts[1]) > 0 {
        v, err := strconv.Atoi(parts[1])
        if err != nil {
            return err
        }

        p.End = &v
    }

    return nil
}

func Slice(source []string, p Pos) []string {
    var start, end int

    if p.Start == nil {
        start = 0
    } else if *p.Start >= len(source) {
        return source[0:0]
    } else {
        start = *p.Start
    }

    if p.End == nil || *p.End >= len(source) {
        return source[start:]
    } else {
        end = *p.End
    }

    return source[start:end]
}

func Quote(a []string) []string {
    q := make([]string, len(a))
    for i, s := range a {
       q[i] = fmt.Sprintf("%q", s)
    }

    return q
}

func main() {
    ifs := flag.String("ifs", " ", "input field separator")
    ofs := flag.String("ofs", " ", "input field separator")
    quote := flag.Bool("quote", false, "quote returned fields")
    flag.Parse()

    pos := make([]Pos, len(flag.Args()))

    for i, arg := range(flag.Args()) {
        pos[i].Set(arg)
    }

    scanner := bufio.NewScanner(os.Stdin)

    for scanner.Scan() {
        if scanner.Err() != nil {
            log.Fatal(scanner.Err())
        }

        line := scanner.Text()

        var fields, result []string

        // split the line according to input field separator
        if *ifs == " " {
            fields = SPACES.Split(strings.TrimSpace(line), -1)
        } else {
            fields = strings.Split(line, *ifs)
        }

        // do some processing
        if len(pos) > 0 {
            result = make([]string, 0)

            for _, p := range pos {
                val := strings.Join(Slice(fields, p), *ifs)
                result = append(result, val)
            }
        } else {
            result = fields
        }

        if *quote {
            result = Quote(result)
        }

        // join the result according to output field separator
        fmt.Println(strings.Join(result, *ofs))
    }
}
<commit_msg>Added --unquote option: thou shall unquote what thou have quoted<commit_after>package main

import (
	"bufio"
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var (
	SPACES      = regexp.MustCompile("\\s+")
	INVALID_POS = errors.New("invalid position")
)

type Pos struct {
	Start, End *int
}

func (p Pos) String() (result string) {
	if p.Start != nil {
		result = strconv.Itoa(*p.Start)
	}

	result += ":"

	if p.End != nil {
		result += strconv.Itoa(*p.End)
	}

	return
}

func (p *Pos) Set(s string) error {
	p.Start = nil
	p.End = nil

	parts := strings.Split(s, ":")
	if len(parts) < 1 || len(parts) > 2 {
		return INVALID_POS
	}

	if len(parts[0]) > 0 {
		v, err := strconv.Atoi(parts[0])
		if err != nil {
			return err
		}

		p.Start = &v
	}

	if len(parts) == 1 {
		// not a slice
		if p.Start != nil {
			v := *p.Start + 1
			p.End = &v
		}
	} else if len(parts[1]) > 0 {
		v, err := strconv.Atoi(parts[1])
		if err != nil {
			return err
		}

		p.End = &v
	}

	return nil
}

func Slice(source []string, p Pos) []string {
	var start, end int

	if p.Start == nil {
		start = 0
	} else if *p.Start >= len(source) {
		return source[0:0]
	} else {
		start = *p.Start
	}

	if p.End == nil || *p.End >= len(source) {
		return source[start:]
	} else {
		end = *p.End
	}

	return source[start:end]
}

func Quote(a []string) []string {
	q := make([]string, len(a))
	for i, s := range a {
		q[i] = fmt.Sprintf("%q", s)
	}

	return q
}

func Unquote(a []string) []string {
	q := make([]string, len(a))
	for i, s := range a {
		q[i] = strings.Trim(s, `"'`)
	}

	return q
}

func main() {
	ifs := flag.String("ifs", " ", "input field separator")
	ofs := flag.String("ofs", " ", "input field separator")
	quote := flag.Bool("quote", false, "quote returned fields")
	unquote := flag.Bool("unquote", false, "unquote returned fields")
	flag.Parse()

	pos := make([]Pos, len(flag.Args()))

	for i, arg := range flag.Args() {
		pos[i].Set(arg)
	}

	scanner := bufio.NewScanner(os.Stdin)

	for scanner.Scan() {
		if scanner.Err() != nil {
			log.Fatal(scanner.Err())
		}

		line := scanner.Text()

		var fields, result []string

		// split the line according to input field separator
		if *ifs == " " {
			fields = SPACES.Split(strings.TrimSpace(line), -1)
		} else {
			fields = strings.Split(line, *ifs)
		}

		// do some processing
		if len(pos) > 0 {
			result = make([]string, 0)

			for _, p := range pos {
				val := strings.Join(Slice(fields, p), *ifs)
				result = append(result, val)
			}
		} else {
			result = fields
		}

		if *unquote {
			result = Unquote(result)
		}

		if *quote {
			result = Quote(result)
		}

		// join the result according to output field separator
		fmt.Println(strings.Join(result, *ofs))
	}
}
<|endoftext|>


<commit_before>package signal_test

import (
	"context"
	"runtime"
	"testing"
	"time"

	. "v2ray.com/core/common/signal"
	. "v2ray.com/ext/assert"
)

func TestActivityTimer(t *testing.T) {
	assert := With(t)

	ctx, cancel := context.WithCancel(context.Background())
	timer := CancelAfterInactivity(ctx, cancel, time.Second*5)
	time.Sleep(time.Second * 6)
	assert(ctx.Err(), IsNotNil)
	runtime.KeepAlive(timer)
}

func TestActivityTimerUpdate(t *testing.T) {
	assert := With(t)

	ctx, cancel := context.WithCancel(context.Background())
	timer := CancelAfterInactivity(ctx, cancel, time.Second*10)
	time.Sleep(time.Second * 3)
	assert(ctx.Err(), IsNil)
	timer.SetTimeout(time.Second * 1)
	time.Sleep(time.Second * 2)
	assert(ctx.Err(), IsNotNil)
	runtime.KeepAlive(timer)
}

func TestActivityTimerNonBlocking(t *testing.T) {
	assert := With(t)

	ctx, cancel := context.WithCancel(context.Background())
	timer := CancelAfterInactivity(ctx, cancel, 0)
	time.Sleep(time.Second * 1)
	assert(ctx, HasDone)
	timer.SetTimeout(0)
	timer.SetTimeout(1)
	timer.SetTimeout(2)
}

func TestActivityTimerZeroTimeout(t *testing.T) {
	assert := With(t)

	ctx, cancel := context.WithCancel(context.Background())
	timer := CancelAfterInactivity(ctx, cancel, 0)
	assert(ctx, HasDone)
	runtime.KeepAlive(timer)
}
<commit_msg>fix flaky test<commit_after>package signal_test

import (
	"context"
	"runtime"
	"testing"
	"time"

	. "v2ray.com/core/common/signal"
	. "v2ray.com/ext/assert"
)

func TestActivityTimer(t *testing.T) {
	assert := With(t)

	ctx, cancel := context.WithCancel(context.Background())
	timer := CancelAfterInactivity(ctx, cancel, time.Second*4)
	time.Sleep(time.Second * 6)
	assert(ctx.Err(), IsNotNil)
	runtime.KeepAlive(timer)
}

func TestActivityTimerUpdate(t *testing.T) {
	assert := With(t)

	ctx, cancel := context.WithCancel(context.Background())
	timer := CancelAfterInactivity(ctx, cancel, time.Second*10)
	time.Sleep(time.Second * 3)
	assert(ctx.Err(), IsNil)
	timer.SetTimeout(time.Second * 1)
	time.Sleep(time.Second * 2)
	assert(ctx.Err(), IsNotNil)
	runtime.KeepAlive(timer)
}

func TestActivityTimerNonBlocking(t *testing.T) {
	assert := With(t)

	ctx, cancel := context.WithCancel(context.Background())
	timer := CancelAfterInactivity(ctx, cancel, 0)
	time.Sleep(time.Second * 1)
	assert(ctx, HasDone)
	timer.SetTimeout(0)
	timer.SetTimeout(1)
	timer.SetTimeout(2)
}

func TestActivityTimerZeroTimeout(t *testing.T) {
	assert := With(t)

	ctx, cancel := context.WithCancel(context.Background())
	timer := CancelAfterInactivity(ctx, cancel, 0)
	assert(ctx, HasDone)
	runtime.KeepAlive(timer)
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package platformvm

import (
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
)

var (
	errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block")

	_ Block    = &AtomicBlock{}
	_ decision = &AtomicBlock{}
)

// AtomicBlock being accepted results in the transaction contained in the
// block to be accepted and committed to the chain.
type AtomicBlock struct {
	CommonDecisionBlock `serialize:"true"`

	Tx Tx `serialize:"true" json:"tx"`

	inputs ids.Set
}

// initialize this block
func (ab *AtomicBlock) initialize(vm *VM, bytes []byte, status choices.Status, self Block) error {
	if err := ab.CommonDecisionBlock.initialize(vm, bytes, status, self); err != nil {
		return fmt.Errorf("failed to initialize: %w", err)
	}
	unsignedBytes, err := vm.codec.Marshal(codecVersion, &ab.Tx.UnsignedTx)
	if err != nil {
		return fmt.Errorf("failed to marshal unsigned tx: %w", err)
	}
	signedBytes, err := ab.vm.codec.Marshal(codecVersion, &ab.Tx)
	if err != nil {
		return fmt.Errorf("failed to marshal tx: %w", err)
	}
	ab.Tx.Initialize(unsignedBytes, signedBytes)
	return nil
}

// Reject implements the snowman.Block interface
func (ab *AtomicBlock) conflicts(s ids.Set) (bool, error) {
	if ab.Status() == choices.Accepted {
		return false, nil
	}
	if ab.inputs.Overlaps(s) {
		return true, nil
	}
	parent, err := ab.parent()
	if err != nil {
		return false, err
	}
	return parent.conflicts(s)
}

// Verify this block performs a valid state transition.
//
// The parent block must be a proposal
//
// This function also sets onAcceptDB database if the verification passes.
func (ab *AtomicBlock) Verify() error {
	if err := ab.CommonDecisionBlock.Verify(); err != nil {
		if err := ab.Reject(); err != nil {
			ab.vm.ctx.Log.Error("failed to reject atomic block %s due to %s", ab.ID(), err)
		}
		return err
	}

	tx, ok := ab.Tx.UnsignedTx.(UnsignedAtomicTx)
	if !ok {
		return errWrongTxType
	}
	ab.inputs = tx.InputUTXOs()

	parentIntf, err := ab.parent()
	if err != nil {
		return err
	}

	conflicts, err := parentIntf.conflicts(ab.inputs)
	if err != nil {
		return err
	}
	if conflicts {
		return errConflictingParentTxs
	}

	// AtomicBlock is not a modifier on a proposal block, so its parent must be
	// a decision.
	parent, ok := parentIntf.(decision)
	if !ok {
		return errInvalidBlockType
	}

	parentState := parent.onAccept()
	onAccept, err := tx.SemanticVerify(ab.vm, parentState, &ab.Tx)
	if err != nil {
		ab.vm.droppedTxCache.Put(ab.Tx.ID(), err.Error()) // cache tx as dropped
		return fmt.Errorf("tx %s failed semantic verification: %w", tx.ID(), err)
	}
	onAccept.AddTx(&ab.Tx, Committed)

	ab.onAcceptState = onAccept
	ab.vm.currentBlocks[ab.ID()] = ab
	parentIntf.addChild(ab)
	return nil
}

// Accept implements the snowman.Block interface
func (ab *AtomicBlock) Accept() error {
	blkID := ab.ID()
	ab.vm.ctx.Log.Verbo(
		"Accepting Atomic Block %s at height %d with parent %s",
		blkID,
		ab.Height(),
		ab.ParentID(),
	)

	if err := ab.CommonBlock.Accept(); err != nil {
		return fmt.Errorf("failed to accept CommonBlock of %s: %w", blkID, err)
	}

	tx, ok := ab.Tx.UnsignedTx.(UnsignedAtomicTx)
	if !ok {
		return errWrongTxType
	}

	// Update the state of the chain in the database
	ab.onAcceptState.Apply(ab.vm.internalState)

	defer ab.vm.internalState.Abort()
	batch, err := ab.vm.internalState.CommitBatch()
	if err != nil {
		return fmt.Errorf("failed to commit VM's database for block %s: %w", ab.ID(), err)
	}
	if err := tx.Accept(ab.vm.ctx, batch); err != nil {
		return fmt.Errorf("failed to atomically accept tx %s in block %s: %w", tx.ID(), ab.ID(), err)
	}

	for _, child := range ab.children {
		child.setBaseState()
	}
	if ab.onAcceptFunc != nil {
		if err := ab.onAcceptFunc(); err != nil {
			return fmt.Errorf("failed to execute onAcceptFunc of %s: %w", blkID, err)
		}
	}

	ab.free()
	return nil
}

// Reject implements the snowman.Block interface
func (ab *AtomicBlock) Reject() error {
	ab.vm.ctx.Log.Verbo("Rejecting Atomic Block %s at height %d with parent %s", ab.ID(), ab.Height(), ab.ParentID())

	if err := ab.vm.mempool.IssueTx(&ab.Tx); err != nil {
		ab.vm.ctx.Log.Debug("failed to reissue tx %q due to: %s", ab.Tx.ID(), err)
	}
	return ab.CommonDecisionBlock.Reject()
}

// newAtomicBlock returns a new *AtomicBlock where the block's parent, a
// decision block, has ID [parentID].
func (vm *VM) newAtomicBlock(parentID ids.ID, height uint64, tx Tx) (*AtomicBlock, error) {
	ab := &AtomicBlock{
		CommonDecisionBlock: CommonDecisionBlock{
			CommonBlock: CommonBlock{
				PrntID: parentID,
				Hght:   height,
			},
		},
		Tx: tx,
	}

	// We serialize this block as a Block so that it can be deserialized into a
	// Block
	blk := Block(ab)
	bytes, err := Codec.Marshal(codecVersion, &blk)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal block: %w", err)
	}
	return ab, ab.CommonDecisionBlock.initialize(vm, bytes, choices.Processing, ab)
}
<commit_msg>cleanup atomic block<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package platformvm

import (
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow/choices"
)

var (
	errConflictingParentTxs = errors.New("block contains a transaction that conflicts with a transaction in a parent block")

	_ Block    = &AtomicBlock{}
	_ decision = &AtomicBlock{}
)

// AtomicBlock being accepted results in the atomic transaction contained in the
// block to be accepted and committed to the chain.
type AtomicBlock struct {
	CommonDecisionBlock `serialize:"true"`

	Tx Tx `serialize:"true" json:"tx"`

	// inputs are the atomic inputs that are consumed by this block's atomic
	// transaction
	inputs ids.Set
}

func (ab *AtomicBlock) initialize(vm *VM, bytes []byte, status choices.Status, self Block) error {
	if err := ab.CommonDecisionBlock.initialize(vm, bytes, status, self); err != nil {
		return fmt.Errorf("failed to initialize: %w", err)
	}
	unsignedBytes, err := vm.codec.Marshal(codecVersion, &ab.Tx.UnsignedTx)
	if err != nil {
		return fmt.Errorf("failed to marshal unsigned tx: %w", err)
	}
	signedBytes, err := ab.vm.codec.Marshal(codecVersion, &ab.Tx)
	if err != nil {
		return fmt.Errorf("failed to marshal tx: %w", err)
	}
	ab.Tx.Initialize(unsignedBytes, signedBytes)
	return nil
}

// conflicts checks to see if the provided input set contains any conflicts with
// any of this block's non-accepted ancestors or itself.
func (ab *AtomicBlock) conflicts(s ids.Set) (bool, error) {
	if ab.Status() == choices.Accepted {
		return false, nil
	}
	if ab.inputs.Overlaps(s) {
		return true, nil
	}
	parent, err := ab.parent()
	if err != nil {
		return false, err
	}
	return parent.conflicts(s)
}

// Verify this block performs a valid state transition.
//
// The parent block must be a proposal
//
// This function also sets onAcceptDB database if the verification passes.
func (ab *AtomicBlock) Verify() error {
	blkID := ab.ID()

	if err := ab.CommonDecisionBlock.Verify(); err != nil {
		if err := ab.Reject(); err != nil {
			ab.vm.ctx.Log.Error(
				"failed to reject atomic block %s due to %s",
				blkID,
				err,
			)
		}
		return err
	}

	tx, ok := ab.Tx.UnsignedTx.(UnsignedAtomicTx)
	if !ok {
		return errWrongTxType
	}
	ab.inputs = tx.InputUTXOs()

	parentIntf, err := ab.parent()
	if err != nil {
		return err
	}

	conflicts, err := parentIntf.conflicts(ab.inputs)
	if err != nil {
		return err
	}
	if conflicts {
		return errConflictingParentTxs
	}

	// AtomicBlock is not a modifier on a proposal block, so its parent must be
	// a decision.
	parent, ok := parentIntf.(decision)
	if !ok {
		return errInvalidBlockType
	}

	parentState := parent.onAccept()
	onAccept, err := tx.SemanticVerify(ab.vm, parentState, &ab.Tx)
	if err != nil {
		txID := tx.ID()
		ab.vm.droppedTxCache.Put(txID, err.Error()) // cache tx as dropped
		return fmt.Errorf("tx %s failed semantic verification: %w", txID, err)
	}
	onAccept.AddTx(&ab.Tx, Committed)

	ab.onAcceptState = onAccept
	ab.vm.currentBlocks[blkID] = ab
	parentIntf.addChild(ab)
	return nil
}

// Accept implements the snowman.Block interface
func (ab *AtomicBlock) Accept() error {
	blkID := ab.ID()
	ab.vm.ctx.Log.Verbo(
		"Accepting Atomic Block %s at height %d with parent %s",
		blkID,
		ab.Height(),
		ab.ParentID(),
	)

	if err := ab.CommonBlock.Accept(); err != nil {
		return fmt.Errorf("failed to accept CommonBlock of %s: %w", blkID, err)
	}

	tx, ok := ab.Tx.UnsignedTx.(UnsignedAtomicTx)
	if !ok {
		return errWrongTxType
	}

	// Update the state of the chain in the database
	ab.onAcceptState.Apply(ab.vm.internalState)

	defer ab.vm.internalState.Abort()
	batch, err := ab.vm.internalState.CommitBatch()
	if err != nil {
		return fmt.Errorf(
			"failed to commit VM's database for block %s: %w",
			blkID,
			err,
		)
	}
	if err := tx.Accept(ab.vm.ctx, batch); err != nil {
		return fmt.Errorf(
			"failed to atomically accept tx %s in block %s: %w",
			tx.ID(),
			blkID,
			err,
		)
	}

	for _, child := range ab.children {
		child.setBaseState()
	}
	if ab.onAcceptFunc != nil {
		if err := ab.onAcceptFunc(); err != nil {
			return fmt.Errorf(
				"failed to execute onAcceptFunc of %s: %w",
				blkID,
				err,
			)
		}
	}

	ab.free()
	return nil
}

// Reject implements the snowman.Block interface
func (ab *AtomicBlock) Reject() error {
	ab.vm.ctx.Log.Verbo(
		"Rejecting Atomic Block %s at height %d with parent %s",
		ab.ID(),
		ab.Height(),
		ab.ParentID(),
	)

	if err := ab.vm.mempool.IssueTx(&ab.Tx); err != nil {
		ab.vm.ctx.Log.Debug(
			"failed to reissue tx %q due to: %s",
			ab.Tx.ID(),
			err,
		)
	}
	return ab.CommonDecisionBlock.Reject()
}

// newAtomicBlock returns a new *AtomicBlock where the block's parent, a
// decision block, has ID [parentID].
func (vm *VM) newAtomicBlock(parentID ids.ID, height uint64, tx Tx) (*AtomicBlock, error) {
	ab := &AtomicBlock{
		CommonDecisionBlock: CommonDecisionBlock{
			CommonBlock: CommonBlock{
				PrntID: parentID,
				Hght:   height,
			},
		},
		Tx: tx,
	}

	// We serialize this block as a Block so that it can be deserialized into a
	// Block
	blk := Block(ab)
	bytes, err := Codec.Marshal(codecVersion, &blk)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal block: %w", err)
	}
	return ab, ab.CommonDecisionBlock.initialize(vm, bytes, choices.Processing, ab)
}
<|endoftext|>


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

package gogs

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

func Version() string {
	return "0.12.3"
}

// Client represents a Gogs API client.
type Client struct {
	url         string
	accessToken string
	client      *http.Client
}

// NewClient initializes and returns a API client.
func NewClient(url, token string) *Client {
	return &Client{
		url:         strings.TrimSuffix(url, "/"),
		accessToken: token,
		client:      &http.Client{},
	}
}

// SetHTTPClient replaces default http.Client with user given one.
func (c *Client) SetHTTPClient(client *http.Client) {
	c.client = client
}

func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, error) {
	req, err := http.NewRequest(method, c.url+"/api/v1"+path, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "token "+c.accessToken)
	for k, v := range header {
		req.Header[k] = v
	}

	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	case 403:
		return nil, errors.New("403 Forbidden")
	case 404:
		return nil, errors.New("404 Not Found")
	}

	if resp.StatusCode/100 != 2 {
		errMap := make(map[string]interface{})
		if err = json.Unmarshal(data, &errMap); err != nil {
			return nil, err
		}
		return nil, errors.New(errMap["message"].(string))
	}

	return data, nil
}

func (c *Client) getParsedResponse(method, path string, header http.Header, body io.Reader, obj interface{}) error {
	data, err := c.getResponse(method, path, header, body)
	if err != nil {
		return err
	}
	return json.Unmarshal(data, obj)
}
<commit_msg>Split Client.getResponse to Client.doRequest & Client.getResponse. (#44)<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gogs

import (
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
)

func Version() string {
	return "0.12.3"
}

// Client represents a Gogs API client.
type Client struct {
	url         string
	accessToken string
	client      *http.Client
}

// NewClient initializes and returns a API client.
func NewClient(url, token string) *Client {
	return &Client{
		url:         strings.TrimSuffix(url, "/"),
		accessToken: token,
		client:      &http.Client{},
	}
}

// SetHTTPClient replaces default http.Client with user given one.
func (c *Client) SetHTTPClient(client *http.Client) {
	c.client = client
}

func (c *Client) doRequest(method, path string, header http.Header, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, c.url+"/api/v1"+path, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "token "+c.accessToken)
	for k, v := range header {
		req.Header[k] = v
	}

	return c.client.Do(req)
}

func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, error) {
	resp, err := c.doRequest(method, path, header, body)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	switch resp.StatusCode {
	case 403:
		return nil, errors.New("403 Forbidden")
	case 404:
		return nil, errors.New("404 Not Found")
	}

	if resp.StatusCode/100 != 2 {
		errMap := make(map[string]interface{})
		if err = json.Unmarshal(data, &errMap); err != nil {
			return nil, err
		}
		return nil, errors.New(errMap["message"].(string))
	}

	return data, nil
}

func (c *Client) getParsedResponse(method, path string, header http.Header, body io.Reader, obj interface{}) error {
	data, err := c.getResponse(method, path, header, body)
	if err != nil {
		return err
	}
	return json.Unmarshal(data, obj)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"
)

type Tracker map[Window]*Track

type Track struct {
	Start time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		log.Fatal("active: ", err)
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		log.Fatal("net name: ", err)
	}
	if string(name.Value) != "" {
		return string(name.Value)
	}
	name, err = x.property(w, x.nameAtom)
	if err != nil {
		log.Fatal("wm name: ", err)
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		log.Fatal("class: ", err)
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return "unknown"
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) Update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Start = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.Update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Fatal("wait for event: ", everr)
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Start)
			}
			prev = x.Update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Start) > d {
			log.Println("removing", k)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
}

type Index struct {
	Title  string
	Tracks Tracks
	Class  Tracks
	Total  time.Duration
	Zzz    bool
}

type Tracks []track

type track struct {
	Class string
	Name  string
	Time  time.Duration
}

func (t Tracks) Len() int           { return len(t) }
func (t Tracks) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t Tracks) Less(i, j int) bool { return t[i].Time < t[j].Time }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var i Index
	i.Title = "Gone Time Tracker"
	i.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		i.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		i.Tracks = append(i.Tracks, track{
			Class: k.Class,
			Name:  k.Name,
			Time:  v.Spent})
	}
	for k, v := range classtotal {
		i.Class = append(i.Class, track{Class: k, Time: v})
	}
	sort.Sort(sort.Reverse(i.Class))
	sort.Sort(sort.Reverse(i.Tracks))
	err := tmpl.Execute(w, i)
	if err != nil {
		log.Println(err)
	}
}

const (
	port = ":8001"
	file = "dump.gob"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

func main() {
	tracks.load(file)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(6 * time.Hour)
			tracks.store(file)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.ListenAndServe(port, nil)
}
<commit_msg>bumt inactivity to a duration of an average work day of 8h<commit_after>package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"github.com/BurntSushi/xgb"
	"github.com/BurntSushi/xgb/screensaver"
	"github.com/BurntSushi/xgb/xproto"
	"html/template"
	"log"
	"net/http"
	"os"
	"sort"
	"time"
)

type Tracker map[Window]*Track

type Track struct {
	Start time.Time
	Spent time.Duration
}

type Window struct {
	Class string
	Name  string
}

type Xorg struct {
	X           *xgb.Conn
	root        xproto.Window
	activeAtom  *xproto.InternAtomReply
	netNameAtom *xproto.InternAtomReply
	nameAtom    *xproto.InternAtomReply
	classAtom   *xproto.InternAtomReply
}

func (t Track) String() string {
	return fmt.Sprint(t.Spent)
}

func (w Window) String() string {
	return fmt.Sprintf("%s %s", w.Class, w.Name)
}

func (x Xorg) atom(aname string) *xproto.InternAtomReply {
	a, err := xproto.InternAtom(x.X, true, uint16(len(aname)), aname).Reply()
	if err != nil {
		log.Fatal("atom: ", err)
	}
	return a
}

func (x Xorg) property(w xproto.Window, a *xproto.InternAtomReply) (*xproto.GetPropertyReply, error) {
	return xproto.GetProperty(x.X, false, w, a.Atom,
		xproto.GetPropertyTypeAny, 0, (1<<32)-1).Reply()
}

func (x Xorg) active() xproto.Window {
	p, err := x.property(x.root, x.activeAtom)
	if err != nil {
		log.Fatal("active: ", err)
	}
	return xproto.Window(xgb.Get32(p.Value))
}

func (x Xorg) name(w xproto.Window) string {
	name, err := x.property(w, x.netNameAtom)
	if err != nil {
		log.Fatal("net name: ", err)
	}
	if string(name.Value) != "" {
		return string(name.Value)
	}
	name, err = x.property(w, x.nameAtom)
	if err != nil {
		log.Fatal("wm name: ", err)
	}
	return string(name.Value)
}

func (x Xorg) class(w xproto.Window) string {
	class, err := x.property(w, x.classAtom)
	if err != nil {
		log.Fatal("class: ", err)
	}
	i := bytes.IndexByte(class.Value, 0)
	if i == -1 || string(class.Value[:i]) == "" {
		return "unknown"
	}
	return string(class.Value[:i])
}

func (x Xorg) winName() (Window, bool) {
	windowId := x.active()
	/* skip invalid window id */
	if windowId == 0 {
		return Window{}, false
	}
	x.spy(windowId)
	return Window{
		Class: x.class(windowId),
		Name:  x.name(windowId),
	}, true
}

func (x Xorg) spy(w xproto.Window) {
	xproto.ChangeWindowAttributes(x.X, w, xproto.CwEventMask,
		[]uint32{xproto.EventMaskPropertyChange})
}

func (x Xorg) Update(t Tracker) (prev *Track) {
	if win, ok := x.winName(); ok {
		if _, ok := t[win]; !ok {
			t[win] = new(Track)
		}
		t[win].Start = time.Now()
		prev = t[win]
	}
	return
}

func connect() Xorg {
	var x Xorg
	var err error

	x.X, err = xgb.NewConn()
	if err != nil {
		log.Fatal("xgb: ", err)
	}

	err = screensaver.Init(x.X)
	if err != nil {
		log.Fatal("screensaver: ", err)
	}

	setup := xproto.Setup(x.X)
	x.root = setup.DefaultScreen(x.X).Root

	drw := xproto.Drawable(x.root)
	screensaver.SelectInput(x.X, drw, screensaver.EventNotifyMask)

	x.activeAtom = x.atom("_NET_ACTIVE_WINDOW")
	x.netNameAtom = x.atom("_NET_WM_NAME")
	x.nameAtom = x.atom("WM_NAME")
	x.classAtom = x.atom("WM_CLASS")

	x.spy(x.root)

	return x
}

func (t Tracker) collect() {
	x := connect()
	defer x.X.Close()

	prev := x.Update(t)
	for {
		ev, everr := x.X.WaitForEvent()
		if everr != nil {
			log.Fatal("wait for event: ", everr)
		}
		switch event := ev.(type) {
		case xproto.PropertyNotifyEvent:
			if prev != nil {
				prev.Spent += time.Since(prev.Start)
			}
			prev = x.Update(t)
		case screensaver.NotifyEvent:
			switch event.State {
			case screensaver.StateOn:
				fmt.Println("away from keyboard")
				prev = nil
				zzz = true
			default:
				zzz = false
			}
		}
	}
}

func (t Tracker) cleanup(d time.Duration) {
	for k, v := range t {
		if time.Since(v.Start) > d {
			log.Println("removing", k)
			delete(t, k)
		}
	}
}

func (t Tracker) load(fname string) {
	dump, err := os.Open(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	dec := gob.NewDecoder(dump)
	err = dec.Decode(&t)
	if err != nil {
		log.Println(err)
	}
}

func (t Tracker) store(fname string) {
	dump, err := os.Create(fname)
	if err != nil {
		log.Println(err)
		return
	}
	defer dump.Close()
	enc := gob.NewEncoder(dump)
	err = enc.Encode(t)
	if err != nil {
		log.Println(err)
	}
}

type Index struct {
	Title  string
	Tracks Tracks
	Class  Tracks
	Total  time.Duration
	Zzz    bool
}

type Tracks []track

type track struct {
	Class string
	Name  string
	Time  time.Duration
}

func (t Tracks) Len() int           { return len(t) }
func (t Tracks) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t Tracks) Less(i, j int) bool { return t[i].Time < t[j].Time }

func indexHandler(w http.ResponseWriter, r *http.Request) {
	var i Index
	i.Title = "Gone Time Tracker"
	i.Zzz = zzz
	class := r.URL.Path[1:]

	classtotal := make(map[string]time.Duration)

	for k, v := range tracks {
		classtotal[k.Class] += v.Spent
		i.Total += v.Spent
		if class != "" && class != k.Class {
			continue
		}
		i.Tracks = append(i.Tracks, track{
			Class: k.Class,
			Name:  k.Name,
			Time:  v.Spent})
	}
	for k, v := range classtotal {
		i.Class = append(i.Class, track{Class: k, Time: v})
	}
	sort.Sort(sort.Reverse(i.Class))
	sort.Sort(sort.Reverse(i.Tracks))
	err := tmpl.Execute(w, i)
	if err != nil {
		log.Println(err)
	}
}

const (
	port = ":8001"
	file = "dump.gob"
)

var (
	tracks = make(Tracker)
	tmpl   = template.Must(template.ParseFiles("index.html"))
	zzz    bool
)

func main() {
	tracks.load(file)
	go tracks.collect()
	go func() {
		for {
			tracks.cleanup(8 * time.Hour)
			tracks.store(file)
			time.Sleep(time.Minute)
		}
	}()
	log.Println("listen on", port)
	http.HandleFunc("/", indexHandler)
	http.ListenAndServe(port, nil)
}
<|endoftext|>


<commit_before>package goth

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

var (
	// DefaultDeniedHandler handles the requests that were denied access because
	// of OAuth1. By default, returns a 401 status code with a generic message.
	DefaultDeniedHandler = http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Unauthorized", 401)
	}))
)

type OAuth1 struct {
	// A function that return the consumer secret and the username of the associated consumer key
	CheckerFunc OAuthCheckerFunc

	// DeniedHandler is called if the request is unauthorized. If it is nil,
	// the DefaultDeniedHandler variable is used.
	DeniedHandler http.Handler
}

type OAuthCheckerFunc func(consumerKey string) (userId string, consumerSecret string)

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() error { return nil }

func splitAuthHeader(oAuthHeader string) map[string]string {
	// test it starts with OAuth
	if !strings.HasPrefix(oAuthHeader, "OAuth ") {
		return nil
	}
	m := make(map[string]string)
	// split the values by comma
	splitted := strings.Split(oAuthHeader[5:], ",")
	for _, oauthField := range splitted {
		oauthField := strings.TrimSpace(oauthField)

		// check this is a real oauth field
		if !strings.HasPrefix(oauthField, "oauth_") {
			continue
		}

		splittedField := strings.Split(oauthField, "=")
		if len(splittedField) < 2 {
			continue
		}
		key := splittedField[0]
		val := splittedField[1]

		// ensure val is surrounded by ""
		if !strings.HasPrefix(val, "\"") || !strings.HasSuffix(val, "\"") {
			continue
		}
		val = val[1 : len(val)-1]
		// url unescape
		m[key], _ = url.QueryUnescape(val)
	}
	return m
}

func signatureBase(r *http.Request, oAuthHeaders map[string]string) string {
	// we sort the oauth params alphabetically then normalize it
	orderedParams := make([]string, len(oAuthHeaders))
	var i int
	for k, _ := range oAuthHeaders {
		orderedParams[i] = k
		i++
	}

	sort.Strings(orderedParams)

	stringParams := make([]string, 0)
	for _, param := range orderedParams {
		// remove the oauth_signature
		if param == "oauth_signature" {
			continue
		}
		stringParams = append(stringParams, param+"="+oAuthHeaders[param])
	}

	normalizedParams := strings.Join(stringParams, "&")

	//TODO: find the base url
	sign := fmt.Sprintf("%s&%s&%s",
		r.Method,
		url.QueryEscape("http://127.0.0.1:3000"+r.URL.String()),
		url.QueryEscape(normalizedParams))
	return sign
}

// checkRequest is checking the request is OAuth 1.0a valid and check the body_hash
// return the oauth_consumer_key
func checkRequest(r *http.Request) (consumerKey string, body string, oAuthHeaders map[string]string) {
	// test we have a header Authorization
	if _, ok := r.Header["Authorization"]; !ok {
		return
	}
	oAuthHeaders = splitAuthHeader(r.Header.Get("Authorization"))

	// test for the most needed oauth_ entries
	if _, ok := oAuthHeaders["oauth_consumer_key"]; !ok {
		return
	}
	if _, ok := oAuthHeaders["oauth_timestamp"]; !ok {
		return
	}
	if _, ok := oAuthHeaders["oauth_signature"]; !ok {
		return
	}

	// check for timestamp
	timestamp, err := strconv.Atoi(oAuthHeaders["oauth_timestamp"])
	if err != (nil) {
		return
	}

	diff := int(time.Now().Unix()) - timestamp
	// stupid Abs for int
	if diff < 0 {
		diff = -diff
	}
	// if request is older than 120s
	if diff > 120 {
		return
	}

	// if the method is not GET and the content type is not url encoded
	// we have to check for oauth_body_hash
	if r.Method != "GET" && r.Header.Get("Content-Type") != "application/x-www-form-urlencoded" {
		if _, ok := oAuthHeaders["oauth_body_hash"]; !ok {
			return
		}
		hasher := sha1.New()

		// we need to read the body for oauth_body_hash to be calculated
		// but then restore it later for the others middleware to read it

		bodydata, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return
		}
		hasher.Write(bodydata)
		bodySign := base64.StdEncoding.EncodeToString(hasher.Sum(nil))

		body = string(bodydata[:])
		if bodySign != oAuthHeaders["oauth_body_hash"] {
			return
		}
		// escape body hash for later signature regeneration
		oAuthHeaders["oauth_body_hash"] = url.QueryEscape(oAuthHeaders["oauth_body_hash"])
	}

	consumerKey = oAuthHeaders["oauth_consumer_key"]
	return
}

func (o *OAuth1) AuthProtect(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if consumerKey, body, oAuthHeaders := checkRequest(r); consumerKey != "" {
			// generate base signature string
			sign := signatureBase(r, oAuthHeaders)
			user, consumerSecret := o.CheckerFunc(consumerKey)
			if consumerSecret != "" {
				// in 3 legs should have consumer_key&secret_key
				// in 2 legs only one but keep &
				hashfun := hmac.New(sha1.New, []byte(consumerSecret+"&"))
				hashfun.Write([]byte(sign))
				rawsignature := hashfun.Sum(nil)
				base64signature := make([]byte, base64.StdEncoding.EncodedLen(len(rawsignature)))
				base64.StdEncoding.Encode(base64signature, rawsignature)

				sign = string(base64signature)

				if sign == oAuthHeaders["oauth_signature"] {
					r.Body = nopCloser{bytes.NewBufferString(body)}
					r.URL.User = url.UserPassword(user, "")
					h.ServeHTTP(w, r)
					return
				}
			}
		}
		dh := o.DeniedHandler
		if dh == nil {
			dh = DefaultDeniedHandler
		}
		dh.ServeHTTP(w, r)
	})
}
<commit_msg>added body hash params, fix issue with Hostname<commit_after>package goth

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

var (
	// DefaultDeniedHandler handles the requests that were denied access because
	// of OAuth1. By default, returns a 401 status code with a generic message.
	DefaultDeniedHandler = http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Unauthorized", 401)
	}))
)

type OAuth1 struct {
	// A function that return the consumer secret and the username of the associated consumer key
	CheckerFunc OAuthCheckerFunc

	// DeniedHandler is called if the request is unauthorized. If it is nil,
	// the DefaultDeniedHandler variable is used.
	DeniedHandler http.Handler

	// WithBodyHash will check for the optionnal oauth_body_hash, default to no
	// see http://oauth.googlecode.com/svn'/spec/ext/body_hash/1.0/drafts/4/spec.html
	WithBodyHash bool
}

type OAuthCheckerFunc func(consumerKey string) (userId string, consumerSecret string)

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() error { return nil }

func splitAuthHeader(oAuthHeader string) map[string]string {
	// test it starts with OAuth
	if !strings.HasPrefix(oAuthHeader, "OAuth ") {
		return nil
	}
	m := make(map[string]string)
	// split the values by comma
	splitted := strings.Split(oAuthHeader[5:], ",")
	for _, oauthField := range splitted {
		oauthField := strings.TrimSpace(oauthField)

		// check this is a real oauth field
		if !strings.HasPrefix(oauthField, "oauth_") {
			continue
		}

		splittedField := strings.Split(oauthField, "=")
		if len(splittedField) < 2 {
			continue
		}
		key := splittedField[0]
		val := splittedField[1]

		// ensure val is surrounded by ""
		if !strings.HasPrefix(val, "\"") || !strings.HasSuffix(val, "\"") {
			continue
		}
		val = val[1 : len(val)-1]
		// url unescape
		m[key], _ = url.QueryUnescape(val)
	}
	return m
}

func signatureBase(r *http.Request, oAuthHeaders map[string]string) string {
	// we sort the oauth params alphabetically then normalize it
	orderedParams := make([]string, len(oAuthHeaders))
	var i int
	for k, _ := range oAuthHeaders {
		orderedParams[i] = k
		i++
	}

	sort.Strings(orderedParams)

	stringParams := make([]string, 0)
	for _, param := range orderedParams {
		// remove the oauth_signature
		if param == "oauth_signature" {
			continue
		}
		stringParams = append(stringParams, param+"="+oAuthHeaders[param])
	}

	normalizedParams := strings.Join(stringParams, "&")
	sign := fmt.Sprintf("%s&%s&%s",
		r.Method,
		// TODO: find real scheme
		url.QueryEscape("http://"+r.Host+r.URL.String()),
		url.QueryEscape(normalizedParams))
	return sign
}

// checkRequest is checking the request is OAuth 1.0a valid and check the body_hash
// return the oauth_consumer_key
func checkRequest(r *http.Request, checkBodyHash bool) (consumerKey string, body string, oAuthHeaders map[string]string) {
	// test we have a header Authorization
	if _, ok := r.Header["Authorization"]; !ok {
		return
	}
	oAuthHeaders = splitAuthHeader(r.Header.Get("Authorization"))

	// test for the most needed oauth_ entries
	if _, ok := oAuthHeaders["oauth_consumer_key"]; !ok {
		return
	}
	if _, ok := oAuthHeaders["oauth_timestamp"]; !ok {
		return
	}
	if _, ok := oAuthHeaders["oauth_signature"]; !ok {
		return
	}

	// check for timestamp
	timestamp, err := strconv.Atoi(oAuthHeaders["oauth_timestamp"])
	if err != (nil) {
		return
	}

	diff := int(time.Now().Unix()) - timestamp
	// stupid Abs for int
	if diff < 0 {
		diff = -diff
	}
	// if request is older than 120s
	if diff > 120 {
		return
	}

	// if the method is not GET and the content type is not url encoded
	// we have to check for oauth_body_hash
	if checkBodyHash && r.Method != "GET" && r.Header.Get("Content-Type") != "application/x-www-form-urlencoded" {
		if _, ok := oAuthHeaders["oauth_body_hash"]; !ok {
			return
		}
		hasher := sha1.New()

		// we need to read the body for oauth_body_hash to be calculated
		// but then restore it later for the others middleware to read it
		bodydata, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return
		}
		hasher.Write(bodydata)
		bodySign := base64.StdEncoding.EncodeToString(hasher.Sum(nil))

		body = string(bodydata[:])
		if bodySign != oAuthHeaders["oauth_body_hash"] {
			return
		}
		// escape body hash for later signature regeneration
		oAuthHeaders["oauth_body_hash"] = url.QueryEscape(oAuthHeaders["oauth_body_hash"])
	}
	consumerKey = oAuthHeaders["oauth_consumer_key"]
	return
}

func (o *OAuth1) AuthProtect(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if consumerKey, body, oAuthHeaders := checkRequest(r, o.WithBodyHash); consumerKey != "" {
			// generate base signature string
			sign := signatureBase(r, oAuthHeaders)
			user, consumerSecret := o.CheckerFunc(consumerKey)
			if consumerSecret != "" {
				// in 3 legs should have consumer_key&secret_key
				// in 2 legs only one but keep &
				hashfun := hmac.New(sha1.New, []byte(consumerSecret+"&"))
				hashfun.Write([]byte(sign))
				rawsignature := hashfun.Sum(nil)
				base64signature := make([]byte, base64.StdEncoding.EncodedLen(len(rawsignature)))
				base64.StdEncoding.Encode(base64signature, rawsignature)
				sign = string(base64signature)

				if sign == oAuthHeaders["oauth_signature"] {
					r.Body = nopCloser{bytes.NewBufferString(body)}
					r.URL.User = url.UserPassword(user, "")
					h.ServeHTTP(w, r)
					return
				}
			}
		}
		dh := o.DeniedHandler
		if dh == nil {
			dh = DefaultDeniedHandler
		}
		dh.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package goth

import (
	"fmt"
	"net/http"
	"regexp"
)

import (
	"github.com/gorilla/sessions"
	. "github.com/jroes/goth/user"
	"github.com/jroes/goth/user/gobstore"
)

type AuthHandler struct {
	RoutePath        string
	TemplatePath     string
	AfterSignupPath  string
	AfterSigninPath  string
	AfterSignoutPath string
	SessionSecret    string
	SessionStore     *sessions.CookieStore
	UserStore        UserStore
}

var DefaultAuthHandler = AuthHandler{
	RoutePath:        "/auth/",
	TemplatePath:     "tmpl/",
	AfterSignupPath:  "/",
	AfterSigninPath:  "/",
	AfterSignoutPath: "/",
	SessionSecret:    "change-me-please",
	SessionStore:     sessions.NewCookieStore([]byte("change-me-please")),
	UserStore:        gobstore.NewUserGobStore("users/"),
}

func (handler AuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	actionRegexp := regexp.MustCompile(".*\\/(.*)")
	actionMatches := actionRegexp.FindStringSubmatch(r.URL.Path)
	if actionMatches == nil || len(actionMatches) != 2 {
		fmt.Printf("actionMatches was %q for %s", actionMatches, r.URL.Path)
		http.NotFound(w, r)
		return
	}

	action := actionMatches[1]

	if action == "sign_in" {
		handler.SignInHandler(w, r)
		return
	} else if action == "sign_out" {
		handler.SignOutHandler(w, r)
		return
	} else if action == "sign_up" {
		handler.SignUpHandler(w, r)
		return
	}

	http.NotFound(w, r)
}

func (handler AuthHandler) CurrentUser(r *http.Request) (*User, bool) {
	session, err := handler.SessionStore.Get(r, "goth-session")
	if err != nil {
		panic(err)
	}
	emailHash, ok := session.Values["identifier"]
	if ok {
		user, err := handler.UserStore.FindByHash(emailHash.(string))
		if err != nil {
			panic(fmt.Errorf("Couldn't find user with identifier %s in user store.", emailHash.(string)))
		}
		return user, true
	}
	return &User{}, false
}
<commit_msg>Document public goth interface.<commit_after>// Package goth provides an authentication system for Go web apps.
package goth

import (
	"fmt"
	"net/http"
	"regexp"
)

import (
	"github.com/gorilla/sessions"
	. "github.com/jroes/goth/user"
	"github.com/jroes/goth/user/gobstore"
)

type AuthHandler struct {
	// Where to mount URLs for authentication (e.g. signup, signin)
	RoutePath string
	// Where on disk HTML templates are stored for authentication pages.
	TemplatePath string
	// Where to redirect the user after various authentication operations.
	AfterSignupPath  string
	AfterSigninPath  string
	AfterSignoutPath string
	// This should be set to a string of characters used to encrypt and sign
	// sessions. It should be kept private from any source repositories. You could
	// use os.Getenv() for this and store it in an environment variable.
	SessionSecret string
	SessionStore  *sessions.CookieStore
	UserStore     UserStore
}

var DefaultAuthHandler = AuthHandler{
	RoutePath:        "/auth/",
	TemplatePath:     "tmpl/",
	AfterSignupPath:  "/",
	AfterSigninPath:  "/",
	AfterSignoutPath: "/",
	SessionSecret:    "change-me-please",
	SessionStore:     sessions.NewCookieStore([]byte("change-me-please")),
	UserStore:        gobstore.NewUserGobStore("users/"),
}

// ServeHTTP implements the http.Handler interface to delegate
// authentication-related routing to the proper handler.
func (handler AuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	actionRegexp := regexp.MustCompile(".*\\/(.*)")
	actionMatches := actionRegexp.FindStringSubmatch(r.URL.Path)
	if actionMatches == nil || len(actionMatches) != 2 {
		fmt.Printf("Unexpected request: %s", r.URL.Path)
		http.NotFound(w, r)
		return
	}

	action := actionMatches[1]

	if action == "sign_in" {
		handler.SignInHandler(w, r)
		return
	} else if action == "sign_out" {
		handler.SignOutHandler(w, r)
		return
	} else if action == "sign_up" {
		handler.SignUpHandler(w, r)
		return
	}

	http.NotFound(w, r)
}

// CurrentUser retrieves the User object for the currently logged in user based
// on the request. The first return value is the User object, the second is
// true if a user is logged in. If a user is not logged in, the first return
// value will be an empty User, and the second return value will be false.
func (handler AuthHandler) CurrentUser(r *http.Request) (*User, bool) {
	session, err := handler.SessionStore.Get(r, "goth-session")
	if err != nil {
		panic(err)
	}
	emailHash, ok := session.Values["identifier"]
	if ok {
		user, err := handler.UserStore.FindByHash(emailHash.(string))
		if err != nil {
			panic(fmt.Errorf("Couldn't find user with identifier %s in user store.", emailHash.(string)))
		}
		return user, true
	}
	return &User{}, false
}
<|endoftext|>