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

package walk

import (
	"fmt"
	"math/big"
	"syscall"
	"time"
	"unsafe"
)

import . "github.com/lxn/go-winapi"

type ComboBox struct {
	WidgetBase
	bindingValueProvider         BindingValueProvider
	model                        ListModel
	format                       string
	precision                    int
	itemsResetHandlerHandle      int
	itemChangedHandlerHandle     int
	maxItemTextWidth             int
	prevCurIndex                 int
	selChangeIndex               int
	currentIndexChangedPublisher EventPublisher
	currentIndexProperty         *Property
	valueProperty                *Property
}

func NewComboBox(parent Container) (*ComboBox, error) {
	cb := &ComboBox{prevCurIndex: -1, selChangeIndex: -1, precision: 2}

	if err := InitChildWidget(
		cb,
		parent,
		"COMBOBOX",
		WS_TABSTOP|WS_VISIBLE|WS_VSCROLL|CBS_DROPDOWNLIST,
		0); err != nil {
		return nil, err
	}

	cb.valueProperty = NewProperty(
		"Value",
		func() interface{} {
			index := cb.CurrentIndex()

			if cb.bindingValueProvider == nil || index == -1 {
				return nil
			}

			return cb.bindingValueProvider.BindingValue(index)
		},
		func(v interface{}) error {
			if cb.bindingValueProvider == nil {
				return newError("Data binding is only supported using a model that implements BindingValueProvider.")
			}

			index := -1

			count := cb.model.ItemCount()
			for i := 0; i < count; i++ {
				if cb.bindingValueProvider.BindingValue(i) == v {
					index = i
					break
				}
			}

			return cb.SetCurrentIndex(index)
		},
		cb.CurrentIndexChanged())

	cb.currentIndexProperty = NewProperty(
		"CurrentIndex",
		func() interface{} {
			return cb.CurrentIndex()
		},
		func(v interface{}) error {
			return cb.SetCurrentIndex(v.(int))
		},
		cb.CurrentIndexChanged())

	cb.MustRegisterProperties(cb.valueProperty, cb.currentIndexProperty)

	return cb, nil
}

func (*ComboBox) LayoutFlags() LayoutFlags {
	return GrowableHorz
}

func (cb *ComboBox) MinSizeHint() Size {
	defaultSize := cb.dialogBaseUnitsToPixels(Size{50, 12})

	if cb.model != nil && cb.maxItemTextWidth <= 0 {
		cb.maxItemTextWidth = cb.calculateMaxItemTextWidth()
	}

	// FIXME: Use GetThemePartSize instead of guessing
	w := maxi(defaultSize.Width, cb.maxItemTextWidth+24)
	h := defaultSize.Height + 1

	return Size{w, h}
}

func (cb *ComboBox) SizeHint() Size {
	return cb.MinSizeHint()
}

func (cb *ComboBox) itemString(index int) string {
	switch val := cb.model.Value(index).(type) {
	case string:
		return val

	case time.Time:
		return val.Format(cb.format)

	case *big.Rat:
		return val.FloatString(cb.precision)

	default:
		return fmt.Sprintf(cb.format, val)
	}

	panic("unreachable")
}

func (cb *ComboBox) insertItemAt(index int) error {
	str := cb.itemString(index)
	lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))

	if CB_ERR == cb.SendMessage(CB_INSERTSTRING, uintptr(index), lp) {
		return newError("SendMessage(CB_INSERTSTRING)")
	}

	return nil
}

func (cb *ComboBox) resetItems() error {
	cb.SetSuspended(true)
	defer cb.SetSuspended(false)

	if FALSE == cb.SendMessage(CB_RESETCONTENT, 0, 0) {
		return newError("SendMessage(CB_RESETCONTENT)")
	}

	cb.maxItemTextWidth = 0

	cb.SetCurrentIndex(-1)

	if cb.model == nil {
		return nil
	}

	count := cb.model.ItemCount()

	for i := 0; i < count; i++ {
		if err := cb.insertItemAt(i); err != nil {
			return err
		}
	}

	return nil
}

func (cb *ComboBox) attachModel() {
	itemsResetHandler := func() {
		cb.resetItems()
	}
	cb.itemsResetHandlerHandle = cb.model.ItemsReset().Attach(itemsResetHandler)

	itemChangedHandler := func(index int) {
		if CB_ERR == cb.SendMessage(CB_DELETESTRING, uintptr(index), 0) {
			newError("SendMessage(CB_DELETESTRING)")
		}

		cb.insertItemAt(index)

		cb.SetCurrentIndex(cb.prevCurIndex)
	}
	cb.itemChangedHandlerHandle = cb.model.ItemChanged().Attach(itemChangedHandler)
}

func (cb *ComboBox) detachModel() {
	cb.model.ItemsReset().Detach(cb.itemsResetHandlerHandle)
	cb.model.ItemChanged().Detach(cb.itemChangedHandlerHandle)
}

func (cb *ComboBox) Model() ListModel {
	return cb.model
}

func (cb *ComboBox) SetModel(model ListModel) error {
	if cb.model != nil {
		cb.detachModel()
	}

	cb.model = model
	cb.bindingValueProvider, _ = model.(BindingValueProvider)

	if model != nil {
		cb.attachModel()
	}

	return cb.resetItems()
}

func (cb *ComboBox) Format() string {
	return cb.format
}

func (cb *ComboBox) SetFormat(value string) {
	cb.format = value
}

func (cb *ComboBox) Precision() int {
	return cb.precision
}

func (cb *ComboBox) SetPrecision(value int) {
	cb.precision = value
}

func (cb *ComboBox) calculateMaxItemTextWidth() int {
	hdc := GetDC(cb.hWnd)
	if hdc == 0 {
		newError("GetDC failed")
		return -1
	}
	defer ReleaseDC(cb.hWnd, hdc)

	hFontOld := SelectObject(hdc, HGDIOBJ(cb.Font().handleForDPI(0)))
	defer SelectObject(hdc, hFontOld)

	var maxWidth int

	count := cb.model.ItemCount()
	for i := 0; i < count; i++ {
		var s SIZE
		str := syscall.StringToUTF16(cb.itemString(i))

		if !GetTextExtentPoint32(hdc, &str[0], int32(len(str)-1), &s) {
			newError("GetTextExtentPoint32 failed")
			return -1
		}

		maxWidth = maxi(maxWidth, int(s.CX))
	}

	return maxWidth
}

func (cb *ComboBox) CurrentIndex() int {
	return int(cb.SendMessage(CB_GETCURSEL, 0, 0))
}

func (cb *ComboBox) SetCurrentIndex(value int) error {
	index := int(cb.SendMessage(CB_SETCURSEL, uintptr(value), 0))

	if index != value {
		return newError("invalid index")
	}

	if value != cb.prevCurIndex {
		cb.prevCurIndex = value
		cb.currentIndexChangedPublisher.Publish()
	}

	return nil
}

func (cb *ComboBox) CurrentIndexChanged() *Event {
	return cb.currentIndexChangedPublisher.Event()
}

func (cb *ComboBox) Text() string {
	return widgetText(cb.hWnd)
}

func (cb *ComboBox) SetText(value string) error {
	return setWidgetText(cb.hWnd, value)
}

func (cb *ComboBox) TextSelection() (start, end int) {
	cb.SendMessage(CB_GETEDITSEL, uintptr(unsafe.Pointer(&start)), uintptr(unsafe.Pointer(&end)))
	return
}

func (cb *ComboBox) SetTextSelection(start, end int) {
	cb.SendMessage(CB_SETEDITSEL, 0, uintptr(MAKELONG(uint16(start), uint16(end))))
}

func (cb *ComboBox) WndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_COMMAND:
		code := HIWORD(uint32(wParam))
		selIndex := cb.CurrentIndex()

		switch code {
		case CBN_SELCHANGE:
			cb.selChangeIndex = selIndex

		case CBN_SELENDCANCEL:
			if cb.selChangeIndex != -1 {
				cb.SetCurrentIndex(cb.selChangeIndex)

				cb.selChangeIndex = -1
			}

		case CBN_SELENDOK:
			if selIndex != cb.prevCurIndex {
				cb.currentIndexChangedPublisher.Publish()
				cb.prevCurIndex = selIndex
				return 0
			}

			cb.selChangeIndex = -1
		}
	}

	return cb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
<commit_msg>ComboBox: Preselect single option.<commit_after>// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package walk

import (
	"fmt"
	"math/big"
	"syscall"
	"time"
	"unsafe"
)

import . "github.com/lxn/go-winapi"

type ComboBox struct {
	WidgetBase
	bindingValueProvider         BindingValueProvider
	model                        ListModel
	format                       string
	precision                    int
	itemsResetHandlerHandle      int
	itemChangedHandlerHandle     int
	maxItemTextWidth             int
	prevCurIndex                 int
	selChangeIndex               int
	currentIndexChangedPublisher EventPublisher
	currentIndexProperty         *Property
	valueProperty                *Property
}

func NewComboBox(parent Container) (*ComboBox, error) {
	cb := &ComboBox{prevCurIndex: -1, selChangeIndex: -1, precision: 2}

	if err := InitChildWidget(
		cb,
		parent,
		"COMBOBOX",
		WS_TABSTOP|WS_VISIBLE|WS_VSCROLL|CBS_DROPDOWNLIST,
		0); err != nil {
		return nil, err
	}

	cb.valueProperty = NewProperty(
		"Value",
		func() interface{} {
			index := cb.CurrentIndex()

			if cb.bindingValueProvider == nil || index == -1 {
				return nil
			}

			return cb.bindingValueProvider.BindingValue(index)
		},
		func(v interface{}) error {
			if cb.bindingValueProvider == nil {
				return newError("Data binding is only supported using a model that implements BindingValueProvider.")
			}

			index := -1

			count := cb.model.ItemCount()
			for i := 0; i < count; i++ {
				if cb.bindingValueProvider.BindingValue(i) == v {
					index = i
					break
				}
			}

			return cb.SetCurrentIndex(index)
		},
		cb.CurrentIndexChanged())

	cb.currentIndexProperty = NewProperty(
		"CurrentIndex",
		func() interface{} {
			return cb.CurrentIndex()
		},
		func(v interface{}) error {
			return cb.SetCurrentIndex(v.(int))
		},
		cb.CurrentIndexChanged())

	cb.MustRegisterProperties(cb.valueProperty, cb.currentIndexProperty)

	return cb, nil
}

func (*ComboBox) LayoutFlags() LayoutFlags {
	return GrowableHorz
}

func (cb *ComboBox) MinSizeHint() Size {
	defaultSize := cb.dialogBaseUnitsToPixels(Size{50, 12})

	if cb.model != nil && cb.maxItemTextWidth <= 0 {
		cb.maxItemTextWidth = cb.calculateMaxItemTextWidth()
	}

	// FIXME: Use GetThemePartSize instead of guessing
	w := maxi(defaultSize.Width, cb.maxItemTextWidth+24)
	h := defaultSize.Height + 1

	return Size{w, h}
}

func (cb *ComboBox) SizeHint() Size {
	return cb.MinSizeHint()
}

func (cb *ComboBox) itemString(index int) string {
	switch val := cb.model.Value(index).(type) {
	case string:
		return val

	case time.Time:
		return val.Format(cb.format)

	case *big.Rat:
		return val.FloatString(cb.precision)

	default:
		return fmt.Sprintf(cb.format, val)
	}

	panic("unreachable")
}

func (cb *ComboBox) insertItemAt(index int) error {
	str := cb.itemString(index)
	lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))

	if CB_ERR == cb.SendMessage(CB_INSERTSTRING, uintptr(index), lp) {
		return newError("SendMessage(CB_INSERTSTRING)")
	}

	return nil
}

func (cb *ComboBox) resetItems() error {
	cb.SetSuspended(true)
	defer cb.SetSuspended(false)

	if FALSE == cb.SendMessage(CB_RESETCONTENT, 0, 0) {
		return newError("SendMessage(CB_RESETCONTENT)")
	}

	cb.maxItemTextWidth = 0

	cb.SetCurrentIndex(-1)

	if cb.model == nil {
		return nil
	}

	count := cb.model.ItemCount()

	for i := 0; i < count; i++ {
		if err := cb.insertItemAt(i); err != nil {
			return err
		}
	}

	return nil
}

func (cb *ComboBox) attachModel() {
	itemsResetHandler := func() {
		cb.resetItems()
	}
	cb.itemsResetHandlerHandle = cb.model.ItemsReset().Attach(itemsResetHandler)

	itemChangedHandler := func(index int) {
		if CB_ERR == cb.SendMessage(CB_DELETESTRING, uintptr(index), 0) {
			newError("SendMessage(CB_DELETESTRING)")
		}

		cb.insertItemAt(index)

		cb.SetCurrentIndex(cb.prevCurIndex)
	}
	cb.itemChangedHandlerHandle = cb.model.ItemChanged().Attach(itemChangedHandler)
}

func (cb *ComboBox) detachModel() {
	cb.model.ItemsReset().Detach(cb.itemsResetHandlerHandle)
	cb.model.ItemChanged().Detach(cb.itemChangedHandlerHandle)
}

func (cb *ComboBox) Model() ListModel {
	return cb.model
}

func (cb *ComboBox) SetModel(model ListModel) error {
	if cb.model != nil {
		cb.detachModel()
	}

	cb.model = model
	cb.bindingValueProvider, _ = model.(BindingValueProvider)

	if model != nil {
		cb.attachModel()
	}

	if err := cb.resetItems(); err != nil {
		return err
	}

	if model != nil && model.ItemCount() == 1 {
		cb.SetCurrentIndex(0)
	}

	return nil
}

func (cb *ComboBox) Format() string {
	return cb.format
}

func (cb *ComboBox) SetFormat(value string) {
	cb.format = value
}

func (cb *ComboBox) Precision() int {
	return cb.precision
}

func (cb *ComboBox) SetPrecision(value int) {
	cb.precision = value
}

func (cb *ComboBox) calculateMaxItemTextWidth() int {
	hdc := GetDC(cb.hWnd)
	if hdc == 0 {
		newError("GetDC failed")
		return -1
	}
	defer ReleaseDC(cb.hWnd, hdc)

	hFontOld := SelectObject(hdc, HGDIOBJ(cb.Font().handleForDPI(0)))
	defer SelectObject(hdc, hFontOld)

	var maxWidth int

	count := cb.model.ItemCount()
	for i := 0; i < count; i++ {
		var s SIZE
		str := syscall.StringToUTF16(cb.itemString(i))

		if !GetTextExtentPoint32(hdc, &str[0], int32(len(str)-1), &s) {
			newError("GetTextExtentPoint32 failed")
			return -1
		}

		maxWidth = maxi(maxWidth, int(s.CX))
	}

	return maxWidth
}

func (cb *ComboBox) CurrentIndex() int {
	return int(cb.SendMessage(CB_GETCURSEL, 0, 0))
}

func (cb *ComboBox) SetCurrentIndex(value int) error {
	index := int(cb.SendMessage(CB_SETCURSEL, uintptr(value), 0))

	if index != value {
		return newError("invalid index")
	}

	if value != cb.prevCurIndex {
		cb.prevCurIndex = value
		cb.currentIndexChangedPublisher.Publish()
	}

	return nil
}

func (cb *ComboBox) CurrentIndexChanged() *Event {
	return cb.currentIndexChangedPublisher.Event()
}

func (cb *ComboBox) Text() string {
	return widgetText(cb.hWnd)
}

func (cb *ComboBox) SetText(value string) error {
	return setWidgetText(cb.hWnd, value)
}

func (cb *ComboBox) TextSelection() (start, end int) {
	cb.SendMessage(CB_GETEDITSEL, uintptr(unsafe.Pointer(&start)), uintptr(unsafe.Pointer(&end)))
	return
}

func (cb *ComboBox) SetTextSelection(start, end int) {
	cb.SendMessage(CB_SETEDITSEL, 0, uintptr(MAKELONG(uint16(start), uint16(end))))
}

func (cb *ComboBox) WndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_COMMAND:
		code := HIWORD(uint32(wParam))
		selIndex := cb.CurrentIndex()

		switch code {
		case CBN_SELCHANGE:
			cb.selChangeIndex = selIndex

		case CBN_SELENDCANCEL:
			if cb.selChangeIndex != -1 {
				cb.SetCurrentIndex(cb.selChangeIndex)

				cb.selChangeIndex = -1
			}

		case CBN_SELENDOK:
			if selIndex != cb.prevCurIndex {
				cb.currentIndexChangedPublisher.Publish()
				cb.prevCurIndex = selIndex
				return 0
			}

			cb.selChangeIndex = -1
		}
	}

	return cb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
<|endoftext|>


<commit_before>package lfs

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/rubyist/tracerx"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
)

const (
	mediaType = "application/vnd.git-lfs+json; charset-utf-8"
)

// The apiEvent* statuses (and the apiEvent channel) are used by
// UploadQueue to know when it is OK to process uploads concurrently.
const (
	apiEventFail = iota
	apiEventSuccess
)

var (
	lfsMediaTypeRE             = regexp.MustCompile(`\Aapplication/vnd\.git\-lfs\+json(;|\z)`)
	jsonMediaTypeRE            = regexp.MustCompile(`\Aapplication/json(;|\z)`)
	objectRelationDoesNotExist = errors.New("relation does not exist")
	hiddenHeaders              = map[string]bool{
		"Authorization": true,
	}

	defaultErrors = map[int]string{
		400: "Client error: %s",
		401: "Authorization error: %s\nCheck that you have proper access to the repository",
		403: "Authorization error: %s\nCheck that you have proper access to the repository",
		404: "Repository or object not found: %s\nCheck that it exists and that you have proper access to it",
		500: "Server error: %s",
	}

	apiEvent = make(chan int)
)

type objectResource struct {
	Oid   string                   `json:"oid,omitempty"`
	Size  int64                    `json:"size,omitempty"`
	Links map[string]*linkRelation `json:"_links,omitempty"`
}

func (o *objectResource) NewRequest(relation, method string) (*http.Request, Creds, error) {
	rel, ok := o.Rel(relation)
	if !ok {
		return nil, nil, objectRelationDoesNotExist
	}

	req, creds, err := newClientRequest(method, rel.Href)
	if err != nil {
		return nil, nil, err
	}

	for h, v := range rel.Header {
		req.Header.Set(h, v)
	}

	return req, creds, nil
}

func (o *objectResource) Rel(name string) (*linkRelation, bool) {
	if o.Links == nil {
		return nil, false
	}

	rel, ok := o.Links[name]
	return rel, ok
}

type linkRelation struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

type ClientError struct {
	Message          string `json:"message"`
	DocumentationUrl string `json:"documentation_url,omitempty"`
	RequestId        string `json:"request_id,omitempty"`
}

func (e *ClientError) Error() string {
	msg := e.Message
	if len(e.DocumentationUrl) > 0 {
		msg += "\nDocs: " + e.DocumentationUrl
	}
	if len(e.RequestId) > 0 {
		msg += "\nRequest ID: " + e.RequestId
	}
	return msg
}

func Download(oid string) (io.ReadCloser, int64, *WrappedError) {
	req, creds, err := newApiRequest("GET", oid)
	if err != nil {
		return nil, 0, Error(err)
	}

	res, obj, wErr := doApiRequest(req, creds)
	if wErr != nil {
		return nil, 0, wErr
	}

	req, creds, err = obj.NewRequest("download", "GET")
	if err != nil {
		return nil, 0, Error(err)
	}

	res, wErr = doHttpRequest(req, creds)
	if wErr != nil {
		return nil, 0, wErr
	}

	return res.Body, res.ContentLength, nil
}

type byteCloser struct {
	*bytes.Reader
}

func (b *byteCloser) Close() error {
	return nil
}

func Upload(oidPath, filename string, cb CopyCallback) *WrappedError {
	oid := filepath.Base(oidPath)
	file, err := os.Open(oidPath)
	if err != nil {
		return Error(err)
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return Error(err)
	}

	reqObj := &objectResource{
		Oid:  oid,
		Size: stat.Size(),
	}

	by, err := json.Marshal(reqObj)
	if err != nil {
		return Error(err)
	}

	req, creds, err := newApiRequest("POST", oid)
	if err != nil {
		return Error(err)
	}

	req.Header.Set("Content-Type", mediaType)
	req.Header.Set("Content-Length", strconv.Itoa(len(by)))
	req.ContentLength = int64(len(by))
	req.Body = &byteCloser{bytes.NewReader(by)}

	tracerx.Printf("api: uploading %s (%s)", filename, oid)
	res, obj, wErr := doApiRequest(req, creds)
	if wErr != nil {
		sendApiEvent(apiEventFail)
		return wErr
	}

	sendApiEvent(apiEventSuccess)

	reader := &CallbackReader{
		C:         cb,
		TotalSize: reqObj.Size,
		Reader:    file,
	}

	if res.StatusCode == 200 {
		// Drain the reader to update any progress bars
		io.Copy(ioutil.Discard, reader)
		return nil
	}

	req, creds, err = obj.NewRequest("upload", "PUT")
	if err != nil {
		return Error(err)
	}

	if len(req.Header.Get("Content-Type")) == 0 {
		req.Header.Set("Content-Type", "application/octet-stream")
	}
	req.Header.Set("Content-Length", strconv.FormatInt(reqObj.Size, 10))
	req.ContentLength = reqObj.Size

	req.Body = ioutil.NopCloser(reader)

	res, wErr = doHttpRequest(req, creds)
	if wErr != nil {
		return wErr
	}

	if res.StatusCode > 299 {
		return Errorf(nil, "Invalid status for %s %s: %d", req.Method, req.URL, res.StatusCode)
	}

	io.Copy(ioutil.Discard, res.Body)
	res.Body.Close()

	req, creds, err = obj.NewRequest("verify", "POST")
	if err == objectRelationDoesNotExist {
		return nil
	} else if err != nil {
		return Error(err)
	}

	req.Header.Set("Content-Type", mediaType)
	req.Header.Set("Content-Length", strconv.Itoa(len(by)))
	req.ContentLength = int64(len(by))
	req.Body = ioutil.NopCloser(bytes.NewReader(by))
	res, wErr = doHttpRequest(req, creds)

	io.Copy(ioutil.Discard, res.Body)
	res.Body.Close()

	return wErr
}

func doHttpRequest(req *http.Request, creds Creds) (*http.Response, *WrappedError) {
	res, err := DoHTTP(Config, req)

	var wErr *WrappedError

	if err != nil {
		wErr = Errorf(err, "Error for %s %s", res.Request.Method, res.Request.URL)
	} else {
		if creds != nil {
			saveCredentials(creds, res)
		}

		wErr = handleResponse(res)
	}

	if wErr != nil {
		if res != nil {
			setErrorResponseContext(wErr, res)
		} else {
			setErrorRequestContext(wErr, req)
		}
	}

	return res, wErr
}

func doApiRequestWithRedirects(req *http.Request, creds Creds, via []*http.Request) (*http.Response, *WrappedError) {
	res, wErr := doHttpRequest(req, creds)
	if wErr != nil {
		return nil, wErr
	}

	if res.StatusCode == 307 {
		redirectedReq, redirectedCreds, err := newClientRequest(req.Method, res.Header.Get("Location"))
		if err != nil {
			return nil, Errorf(err, err.Error())
		}

		via = append(via, req)
		seeker, ok := req.Body.(io.Seeker)
		if !ok {
			return nil, Errorf(nil, "Request body needs to be an io.Seeker to handle redirects.")
		}

		if _, err := seeker.Seek(0, 0); err != nil {
			return nil, Error(err)
		}
		redirectedReq.Body = req.Body
		redirectedReq.ContentLength = req.ContentLength

		if err = checkRedirect(redirectedReq, via); err != nil {
			return nil, Errorf(err, err.Error())
		}

		return doApiRequestWithRedirects(redirectedReq, redirectedCreds, via)
	}

	return res, nil
}

func doApiRequest(req *http.Request, creds Creds) (*http.Response, *objectResource, *WrappedError) {
	via := make([]*http.Request, 0, 4)
	res, wErr := doApiRequestWithRedirects(req, creds, via)
	if wErr != nil {
		return nil, nil, wErr
	}

	obj := &objectResource{}
	wErr = decodeApiResponse(res, obj)

	if wErr != nil {
		setErrorResponseContext(wErr, res)
		return nil, nil, wErr
	}

	return res, obj, nil
}

func handleResponse(res *http.Response) *WrappedError {
	if res.StatusCode < 400 {
		return nil
	}

	defer func() {
		io.Copy(ioutil.Discard, res.Body)
		res.Body.Close()
	}()

	cliErr := &ClientError{}
	wErr := decodeApiResponse(res, cliErr)
	if wErr == nil {
		if len(cliErr.Message) == 0 {
			wErr = defaultError(res)
		} else {
			wErr = Error(cliErr)
		}
	}

	wErr.Panic = res.StatusCode > 499 && res.StatusCode != 501 && res.StatusCode != 509
	return wErr
}

func decodeApiResponse(res *http.Response, obj interface{}) *WrappedError {
	ctype := res.Header.Get("Content-Type")
	if !(lfsMediaTypeRE.MatchString(ctype) || jsonMediaTypeRE.MatchString(ctype)) {
		return nil
	}

	err := json.NewDecoder(res.Body).Decode(obj)
	io.Copy(ioutil.Discard, res.Body)
	res.Body.Close()

	if err != nil {
		return Errorf(err, "Unable to parse HTTP response for %s %s", res.Request.Method, res.Request.URL)
	}

	return nil
}

func defaultError(res *http.Response) *WrappedError {
	var msgFmt string

	if f, ok := defaultErrors[res.StatusCode]; ok {
		msgFmt = f
	} else if res.StatusCode < 500 {
		msgFmt = defaultErrors[400] + fmt.Sprintf(" from HTTP %d", res.StatusCode)
	} else {
		msgFmt = defaultErrors[500] + fmt.Sprintf(" from HTTP %d", res.StatusCode)
	}

	return Error(fmt.Errorf(msgFmt, res.Request.URL))
}

func saveCredentials(creds Creds, res *http.Response) {
	if creds == nil {
		return
	}

	if res.StatusCode < 300 {
		execCreds(creds, "approve")
	} else if res.StatusCode == 401 {
		execCreds(creds, "reject")
	}
}

func newApiRequest(method, oid string) (*http.Request, Creds, error) {
	endpoint := Config.Endpoint()
	objectOid := oid
	operation := "download"
	if method == "POST" {
		objectOid = ""
		operation = "upload"
	}

	res, err := sshAuthenticate(endpoint, operation, oid)
	if err != nil {
		tracerx.Printf("ssh: attempted with %s.  Error: %s",
			endpoint.SshUserAndHost, err.Error(),
		)
	}

	if len(res.Href) > 0 {
		endpoint.Url = res.Href
	}

	u, err := ObjectUrl(endpoint, objectOid)
	if err != nil {
		return nil, nil, err
	}

	req, creds, err := newClientRequest(method, u.String())
	if err == nil {
		req.Header.Set("Accept", mediaType)
		if res.Header != nil {
			for key, value := range res.Header {
				req.Header.Set(key, value)
			}
		}
	}
	return req, creds, err
}

func newClientRequest(method, rawurl string) (*http.Request, Creds, error) {
	req, err := http.NewRequest(method, rawurl, nil)
	if err != nil {
		return req, nil, err
	}

	req.Header.Set("User-Agent", UserAgent)
	creds, err := getCreds(req)
	return req, creds, err
}

func getCreds(req *http.Request) (Creds, error) {
	if len(req.Header.Get("Authorization")) > 0 {
		return nil, nil
	}

	apiUrl, err := Config.ObjectUrl("")
	if err != nil {
		return nil, err
	}

	if req.URL.Scheme == apiUrl.Scheme &&
		req.URL.Host == apiUrl.Host {
		creds, err := credentials(req.URL)
		if err != nil {
			return nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return creds, nil
	}

	return nil, nil
}

func setErrorRequestContext(err *WrappedError, req *http.Request) {
	err.Set("Endpoint", Config.Endpoint().Url)
	err.Set("URL", fmt.Sprintf("%s %s", req.Method, req.URL.String()))
	setErrorHeaderContext(err, "Response", req.Header)
}

func setErrorResponseContext(err *WrappedError, res *http.Response) {
	err.Set("Status", res.Status)
	setErrorHeaderContext(err, "Request", res.Header)
	setErrorRequestContext(err, res.Request)
}

func setErrorHeaderContext(err *WrappedError, prefix string, head http.Header) {
	for key, _ := range head {
		contextKey := fmt.Sprintf("%s:%s", prefix, key)
		if _, skip := hiddenHeaders[key]; skip {
			err.Set(contextKey, "--")
		} else {
			err.Set(contextKey, head.Get(key))
		}
	}
}

func sendApiEvent(event int) {
	select {
	case apiEvent <- event:
	default:
	}
}
<commit_msg>ンンー ンンンン ンーンン<commit_after>package lfs

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/rubyist/tracerx"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
)

const (
	mediaType = "application/vnd.git-lfs+json; charset-utf-8"
)

// The apiEvent* statuses (and the apiEvent channel) are used by
// UploadQueue to know when it is OK to process uploads concurrently.
const (
	apiEventFail = iota
	apiEventSuccess
)

var (
	lfsMediaTypeRE             = regexp.MustCompile(`\Aapplication/vnd\.git\-lfs\+json(;|\z)`)
	jsonMediaTypeRE            = regexp.MustCompile(`\Aapplication/json(;|\z)`)
	objectRelationDoesNotExist = errors.New("relation does not exist")
	hiddenHeaders              = map[string]bool{
		"Authorization": true,
	}

	defaultErrors = map[int]string{
		400: "Client error: %s",
		401: "Authorization error: %s\nCheck that you have proper access to the repository",
		403: "Authorization error: %s\nCheck that you have proper access to the repository",
		404: "Repository or object not found: %s\nCheck that it exists and that you have proper access to it",
		500: "Server error: %s",
	}

	apiEvent = make(chan int)
)

type objectResource struct {
	Oid   string                   `json:"oid,omitempty"`
	Size  int64                    `json:"size,omitempty"`
	Links map[string]*linkRelation `json:"_links,omitempty"`
}

func (o *objectResource) NewRequest(relation, method string) (*http.Request, Creds, error) {
	rel, ok := o.Rel(relation)
	if !ok {
		return nil, nil, objectRelationDoesNotExist
	}

	req, creds, err := newClientRequest(method, rel.Href)
	if err != nil {
		return nil, nil, err
	}

	for h, v := range rel.Header {
		req.Header.Set(h, v)
	}

	return req, creds, nil
}

func (o *objectResource) Rel(name string) (*linkRelation, bool) {
	if o.Links == nil {
		return nil, false
	}

	rel, ok := o.Links[name]
	return rel, ok
}

type linkRelation struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

type ClientError struct {
	Message          string `json:"message"`
	DocumentationUrl string `json:"documentation_url,omitempty"`
	RequestId        string `json:"request_id,omitempty"`
}

func (e *ClientError) Error() string {
	msg := e.Message
	if len(e.DocumentationUrl) > 0 {
		msg += "\nDocs: " + e.DocumentationUrl
	}
	if len(e.RequestId) > 0 {
		msg += "\nRequest ID: " + e.RequestId
	}
	return msg
}

func Download(oid string) (io.ReadCloser, int64, *WrappedError) {
	req, creds, err := newApiRequest("GET", oid)
	if err != nil {
		return nil, 0, Error(err)
	}

	res, obj, wErr := doApiRequest(req, creds)
	if wErr != nil {
		return nil, 0, wErr
	}

	req, creds, err = obj.NewRequest("download", "GET")
	if err != nil {
		return nil, 0, Error(err)
	}

	res, wErr = doHttpRequest(req, creds)
	if wErr != nil {
		return nil, 0, wErr
	}

	return res.Body, res.ContentLength, nil
}

type byteCloser struct {
	*bytes.Reader
}

func (b *byteCloser) Close() error {
	return nil
}

func Upload(oidPath, filename string, cb CopyCallback) *WrappedError {
	oid := filepath.Base(oidPath)
	file, err := os.Open(oidPath)
	if err != nil {
		return Error(err)
	}
	defer file.Close()

	stat, err := file.Stat()
	if err != nil {
		return Error(err)
	}

	reqObj := &objectResource{
		Oid:  oid,
		Size: stat.Size(),
	}

	by, err := json.Marshal(reqObj)
	if err != nil {
		return Error(err)
	}

	req, creds, err := newApiRequest("POST", oid)
	if err != nil {
		return Error(err)
	}

	req.Header.Set("Content-Type", mediaType)
	req.Header.Set("Content-Length", strconv.Itoa(len(by)))
	req.ContentLength = int64(len(by))
	req.Body = &byteCloser{bytes.NewReader(by)}

	tracerx.Printf("api: uploading %s (%s)", filename, oid)
	res, obj, wErr := doApiRequest(req, creds)
	if wErr != nil {
		sendApiEvent(apiEventFail)
		return wErr
	}

	sendApiEvent(apiEventSuccess)

	reader := &CallbackReader{
		C:         cb,
		TotalSize: reqObj.Size,
		Reader:    file,
	}

	if res.StatusCode == 200 {
		// Drain the reader to update any progress bars
		io.Copy(ioutil.Discard, reader)
		return nil
	}

	req, creds, err = obj.NewRequest("upload", "PUT")
	if err != nil {
		return Error(err)
	}

	if len(req.Header.Get("Content-Type")) == 0 {
		req.Header.Set("Content-Type", "application/octet-stream")
	}
	req.Header.Set("Content-Length", strconv.FormatInt(reqObj.Size, 10))
	req.ContentLength = reqObj.Size

	req.Body = ioutil.NopCloser(reader)

	res, wErr = doHttpRequest(req, creds)
	if wErr != nil {
		return wErr
	}

	if res.StatusCode > 299 {
		return Errorf(nil, "Invalid status for %s %s: %d", req.Method, req.URL, res.StatusCode)
	}

	io.Copy(ioutil.Discard, res.Body)
	res.Body.Close()

	req, creds, err = obj.NewRequest("verify", "POST")
	if err == objectRelationDoesNotExist {
		return nil
	} else if err != nil {
		return Error(err)
	}

	req.Header.Set("Content-Type", mediaType)
	req.Header.Set("Content-Length", strconv.Itoa(len(by)))
	req.ContentLength = int64(len(by))
	req.Body = ioutil.NopCloser(bytes.NewReader(by))
	res, wErr = doHttpRequest(req, creds)

	io.Copy(ioutil.Discard, res.Body)
	res.Body.Close()

	return wErr
}

func doHttpRequest(req *http.Request, creds Creds) (*http.Response, *WrappedError) {
	res, err := DoHTTP(Config, req)

	var wErr *WrappedError

	if err != nil {
		wErr = Errorf(err, "Error for %s %s", res.Request.Method, res.Request.URL)
	} else {
		if creds != nil {
			saveCredentials(creds, res)
		}

		wErr = handleResponse(res)
	}

	if wErr != nil {
		if res != nil {
			setErrorResponseContext(wErr, res)
		} else {
			setErrorRequestContext(wErr, req)
		}
	}

	return res, wErr
}

func doApiRequestWithRedirects(req *http.Request, creds Creds, via []*http.Request) (*http.Response, *WrappedError) {
	res, wErr := doHttpRequest(req, creds)
	if wErr != nil {
		return nil, wErr
	}

	if res.StatusCode == 307 {
		redirectedReq, redirectedCreds, err := newClientRequest(req.Method, res.Header.Get("Location"))
		if err != nil {
			return nil, Errorf(err, err.Error())
		}

		via = append(via, req)
		seeker, ok := req.Body.(io.Seeker)
		if !ok {
			return nil, Errorf(nil, "Request body needs to be an io.Seeker to handle redirects.")
		}

		if _, err := seeker.Seek(0, 0); err != nil {
			return nil, Error(err)
		}
		redirectedReq.Body = req.Body
		redirectedReq.ContentLength = req.ContentLength

		if err = checkRedirect(redirectedReq, via); err != nil {
			return nil, Errorf(err, err.Error())
		}

		return doApiRequestWithRedirects(redirectedReq, redirectedCreds, via)
	}

	return res, nil
}

func doApiRequest(req *http.Request, creds Creds) (*http.Response, *objectResource, *WrappedError) {
	via := make([]*http.Request, 0, 4)
	res, wErr := doApiRequestWithRedirects(req, creds, via)
	if wErr != nil {
		return nil, nil, wErr
	}

	obj := &objectResource{}
	wErr = decodeApiResponse(res, obj)

	if wErr != nil {
		setErrorResponseContext(wErr, res)
		return nil, nil, wErr
	}

	return res, obj, nil
}

func handleResponse(res *http.Response) *WrappedError {
	if res.StatusCode < 400 {
		return nil
	}

	defer func() {
		io.Copy(ioutil.Discard, res.Body)
		res.Body.Close()
	}()

	cliErr := &ClientError{}
	wErr := decodeApiResponse(res, cliErr)
	if wErr == nil {
		if len(cliErr.Message) == 0 {
			wErr = defaultError(res)
		} else {
			wErr = Error(cliErr)
		}
	}

	wErr.Panic = res.StatusCode > 499 && res.StatusCode != 501 && res.StatusCode != 509
	return wErr
}

func decodeApiResponse(res *http.Response, obj interface{}) *WrappedError {
	ctype := res.Header.Get("Content-Type")
	if !(lfsMediaTypeRE.MatchString(ctype) || jsonMediaTypeRE.MatchString(ctype)) {
		return nil
	}

	err := json.NewDecoder(res.Body).Decode(obj)
	io.Copy(ioutil.Discard, res.Body)
	res.Body.Close()

	if err != nil {
		return Errorf(err, "Unable to parse HTTP response for %s %s", res.Request.Method, res.Request.URL)
	}

	return nil
}

func defaultError(res *http.Response) *WrappedError {
	var msgFmt string

	if f, ok := defaultErrors[res.StatusCode]; ok {
		msgFmt = f
	} else if res.StatusCode < 500 {
		msgFmt = defaultErrors[400] + fmt.Sprintf(" from HTTP %d", res.StatusCode)
	} else {
		msgFmt = defaultErrors[500] + fmt.Sprintf(" from HTTP %d", res.StatusCode)
	}

	return Error(fmt.Errorf(msgFmt, res.Request.URL))
}

func saveCredentials(creds Creds, res *http.Response) {
	if creds == nil {
		return
	}

	if res.StatusCode < 300 {
		execCreds(creds, "approve")
	} else if res.StatusCode == 401 {
		execCreds(creds, "reject")
	}
}

func newApiRequest(method, oid string) (*http.Request, Creds, error) {
	endpoint := Config.Endpoint()
	objectOid := oid
	operation := "download"
	if method == "POST" {
		objectOid = ""
		operation = "upload"
	}

	res, err := sshAuthenticate(endpoint, operation, oid)
	if err != nil {
		tracerx.Printf("ssh: attempted with %s.  Error: %s",
			endpoint.SshUserAndHost, err.Error(),
		)
	}

	if len(res.Href) > 0 {
		endpoint.Url = res.Href
	}

	u, err := ObjectUrl(endpoint, objectOid)
	if err != nil {
		return nil, nil, err
	}

	req, creds, err := newClientRequest(method, u.String())
	if err != nil {
		return nil, nil, err
	}

	req.Header.Set("Accept", mediaType)
	if res.Header != nil {
		for key, value := range res.Header {
			req.Header.Set(key, value)
		}
	}

	return req, creds, nil
}

func newClientRequest(method, rawurl string) (*http.Request, Creds, error) {
	req, err := http.NewRequest(method, rawurl, nil)
	if err != nil {
		return req, nil, err
	}

	req.Header.Set("User-Agent", UserAgent)
	creds, err := getCreds(req)
	return req, creds, err
}

func getCreds(req *http.Request) (Creds, error) {
	if len(req.Header.Get("Authorization")) > 0 {
		return nil, nil
	}

	apiUrl, err := Config.ObjectUrl("")
	if err != nil {
		return nil, err
	}

	if req.URL.Scheme == apiUrl.Scheme &&
		req.URL.Host == apiUrl.Host {
		creds, err := credentials(req.URL)
		if err != nil {
			return nil, err
		}

		token := fmt.Sprintf("%s:%s", creds["username"], creds["password"])
		auth := "Basic " + base64.URLEncoding.EncodeToString([]byte(token))
		req.Header.Set("Authorization", auth)
		return creds, nil
	}

	return nil, nil
}

func setErrorRequestContext(err *WrappedError, req *http.Request) {
	err.Set("Endpoint", Config.Endpoint().Url)
	err.Set("URL", fmt.Sprintf("%s %s", req.Method, req.URL.String()))
	setErrorHeaderContext(err, "Response", req.Header)
}

func setErrorResponseContext(err *WrappedError, res *http.Response) {
	err.Set("Status", res.Status)
	setErrorHeaderContext(err, "Request", res.Header)
	setErrorRequestContext(err, res.Request)
}

func setErrorHeaderContext(err *WrappedError, prefix string, head http.Header) {
	for key, _ := range head {
		contextKey := fmt.Sprintf("%s:%s", prefix, key)
		if _, skip := hiddenHeaders[key]; skip {
			err.Set(contextKey, "--")
		} else {
			err.Set(contextKey, head.Get(key))
		}
	}
}

func sendApiEvent(event int) {
	select {
	case apiEvent <- event:
	default:
	}
}
<|endoftext|>


<commit_before>/*
	This file is part of LogHub.

	LogHub is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	LogHub 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with LogHub.  If not, see <http://www.gnu.org/licenses/>.
*/
package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strings"
	"sync/atomic"
	"time"
)

var logFileCloseDelay = time.Second * 10

type LogManagerStat struct {
	Capacity int64
	Size     int64
}

type LogManager interface {
	Logger

	Close()
	Stat() *LogManagerStat
}

type defaultLogManager struct {
	home      string
	writeChan chan *LogEntry
	readChan  chan *LogQuery
	closeChan chan *logClose
}

func NewDefaultLogManager(home string) LogManager {
	var mg = &defaultLogManager{home, make(chan *LogEntry), make(chan *LogQuery), make(chan *logClose)}

	go mg.run()

	return mg
}

func (mg *defaultLogManager) WriteLog(ent *LogEntry) {
	mg.writeChan <- ent
}

func (mg *defaultLogManager) ReadLog(q *LogQuery) {
	mg.readChan <- q
}

func (mg *defaultLogManager) Stat() *LogManagerStat {
	//TODO: Implement.
	return nil
}

func (mg *defaultLogManager) Close() {
	close(mg.writeChan)
	close(mg.readChan)

	ack := make(chan bool)
	closeCmd := &logClose{ack}

	mg.closeChan <- closeCmd
	<-ack

	close(mg.closeChan)
}

func getSourceDirName(source string) string {
	return base64.URLEncoding.EncodeToString([]byte(source))
}

func getSourceNameByDir(dir string) (string, error) {
	if name, err := base64.URLEncoding.DecodeString(dir); err == nil {
		return string(name), nil
	} else {
		return "", err
	}
}

func getFileNameForTimestamp(timestamp int64) string {
	t := time.Unix(0, timestamp)
	return fmt.Sprintf("%d.%d.%d.%d", t.Year(), int(t.Month()), t.Day(), t.Hour())
}

func getLogFileNameForEntry(entry *LogEntry) string {
	return getSourceDirName(entry.Source) + "/" + getFileNameForTimestamp(entry.Timestamp)
}

func initLogManager(home string) (logSources map[string]bool, initialized bool) {
	initialized = false
	logSources = make(map[string]bool, 0)

	if homeDir, err := os.Open(home); err == nil {
		if dirnames, err := homeDir.Readdirnames(0); err == nil {
			for _, dir := range dirnames {
				if src, err := getSourceNameByDir(dir); err == nil {
					logSources[src] = true
				}
			}

			initialized = true
		} else {
			println("Failed to initialize log manager:", err.Error())
		}
	} else {
		println("Failed to initialize log manager:", err.Error())
	}

	return
}

func getLogFileNamesForRange(sources []string, minTimestamp int64, maxTimestamp int64, fileNames chan string) {
	minHour := time.Unix(0, minTimestamp).Truncate(time.Hour).UnixNano()
	maxHour := time.Unix(0, maxTimestamp).Truncate(time.Hour).UnixNano()

	for _, src := range sources {
		for curTs := minHour; curTs <= maxHour; curTs += int64(time.Hour) {
			fileNames <- getSourceDirName(src) + "/" + getFileNameForTimestamp(curTs)
		}
	}

	close(fileNames)
}

func (mg *defaultLogManager) run() {
	logSources, initialized := initLogManager(mg.home)
	openLogFiles := make(map[string]*LogFile)
	closeLogFileChan := make(chan string)
	closeLogFileChanClosed := new(int32)

	waitAndCloseFile := func(fileName string) {
		<-time.After(logFileCloseDelay)

		if atomic.LoadInt32(closeLogFileChanClosed) == 0 {
			closeLogFileChan <- fileName
		}
	}

	getLogFile := func(fileName string, create bool) (*LogFile, error) {
		if logFile, found := openLogFiles[fileName]; !found {
			srcDir := mg.home + "/" + fileName[0:strings.LastIndex(fileName, "/")]

			if srcDirStat, err := os.Stat(srcDir); err != nil {
				if os.IsNotExist(err) && create {
					if err = os.Mkdir(srcDir, 0777); err != nil {
						return nil, err
					}
				} else {
					return nil, err
				}
			} else {
				if !srcDirStat.IsDir() {
					return nil, errors.New("Source directory " + srcDir + " is not a directory")
				}
			}

			if logFile, err := OpenLogFile(mg.home+"/"+fileName, create); err == nil {
				openLogFiles[fileName] = logFile

				go waitAndCloseFile(fileName)

				return logFile, nil
			} else {
				return nil, err
			}
		} else {
			return logFile, nil
		}
	}

	queryLogSources := func(sources []string, q *LogQuery) {
		fileNames := make(chan string)

		go getLogFileNamesForRange(sources, q.From, q.To, fileNames)

		var results chan *LogEntry = nil

		for fileName := range fileNames {
			if logFile, err := getLogFile(fileName, false); err == nil {
				subQuery := *q
				subQuery.Result = make(chan *LogEntry)

				logFile.ReadLog(&subQuery)

				if results == nil {
					results = subQuery.Result
				} else {
					merged := make(chan *LogEntry)
					go MergeLogs(results, subQuery.Result, merged)
					results = merged
				}
			} else {
				if !os.IsNotExist(err) {
					println("Failed to get log file:", err.Error())
				}
			}
		}

		go func() {
			if results != nil {
				for ent := range results {
					q.Result <- ent
				}
			}

			close(q.Result)
		}()
	}

	onWrite := func(ent *LogEntry) {
		if initialized {
			ent.Timestamp = time.Now().UnixNano()
			logFileToWrite := getLogFileNameForEntry(ent)

			if logFile, err := getLogFile(logFileToWrite, true); err == nil {
				logFile.WriteLog(ent)
				logSources[ent.Source] = true
			} else {
				println("Failed to obtain log file :", err.Error())
			}
		}
	}

	onRead := func(q *LogQuery) {
		if initialized {
			sources := make([]string, 0, 100)

			if q.Source != "" {
				if re, err := regexp.Compile(q.Source); err == nil {
					for src, _ := range logSources {
						if re.MatchString(src) {
							sources = append(sources, src)
						}
					}
				}
			} else {
				for src, _ := range logSources {
					sources = append(sources, src)
				}
			}

			if len(sources) > 0 {
				queryLogSources(sources, q)
			} else {
				close(q.Result)
			}
		} else {
			close(q.Result)
		}
	}

	for run := true; run; {
		select {
		case ent, ok := <-mg.writeChan:
			if ok {
				onWrite(ent)
			}

		case logFileToClose, ok := <-closeLogFileChan:
			if ok {
				if logFile, found := openLogFiles[logFileToClose]; found {
					logFile.Close()
				}
			}

		case q, ok := <-mg.readChan:
			if ok {
				onRead(q)
			}

		case cmd := <-mg.closeChan:
			for ent := range mg.writeChan {
				onWrite(ent)
			}

			for q := range mg.readChan {
				onRead(q)
			}

			atomic.AddInt32(closeLogFileChanClosed, 1)
			close(closeLogFileChan)

			for _, logFile := range openLogFiles {
				logFile.Close()
			}

			cmd.ack <- true
			run = false
		}
	}
}
<commit_msg>Prolongate log file timeout TODO<commit_after>/*
	This file is part of LogHub.

	LogHub is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	LogHub 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with LogHub.  If not, see <http://www.gnu.org/licenses/>.
*/
package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"os"
	"regexp"
	"strings"
	"sync/atomic"
	"time"
)

var logFileCloseDelay = time.Second * 10

type LogManagerStat struct {
	Capacity int64
	Size     int64
}

type LogManager interface {
	Logger

	Close()
	Stat() *LogManagerStat
}

type defaultLogManager struct {
	home      string
	writeChan chan *LogEntry
	readChan  chan *LogQuery
	closeChan chan *logClose
}

func NewDefaultLogManager(home string) LogManager {
	var mg = &defaultLogManager{home, make(chan *LogEntry), make(chan *LogQuery), make(chan *logClose)}

	go mg.run()

	return mg
}

func (mg *defaultLogManager) WriteLog(ent *LogEntry) {
	mg.writeChan <- ent
}

func (mg *defaultLogManager) ReadLog(q *LogQuery) {
	mg.readChan <- q
}

func (mg *defaultLogManager) Stat() *LogManagerStat {
	//TODO: Implement.
	return nil
}

func (mg *defaultLogManager) Close() {
	close(mg.writeChan)
	close(mg.readChan)

	ack := make(chan bool)
	closeCmd := &logClose{ack}

	mg.closeChan <- closeCmd
	<-ack

	close(mg.closeChan)
}

func getSourceDirName(source string) string {
	return base64.URLEncoding.EncodeToString([]byte(source))
}

func getSourceNameByDir(dir string) (string, error) {
	if name, err := base64.URLEncoding.DecodeString(dir); err == nil {
		return string(name), nil
	} else {
		return "", err
	}
}

func getFileNameForTimestamp(timestamp int64) string {
	t := time.Unix(0, timestamp)
	return fmt.Sprintf("%d.%d.%d.%d", t.Year(), int(t.Month()), t.Day(), t.Hour())
}

func getLogFileNameForEntry(entry *LogEntry) string {
	return getSourceDirName(entry.Source) + "/" + getFileNameForTimestamp(entry.Timestamp)
}

func initLogManager(home string) (logSources map[string]bool, initialized bool) {
	initialized = false
	logSources = make(map[string]bool, 0)

	if homeDir, err := os.Open(home); err == nil {
		if dirnames, err := homeDir.Readdirnames(0); err == nil {
			for _, dir := range dirnames {
				if src, err := getSourceNameByDir(dir); err == nil {
					logSources[src] = true
				}
			}

			initialized = true
		} else {
			println("Failed to initialize log manager:", err.Error())
		}
	} else {
		println("Failed to initialize log manager:", err.Error())
	}

	return
}

func getLogFileNamesForRange(sources []string, minTimestamp int64, maxTimestamp int64, fileNames chan string) {
	minHour := time.Unix(0, minTimestamp).Truncate(time.Hour).UnixNano()
	maxHour := time.Unix(0, maxTimestamp).Truncate(time.Hour).UnixNano()

	for _, src := range sources {
		for curTs := minHour; curTs <= maxHour; curTs += int64(time.Hour) {
			fileNames <- getSourceDirName(src) + "/" + getFileNameForTimestamp(curTs)
		}
	}

	close(fileNames)
}

func (mg *defaultLogManager) run() {
	logSources, initialized := initLogManager(mg.home)
	openLogFiles := make(map[string]*LogFile)
	closeLogFileChan := make(chan string)
	closeLogFileChanClosed := new(int32)

	waitAndCloseFile := func(fileName string) {
		<-time.After(logFileCloseDelay)

		if atomic.LoadInt32(closeLogFileChanClosed) == 0 {
			closeLogFileChan <- fileName
		}
	}

	getLogFile := func(fileName string, create bool) (*LogFile, error) {
		if logFile, found := openLogFiles[fileName]; !found {
			srcDir := mg.home + "/" + fileName[0:strings.LastIndex(fileName, "/")]

			if srcDirStat, err := os.Stat(srcDir); err != nil {
				if os.IsNotExist(err) && create {
					if err = os.Mkdir(srcDir, 0777); err != nil {
						return nil, err
					}
				} else {
					return nil, err
				}
			} else {
				if !srcDirStat.IsDir() {
					return nil, errors.New("Source directory " + srcDir + " is not a directory")
				}
			}

			if logFile, err := OpenLogFile(mg.home+"/"+fileName, create); err == nil {
				openLogFiles[fileName] = logFile

				go waitAndCloseFile(fileName)

				return logFile, nil
			} else {
				return nil, err
			}
		} else {
			//TODO: Prolongate log file timeout on access.
			return logFile, nil
		}
	}

	queryLogSources := func(sources []string, q *LogQuery) {
		fileNames := make(chan string)

		go getLogFileNamesForRange(sources, q.From, q.To, fileNames)

		var results chan *LogEntry = nil

		for fileName := range fileNames {
			if logFile, err := getLogFile(fileName, false); err == nil {
				subQuery := *q
				subQuery.Result = make(chan *LogEntry)

				logFile.ReadLog(&subQuery)

				if results == nil {
					results = subQuery.Result
				} else {
					merged := make(chan *LogEntry)
					go MergeLogs(results, subQuery.Result, merged)
					results = merged
				}
			} else {
				if !os.IsNotExist(err) {
					println("Failed to get log file:", err.Error())
				}
			}
		}

		go func() {
			if results != nil {
				for ent := range results {
					q.Result <- ent
				}
			}

			close(q.Result)
		}()
	}

	onWrite := func(ent *LogEntry) {
		if initialized {
			ent.Timestamp = time.Now().UnixNano()
			logFileToWrite := getLogFileNameForEntry(ent)

			if logFile, err := getLogFile(logFileToWrite, true); err == nil {
				logFile.WriteLog(ent)
				logSources[ent.Source] = true
			} else {
				println("Failed to obtain log file :", err.Error())
			}
		}
	}

	onRead := func(q *LogQuery) {
		if initialized {
			sources := make([]string, 0, 100)

			if q.Source != "" {
				if re, err := regexp.Compile(q.Source); err == nil {
					for src, _ := range logSources {
						if re.MatchString(src) {
							sources = append(sources, src)
						}
					}
				}
			} else {
				for src, _ := range logSources {
					sources = append(sources, src)
				}
			}

			if len(sources) > 0 {
				queryLogSources(sources, q)
			} else {
				close(q.Result)
			}
		} else {
			close(q.Result)
		}
	}

	for run := true; run; {
		select {
		case ent, ok := <-mg.writeChan:
			if ok {
				onWrite(ent)
			}

		case logFileToClose, ok := <-closeLogFileChan:
			if ok {
				//TODO: Prolongate log file timeout on access.
				if logFile, found := openLogFiles[logFileToClose]; found {
					logFile.Close()
				}
			}

		case q, ok := <-mg.readChan:
			if ok {
				onRead(q)
			}

		case cmd := <-mg.closeChan:
			for ent := range mg.writeChan {
				onWrite(ent)
			}

			for q := range mg.readChan {
				onRead(q)
			}

			atomic.AddInt32(closeLogFileChanClosed, 1)
			close(closeLogFileChan)

			for _, logFile := range openLogFiles {
				logFile.Close()
			}

			cmd.ack <- true
			run = false
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/serbe/ncp"
)

var (
	commands = []string{
		"get",
		"update",
		"name",
		"rating",
		"poster",
	}
	urls = []string{
		"/forum/viewforum.php?f=218",
		"/forum/viewforum.php?f=270",
	}
)

func (a *App) get() error {
	var (
		err error
		i   int64
	)
	for _, parseurl := range urls {
		topics, err := a.net.ParseForumTree(parseurl, a.debug)
		if err != nil {
			log.Println("ParseForumTree ", parseurl, err)
			return err
		}
		for _, topic := range topics {
			_, err := a.getTorrentByHref(topic.Href)
			if err == gorm.ErrRecordNotFound {
				film, err := a.net.ParseTopic(topic, a.debug)
				if err == nil {
					i++
					film = a.checkName(film)
					a.createTorrent(film)
				}
			}
		}
	}
	if i > 0 {
		log.Println("Adding", i, "new films")
	} else {
		log.Println("No adding new films")
	}
	return err
}

func (a *App) update() error {
	var (
		i        int64
		err      error
		torrents []Torrent
	)

	torrents, err = a.getWithDownload()
	if err != nil {
		return err
	}
	for _, tor := range torrents {
		var topic ncp.Topic
		topic.Href = tor.Href
		f, err := a.net.ParseTopic(topic, false)
		if err == nil {
			if f.NNM != tor.NNM || f.Seeders != tor.Seeders || f.Leechers != tor.Leechers || f.Torrent != tor.Torrent {
				i++
				a.updateTorrent(tor.ID, f)
			}
		} else {
			return err
		}
	}
	if i > 0 {
		log.Println("Update", i, "movies")
	} else {
		log.Println("No movies update")
	}
	return nil
}

func (a *App) name() error {
	var i int64
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Name == strings.ToUpper(movie.Name) {
			lowerName, err := a.getLowerName(movie)
			if err == nil {
				i++
				a.updateName(movie.ID, lowerName)
			}
		}
	}
	if i > 0 {
		log.Println(i, "name fixed")
	} else {
		log.Println("No fixed names")
	}
	return nil
}

func (a *App) rating() error {
	var (
		i int64
	)
	movies, err := a.getNoRating()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Kinopoisk == 0 || movie.IMDb == 0 || movie.Duration == "" {
			kp, err := a.getRating(movie)
			if err == nil {
				i++
				_ = a.updateRating(movie, kp)
			}
		}
	}
	if i > 0 {
		log.Println(i, "ratings update")
	} else {
		log.Println("No update ratings")
	}
	return nil
}

func (a *App) poster() error {
	var (
		i     int64
		files []string
	)
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	filesInDir, _ := ioutil.ReadDir(a.hd)
	for _, file := range filesInDir {
		files = append(files, file.Name())
	}
	for _, movie := range movies {
		if movie.PosterURL != "" {
			if movie.Poster != "" {
				if existsFile(a.hd+movie.Poster) == false {
					poster, err := a.getPoster(movie.PosterURL)
					if err == nil {
						i++
						_ = a.updatePoster(movie, poster)
					}
				} else {
					files = deleteFromSlice(files, movie.Poster)
				}
			} else {
				poster, err := a.getPoster(movie.PosterURL)
				if err == nil {
					i++
					_ = a.updatePoster(movie, poster)
				}
			}
		} else {
			film, err := a.getFilmByMovieID(movie.ID)
			if err == nil {
				var topic ncp.Topic
				topic.Href = film.Href
				tempFilm, err := a.net.ParseTopic(topic, a.debug)
				if err == nil {
					if tempFilm.Poster != "" {
						err = a.updatePosterURL(movie, tempFilm.Poster)
						if err != nil {
							poster, err := a.getPoster(tempFilm.Poster)
							if err == nil {
								i++
								_ = a.updatePoster(movie, poster)
							}
						}
					}
				}
			}
		}
	}
	for _, file := range files {
		_ = os.Remove(a.hd + file)
	}
	if i > 0 {
		log.Println(i, "posters update")
	} else {
		log.Println("No update posters")
	}
	if len(files) > 0 {
		log.Println("Remove ", len(files), "unused images")
	}
	return nil
}
<commit_msg>fix compare<commit_after>package main

import (
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/jinzhu/gorm"
	"github.com/serbe/ncp"
)

var (
	commands = []string{
		"get",
		"update",
		"name",
		"rating",
		"poster",
	}
	urls = []string{
		"/forum/viewforum.php?f=218",
		"/forum/viewforum.php?f=270",
	}
)

func (a *App) get() error {
	var (
		err error
		i   int64
	)
	for _, parseurl := range urls {
		topics, err := a.net.ParseForumTree(parseurl, a.debug)
		if err != nil {
			log.Println("ParseForumTree ", parseurl, err)
			return err
		}
		for _, topic := range topics {
			_, err := a.getTorrentByHref(topic.Href)
			if err == gorm.ErrRecordNotFound {
				film, err := a.net.ParseTopic(topic, a.debug)
				if err == nil {
					i++
					film = a.checkName(film)
					a.createTorrent(film)
				}
			}
		}
	}
	if i > 0 {
		log.Println("Adding", i, "new films")
	} else {
		log.Println("No adding new films")
	}
	return err
}

func (a *App) update() error {
	var (
		i        int64
		err      error
		torrents []Torrent
	)

	torrents, err = a.getWithDownload()
	if err != nil {
		return err
	}
	for _, tor := range torrents {
		var topic ncp.Topic
		topic.Href = tor.Href
		f, err := a.net.ParseTopic(topic, false)
		if err == nil {
			if f.NNM != tor.NNM || f.Seeders != tor.Seeders || f.Leechers != tor.Leechers || f.Torrent != tor.Torrent {
				i++
				a.updateTorrent(tor.ID, f)
			}
		} else {
			return err
		}
	}
	if i > 0 {
		log.Println("Update", i, "movies")
	} else {
		log.Println("No movies update")
	}
	return nil
}

func (a *App) name() error {
	var i int64
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Name == strings.ToUpper(movie.Name) {
			lowerName, err := a.getLowerName(movie)
			if err == nil {
				i++
				a.updateName(movie.ID, lowerName)
			}
		}
	}
	if i > 0 {
		log.Println(i, "name fixed")
	} else {
		log.Println("No fixed names")
	}
	return nil
}

func (a *App) rating() error {
	var (
		i int64
	)
	movies, err := a.getNoRating()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Kinopoisk == 0 || movie.IMDb == 0 || movie.Duration == "" {
			kp, err := a.getRating(movie)
			if err == nil {
				i++
				_ = a.updateRating(movie, kp)
			}
		}
	}
	if i > 0 {
		log.Println(i, "ratings update")
	} else {
		log.Println("No update ratings")
	}
	return nil
}

func (a *App) poster() error {
	var (
		i     int64
		files []string
	)
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	filesInDir, _ := ioutil.ReadDir(a.hd)
	for _, file := range filesInDir {
		files = append(files, file.Name())
	}
	for _, movie := range movies {
		if movie.PosterURL != "" {
			if movie.Poster != "" {
				if existsFile(a.hd+movie.Poster) == false {
					poster, err := a.getPoster(movie.PosterURL)
					if err == nil {
						i++
						_ = a.updatePoster(movie, poster)
					}
				} else {
					files = deleteFromSlice(files, movie.Poster)
				}
			} else {
				poster, err := a.getPoster(movie.PosterURL)
				if err == nil {
					i++
					_ = a.updatePoster(movie, poster)
				}
			}
		} else {
			film, err := a.getFilmByMovieID(movie.ID)
			if err == nil {
				var topic ncp.Topic
				topic.Href = film.Href
				tempFilm, err := a.net.ParseTopic(topic, a.debug)
				if err == nil {
					if tempFilm.Poster != "" {
						err = a.updatePosterURL(movie, tempFilm.Poster)
						if err == nil {
							poster, err := a.getPoster(tempFilm.Poster)
							if err == nil {
								i++
								_ = a.updatePoster(movie, poster)
							}
						}
					}
				}
			}
		}
	}
	for _, file := range files {
		_ = os.Remove(a.hd + file)
	}
	if i > 0 {
		log.Println(i, "posters update")
	} else {
		log.Println("No update posters")
	}
	if len(files) > 0 {
		log.Println("Remove ", len(files), "unused images")
	}
	return nil
}
<|endoftext|>


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

// commander helps creating command line programs whose arguments are flags,
// commands and subcommands.
package commander

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"text/template"

	"github.com/sbinet/go-flag"
)

// A Commander holds the configuration for the command line tool.
type Commander struct {
	// Name is the command name, usually the executable's name.
	Name string
	// Commands is the list of commands supported by this commander program.
	Commands []*Command
	// Flag is a set of flags for the whole commander. It should not be
	// changed after Run() is called.
	Flag *flag.FlagSet
}

// Run executes the commander using the provided arguments. The command
// matching the first argument is executed and it receives the remaining
// arguments.
func (c *Commander) Run(args []string) error {
	if c == nil {
		return fmt.Errorf("Called Run() on a nil Commander")
	}
	if c.Flag == nil {
		c.Flag = flag.NewFlagSet(c.Name, flag.ExitOnError)
	}
	if c.Flag.Usage == nil {
		c.Flag.Usage = func() {
			if err := c.usage(); err != nil {
				fmt.Fprintln(os.Stderr, err)
			}
		}
	}
	if !c.Flag.Parsed() {
		if err := c.Flag.Parse(args); err != nil {
			return fmt.Errorf("Commander.Main flag parsing failure: %v", err)
		}
	}
	if len(args) < 1 {
		if err := c.usage(); err != nil {
			return err
		}
		return fmt.Errorf("Not enough arguments provided")
	}

	if args[0] == "help" {
		return c.help(args[1:])
	}

	for _, cmd := range c.Commands {
		if cmd.Name() == args[0] && cmd.Runnable() {
			cmd.Flag.Usage = func() { cmd.Usage() }
			if cmd.CustomFlags {
				args = args[1:]
			} else {
				cmd.Flag.Parse(args[1:])
				args = cmd.Flag.Args()
			}
			cmd.Run(cmd, args)
			return nil
		}
	}

	return fmt.Errorf("unknown subcommand %q\nRun 'help' for usage.\n", args[0])
}

func (c *Commander) usage() error {
	err := tmpl(os.Stderr, usageTemplate, c)
	if err != nil {
		fmt.Println(err)
	}
	return err
}

// help implements the 'help' command.
func (c *Commander) help(args []string) error {
	if len(args) == 0 {
		return c.usage()
	}
	if len(args) != 1 {
		return fmt.Errorf("usage: %v help command\n\nToo many arguments given.\n", c.Name)
	}

	arg := args[0]

	for _, cmd := range c.Commands {
		if cmd.Name() == arg {
			c := struct {
				*Command
				ProgramName string
			}{cmd, c.Name}
			return tmpl(os.Stdout, helpTemplate, c)
		}
	}

	return fmt.Errorf("Unknown help topic %#q.  Run '%v help'.\n", arg, c.Name)
}

// A Command is an implementation of a subcommand.
type Command struct {
	// Run runs the command.
	// The args are the arguments after the command name.
	Run func(cmd *Command, args []string)

	// UsageLine is the one-line usage message.
	// The first word in the line is taken to be the command name.
	UsageLine string

	// Short is the short description shown in the 'help' output.
	Short string

	// Long is the long message shown in the 'help <this-command>' output.
	Long string

	// Flag is a set of flags specific to this command.
	Flag flag.FlagSet

	// CustomFlags indicates that the command will do its own
	// flag parsing.
	CustomFlags bool
}

// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
	name := c.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

// Usage prints the usage details to the standard error output.
func (c *Command) Usage() {
	fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
	fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
}

// flagUsage returns the usage details as a string
func flagUsage(flagset *flag.FlagSet) string {
	if flagset == nil {
		return ""
	}
	var buf bytes.Buffer
	flagset.SetOutput(&buf)
	fmt.Fprintf(&buf, "\noptions:\n")
	if flagset.Usage != nil {
		flagset.Usage()
	} else {
		flagset.PrintDefaults()
	}
	return string(buf.Bytes())
}

// Runnable reports whether the command can be run; otherwise
// it is a documentation pseudo-command such as importpath.
func (c *Command) Runnable() bool {
	return c.Run != nil
}

var usageTemplate = `Usage:

	{{.Name}} command [arguments]

The commands are:
{{range .Commands}}{{if .Runnable}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}

Use "{{$.Name}} help [command]" for more information about a command.

Additional help topics:
{{range .Commands}}{{if not .Runnable}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}

Use "{{.Name}} help [topic]" for more information about that topic.

`

var helpTemplate = `{{if .Runnable}}Usage: {{.ProgramName}} {{.UsageLine}}

{{end}}{{.Long | trim}}
{{.Flag | flagUsage}}
`

// tmpl executes the given template text on data, writing the result to w.
func tmpl(w io.Writer, text string, data interface{}) error {
	t := template.New("top")
	t.Funcs(template.FuncMap{
		"trim": strings.TrimSpace,
		"flagUsage": flagUsage,
	})
	template.Must(t.Parse(text))
	return t.Execute(w, data)
}
<commit_msg>darwin fix for FlagOptions<commit_after>// Copyright 2012 The Go-Commander Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Based on the original work by The Go Authors:
// Copyright 2011 The Go Authors.  All rights reserved.

// commander helps creating command line programs whose arguments are flags,
// commands and subcommands.
package commander

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"text/template"

	"github.com/sbinet/go-flag"
)

// A Commander holds the configuration for the command line tool.
type Commander struct {
	// Name is the command name, usually the executable's name.
	Name string
	// Commands is the list of commands supported by this commander program.
	Commands []*Command
	// Flag is a set of flags for the whole commander. It should not be
	// changed after Run() is called.
	Flag *flag.FlagSet
}

// Run executes the commander using the provided arguments. The command
// matching the first argument is executed and it receives the remaining
// arguments.
func (c *Commander) Run(args []string) error {
	if c == nil {
		return fmt.Errorf("Called Run() on a nil Commander")
	}
	if c.Flag == nil {
		c.Flag = flag.NewFlagSet(c.Name, flag.ExitOnError)
	}
	if c.Flag.Usage == nil {
		c.Flag.Usage = func() {
			if err := c.usage(); err != nil {
				fmt.Fprintln(os.Stderr, err)
			}
		}
	}
	if !c.Flag.Parsed() {
		if err := c.Flag.Parse(args); err != nil {
			return fmt.Errorf("Commander.Main flag parsing failure: %v", err)
		}
	}
	if len(args) < 1 {
		if err := c.usage(); err != nil {
			return err
		}
		return fmt.Errorf("Not enough arguments provided")
	}

	if args[0] == "help" {
		return c.help(args[1:])
	}

	for _, cmd := range c.Commands {
		if cmd.Name() == args[0] && cmd.Runnable() {
			cmd.Flag.Usage = func() { cmd.Usage() }
			if cmd.CustomFlags {
				args = args[1:]
			} else {
				cmd.Flag.Parse(args[1:])
				args = cmd.Flag.Args()
			}
			cmd.Run(cmd, args)
			return nil
		}
	}

	return fmt.Errorf("unknown subcommand %q\nRun 'help' for usage.\n", args[0])
}

func (c *Commander) usage() error {
	err := tmpl(os.Stderr, usageTemplate, c)
	if err != nil {
		fmt.Println(err)
	}
	return err
}

// help implements the 'help' command.
func (c *Commander) help(args []string) error {
	if len(args) == 0 {
		return c.usage()
	}
	if len(args) != 1 {
		return fmt.Errorf("usage: %v help command\n\nToo many arguments given.\n", c.Name)
	}

	arg := args[0]

	for _, cmd := range c.Commands {
		if cmd.Name() == arg {
			c := struct {
				*Command
				ProgramName string
			}{cmd, c.Name}
			return tmpl(os.Stdout, helpTemplate, c)
		}
	}

	return fmt.Errorf("Unknown help topic %#q.  Run '%v help'.\n", arg, c.Name)
}

// A Command is an implementation of a subcommand.
type Command struct {
	// Run runs the command.
	// The args are the arguments after the command name.
	Run func(cmd *Command, args []string)

	// UsageLine is the one-line usage message.
	// The first word in the line is taken to be the command name.
	UsageLine string

	// Short is the short description shown in the 'help' output.
	Short string

	// Long is the long message shown in the 'help <this-command>' output.
	Long string

	// Flag is a set of flags specific to this command.
	Flag flag.FlagSet

	// CustomFlags indicates that the command will do its own
	// flag parsing.
	CustomFlags bool
}

// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
	name := c.UsageLine
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

// Usage prints the usage details to the standard error output.
func (c *Command) Usage() {
	fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
	fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
}

// FlagOptions returns the flag's options as a string
func (c* Command) FlagOptions() string {
	var buf bytes.Buffer
	c.Flag.SetOutput(&buf)
	fmt.Fprintf(&buf, "\noptions:\n")
	if c.Flag.Usage != nil {
		c.Flag.Usage()
	} else {
		c.Flag.PrintDefaults()
	}
	return string(buf.Bytes())
}

// Runnable reports whether the command can be run; otherwise
// it is a documentation pseudo-command such as importpath.
func (c *Command) Runnable() bool {
	return c.Run != nil
}

var usageTemplate = `Usage:

	{{.Name}} command [arguments]

The commands are:
{{range .Commands}}{{if .Runnable}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}

Use "{{$.Name}} help [command]" for more information about a command.

Additional help topics:
{{range .Commands}}{{if not .Runnable}}
    {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}

Use "{{.Name}} help [topic]" for more information about that topic.

`

var helpTemplate = `{{if .Runnable}}Usage: {{.ProgramName}} {{.UsageLine}}

{{end}}{{.Long | trim}}
{{.FlagOptions}}
`

// tmpl executes the given template text on data, writing the result to w.
func tmpl(w io.Writer, text string, data interface{}) error {
	t := template.New("top")
	t.Funcs(template.FuncMap{"trim": strings.TrimSpace})
	template.Must(t.Parse(text))
	return t.Execute(w, data)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"strings"
	"sync"
	"text/tabwriter"

	log "github.com/Sirupsen/logrus"
	flag "github.com/docker/docker/pkg/mflag"

	"github.com/docker/machine/drivers"
	_ "github.com/docker/machine/drivers/azure"
	_ "github.com/docker/machine/drivers/digitalocean"
	_ "github.com/docker/machine/drivers/none"
	_ "github.com/docker/machine/drivers/virtualbox"
)

type DockerCli struct{}

func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) {
	camelArgs := make([]string, len(args))
	for i, s := range args {
		if len(s) == 0 {
			return nil, false
		}
		camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
	}
	methodName := "Cmd" + strings.Join(camelArgs, "")
	method := reflect.ValueOf(cli).MethodByName(methodName)
	if !method.IsValid() {
		return nil, false
	}
	return method.Interface().(func(...string) error), true
}

func (cli *DockerCli) Cmd(args ...string) error {
	if len(args) > 1 {
		method, exists := cli.getMethod(args[:2]...)
		if exists {
			return method(args[2:]...)
		}
	}
	if len(args) > 0 {
		method, exists := cli.getMethod(args[0])
		if !exists {
			fmt.Println("Error: Command not found:", args[0])
			return cli.CmdHelp()
		}
		return method(args[1:]...)
	}
	return cli.CmdHelp()
}

func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet {
	flags := flag.NewFlagSet(name, flag.ContinueOnError)
	flags.Usage = func() {
		options := ""
		if flags.FlagCountUndeprecated() > 0 {
			options = "[OPTIONS] "
		}
		fmt.Fprintf(os.Stderr, "\nUsage: docker %s %s%s\n\n%s\n\n", name, options, signature, description)
		flags.PrintDefaults()
		os.Exit(2)
	}
	return flags
}

func (cli *DockerCli) CmdHelp(args ...string) error {
	if len(args) > 1 {
		method, exists := cli.getMethod(args[:2]...)
		if exists {
			method("--help")
			return nil
		}
	}
	if len(args) > 0 {
		method, exists := cli.getMethod(args[0])
		if !exists {
			fmt.Fprintf(os.Stderr, "Error: Command not found: %s\n", args[0])
		} else {
			method("--help")
			return nil
		}
	}

	flag.Usage()

	return nil
}

func (cli *DockerCli) CmdLs(args ...string) error {
	cmd := cli.Subcmd("machines ls", "", "List machines")
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display names")

	if err := cmd.Parse(args); err != nil {
		return err
	}

	store := NewStore()

	hostList, err := store.List()
	if err != nil {
		return err
	}

	w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)

	if !*quiet {
		fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL")
	}

	wg := sync.WaitGroup{}

	for _, host := range hostList {
		host := host
		if *quiet {
			fmt.Fprintf(w, "%s\n", host.Name)
		} else {
			wg.Add(1)
			go func() {
				currentState, err := host.Driver.GetState()
				if err != nil {
					log.Errorf("error getting state for host %s: %s", host.Name, err)
				}

				url, err := host.GetURL()
				if err != nil {
					if err == drivers.ErrHostIsNotRunning {
						url = ""
					} else {
						log.Errorf("error getting URL for host %s: %s", host.Name, err)
					}
				}

				isActive, err := store.IsActive(&host)
				if err != nil {
					log.Errorf("error determining whether host %q is active: %s",
						host.Name, err)
				}

				activeString := ""
				if isActive {
					activeString = "*"
				}

				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
					host.Name, activeString, host.Driver.DriverName(), currentState, url)
				wg.Done()
			}()
		}
	}

	wg.Wait()
	w.Flush()

	return nil
}

func (cli *DockerCli) CmdCreate(args ...string) error {
	cmd := cli.Subcmd("machines create", "NAME", "Create machines")

	driverDesc := fmt.Sprintf(
		"Driver to create machine with. Available drivers: %s",
		strings.Join(drivers.GetDriverNames(), ", "),
	)

	driver := cmd.String([]string{"d", "-driver"}, "none", driverDesc)

	createFlags := drivers.RegisterCreateFlags(cmd)

	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() != 1 {
		cmd.Usage()
		return nil
	}

	name := cmd.Arg(0)

	store := NewStore()

	driverCreateFlags, _ := createFlags[*driver]
	host, err := store.Create(name, *driver, driverCreateFlags)
	if err != nil {
		return err
	}
	if err := store.SetActive(host); err != nil {
		return err
	}
	log.Infof("%q has been created and is now the active machine. Docker commands will now run against that machine.", name)
	return nil
}

func (cli *DockerCli) CmdStart(args ...string) error {
	cmd := cli.Subcmd("machines start", "NAME", "Start a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}
	return host.Start()
}

func (cli *DockerCli) CmdStop(args ...string) error {
	cmd := cli.Subcmd("machines stop", "NAME", "Stop a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}
	return host.Stop()
}

func (cli *DockerCli) CmdRm(args ...string) error {
	cmd := cli.Subcmd("machines rm", "NAME", "Remove a machine")
	force := cmd.Bool([]string{"f", "-force"}, false, "Remove local configuration even if machine cannot be removed")

	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	isError := false

	store := NewStore()
	for _, host := range cmd.Args() {
		host := host
		if err := store.Remove(host, *force); err != nil {
			log.Errorf("Error removing machine %s: %s", host, err)
			isError = true
		}
	}
	if isError {
		return fmt.Errorf("There was an error removing a machine. To force remove it, pass the -f option. Warning: this might leave it running on the provider.")
	}
	return nil
}

func (cli *DockerCli) CmdIp(args ...string) error {
	cmd := cli.Subcmd("machines ip", "NAME", "Get the IP address of a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() > 1 {
		cmd.Usage()
		return nil
	}

	var (
		err   error
		host  *Host
		store = NewStore()
	)

	if cmd.NArg() == 1 {
		host, err = store.Load(cmd.Arg(0))
		if err != nil {
			return err
		}
	} else {
		host, err = store.GetActive()
		if err != nil {
			return err
		}
		if host == nil {
			os.Exit(1)
		}
	}

	ip, err := host.Driver.GetIP()
	if err != nil {
		return err
	}

	fmt.Println(ip)

	return nil
}

func (cli *DockerCli) CmdUrl(args ...string) error {
	cmd := cli.Subcmd("machines url", "NAME", "Get the URL of a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() > 1 {
		cmd.Usage()
		return nil
	}

	var (
		err   error
		host  *Host
		store = NewStore()
	)

	if cmd.NArg() == 1 {
		host, err = store.Load(cmd.Arg(0))
		if err != nil {
			return err
		}
	} else {
		host, err = store.GetActive()
		if err != nil {
			return err
		}
		if host == nil {
			os.Exit(1)
		}
	}

	url, err := host.GetURL()
	if err != nil {
		return err
	}

	fmt.Println(url)

	return nil
}

func (cli *DockerCli) CmdRestart(args ...string) error {
	cmd := cli.Subcmd("machines restart", "NAME", "Restart a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}
	return host.Driver.Restart()
}

func (cli *DockerCli) CmdKill(args ...string) error {
	cmd := cli.Subcmd("machines kill", "NAME", "Kill a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}
	return host.Driver.Kill()
}

func (cli *DockerCli) CmdSsh(args ...string) error {
	cmd := cli.Subcmd("machines ssh", "NAME [COMMAND ...]", "Log into or run a command on a machine with SSH")
	if err := cmd.Parse(args); err != nil {
		return err
	}

	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	i := 1
	for i < len(os.Args) && os.Args[i-1] != cmd.Arg(0) {
		i++
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}

	sshCmd, err := host.Driver.GetSSHCommand(os.Args[i:]...)
	if err != nil {
		return err
	}
	sshCmd.Stdin = os.Stdin
	sshCmd.Stdout = os.Stdout
	sshCmd.Stderr = os.Stderr
	if err := sshCmd.Run(); err != nil {
		return fmt.Errorf("%s", err)
	}
	return nil
}

func (cli *DockerCli) CmdActive(args ...string) error {
	cmd := cli.Subcmd("machines active", "[NAME]", "Get or set the active machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}

	store := NewStore()

	if cmd.NArg() == 0 {
		host, err := store.GetActive()
		if err != nil {
			return err
		}
		if host != nil {
			fmt.Println(host.Name)
		}
	} else if cmd.NArg() == 1 {
		host, err := store.Load(cmd.Arg(0))
		if err != nil {
			return err
		}
		if err := store.SetActive(host); err != nil {
			return err
		}
	} else {
		cmd.Usage()
	}

	return nil

}

func (cli *DockerCli) CmdInspect(args ...string) error {
	cmd := cli.Subcmd("machines inspect", "[NAME]", "Get detailed information about a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}

	if cmd.NArg() == 0 {
		cmd.Usage()
		return nil
	}

	store := NewStore()
	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}

	prettyJson, err := json.MarshalIndent(host, "", "    ")
	if err != nil {
		return err
	}

	fmt.Println(string(prettyJson))

	return nil
}

func (cli *DockerCli) CmdUpgrade(args ...string) error {
	cmd := cli.Subcmd("machines upgrade", "[NAME]", "Upgrade a machine to the latest version of Docker")
	if err := cmd.Parse(args); err != nil {
		return err
	}

	if cmd.NArg() == 0 {
		cmd.Usage()
		return nil
	}

	store := NewStore()
	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}

	return host.Driver.Upgrade()
}
<commit_msg>Explain setting up Docker to run against machines<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"strings"
	"sync"
	"text/tabwriter"

	log "github.com/Sirupsen/logrus"
	flag "github.com/docker/docker/pkg/mflag"

	"github.com/docker/machine/drivers"
	_ "github.com/docker/machine/drivers/azure"
	_ "github.com/docker/machine/drivers/digitalocean"
	_ "github.com/docker/machine/drivers/none"
	_ "github.com/docker/machine/drivers/virtualbox"
)

type DockerCli struct{}

func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) {
	camelArgs := make([]string, len(args))
	for i, s := range args {
		if len(s) == 0 {
			return nil, false
		}
		camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
	}
	methodName := "Cmd" + strings.Join(camelArgs, "")
	method := reflect.ValueOf(cli).MethodByName(methodName)
	if !method.IsValid() {
		return nil, false
	}
	return method.Interface().(func(...string) error), true
}

func (cli *DockerCli) Cmd(args ...string) error {
	if len(args) > 1 {
		method, exists := cli.getMethod(args[:2]...)
		if exists {
			return method(args[2:]...)
		}
	}
	if len(args) > 0 {
		method, exists := cli.getMethod(args[0])
		if !exists {
			fmt.Println("Error: Command not found:", args[0])
			return cli.CmdHelp()
		}
		return method(args[1:]...)
	}
	return cli.CmdHelp()
}

func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet {
	flags := flag.NewFlagSet(name, flag.ContinueOnError)
	flags.Usage = func() {
		options := ""
		if flags.FlagCountUndeprecated() > 0 {
			options = "[OPTIONS] "
		}
		fmt.Fprintf(os.Stderr, "\nUsage: docker %s %s%s\n\n%s\n\n", name, options, signature, description)
		flags.PrintDefaults()
		os.Exit(2)
	}
	return flags
}

func (cli *DockerCli) CmdHelp(args ...string) error {
	if len(args) > 1 {
		method, exists := cli.getMethod(args[:2]...)
		if exists {
			method("--help")
			return nil
		}
	}
	if len(args) > 0 {
		method, exists := cli.getMethod(args[0])
		if !exists {
			fmt.Fprintf(os.Stderr, "Error: Command not found: %s\n", args[0])
		} else {
			method("--help")
			return nil
		}
	}

	flag.Usage()

	return nil
}

func (cli *DockerCli) CmdLs(args ...string) error {
	cmd := cli.Subcmd("machines ls", "", "List machines")
	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display names")

	if err := cmd.Parse(args); err != nil {
		return err
	}

	store := NewStore()

	hostList, err := store.List()
	if err != nil {
		return err
	}

	w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)

	if !*quiet {
		fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL")
	}

	wg := sync.WaitGroup{}

	for _, host := range hostList {
		host := host
		if *quiet {
			fmt.Fprintf(w, "%s\n", host.Name)
		} else {
			wg.Add(1)
			go func() {
				currentState, err := host.Driver.GetState()
				if err != nil {
					log.Errorf("error getting state for host %s: %s", host.Name, err)
				}

				url, err := host.GetURL()
				if err != nil {
					if err == drivers.ErrHostIsNotRunning {
						url = ""
					} else {
						log.Errorf("error getting URL for host %s: %s", host.Name, err)
					}
				}

				isActive, err := store.IsActive(&host)
				if err != nil {
					log.Errorf("error determining whether host %q is active: %s",
						host.Name, err)
				}

				activeString := ""
				if isActive {
					activeString = "*"
				}

				fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
					host.Name, activeString, host.Driver.DriverName(), currentState, url)
				wg.Done()
			}()
		}
	}

	wg.Wait()
	w.Flush()

	return nil
}

func (cli *DockerCli) CmdCreate(args ...string) error {
	cmd := cli.Subcmd("machines create", "NAME", "Create machines")

	driverDesc := fmt.Sprintf(
		"Driver to create machine with. Available drivers: %s",
		strings.Join(drivers.GetDriverNames(), ", "),
	)

	driver := cmd.String([]string{"d", "-driver"}, "none", driverDesc)

	createFlags := drivers.RegisterCreateFlags(cmd)

	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() != 1 {
		cmd.Usage()
		return nil
	}

	name := cmd.Arg(0)

	store := NewStore()

	driverCreateFlags, _ := createFlags[*driver]
	host, err := store.Create(name, *driver, driverCreateFlags)
	if err != nil {
		return err
	}
	if err := store.SetActive(host); err != nil {
		return err
	}
	log.Infof("%q has been created and is now the active machine. To point Docker at this machine, run: export DOCKER_HOST=$(machine url) DOCKER_AUTH=identity", name)
	return nil
}

func (cli *DockerCli) CmdStart(args ...string) error {
	cmd := cli.Subcmd("machines start", "NAME", "Start a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}
	return host.Start()
}

func (cli *DockerCli) CmdStop(args ...string) error {
	cmd := cli.Subcmd("machines stop", "NAME", "Stop a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}
	return host.Stop()
}

func (cli *DockerCli) CmdRm(args ...string) error {
	cmd := cli.Subcmd("machines rm", "NAME", "Remove a machine")
	force := cmd.Bool([]string{"f", "-force"}, false, "Remove local configuration even if machine cannot be removed")

	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	isError := false

	store := NewStore()
	for _, host := range cmd.Args() {
		host := host
		if err := store.Remove(host, *force); err != nil {
			log.Errorf("Error removing machine %s: %s", host, err)
			isError = true
		}
	}
	if isError {
		return fmt.Errorf("There was an error removing a machine. To force remove it, pass the -f option. Warning: this might leave it running on the provider.")
	}
	return nil
}

func (cli *DockerCli) CmdIp(args ...string) error {
	cmd := cli.Subcmd("machines ip", "NAME", "Get the IP address of a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() > 1 {
		cmd.Usage()
		return nil
	}

	var (
		err   error
		host  *Host
		store = NewStore()
	)

	if cmd.NArg() == 1 {
		host, err = store.Load(cmd.Arg(0))
		if err != nil {
			return err
		}
	} else {
		host, err = store.GetActive()
		if err != nil {
			return err
		}
		if host == nil {
			os.Exit(1)
		}
	}

	ip, err := host.Driver.GetIP()
	if err != nil {
		return err
	}

	fmt.Println(ip)

	return nil
}

func (cli *DockerCli) CmdUrl(args ...string) error {
	cmd := cli.Subcmd("machines url", "NAME", "Get the URL of a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() > 1 {
		cmd.Usage()
		return nil
	}

	var (
		err   error
		host  *Host
		store = NewStore()
	)

	if cmd.NArg() == 1 {
		host, err = store.Load(cmd.Arg(0))
		if err != nil {
			return err
		}
	} else {
		host, err = store.GetActive()
		if err != nil {
			return err
		}
		if host == nil {
			os.Exit(1)
		}
	}

	url, err := host.GetURL()
	if err != nil {
		return err
	}

	fmt.Println(url)

	return nil
}

func (cli *DockerCli) CmdRestart(args ...string) error {
	cmd := cli.Subcmd("machines restart", "NAME", "Restart a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}
	return host.Driver.Restart()
}

func (cli *DockerCli) CmdKill(args ...string) error {
	cmd := cli.Subcmd("machines kill", "NAME", "Kill a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}
	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}
	return host.Driver.Kill()
}

func (cli *DockerCli) CmdSsh(args ...string) error {
	cmd := cli.Subcmd("machines ssh", "NAME [COMMAND ...]", "Log into or run a command on a machine with SSH")
	if err := cmd.Parse(args); err != nil {
		return err
	}

	if cmd.NArg() < 1 {
		cmd.Usage()
		return nil
	}

	i := 1
	for i < len(os.Args) && os.Args[i-1] != cmd.Arg(0) {
		i++
	}

	store := NewStore()

	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}

	sshCmd, err := host.Driver.GetSSHCommand(os.Args[i:]...)
	if err != nil {
		return err
	}
	sshCmd.Stdin = os.Stdin
	sshCmd.Stdout = os.Stdout
	sshCmd.Stderr = os.Stderr
	if err := sshCmd.Run(); err != nil {
		return fmt.Errorf("%s", err)
	}
	return nil
}

func (cli *DockerCli) CmdActive(args ...string) error {
	cmd := cli.Subcmd("machines active", "[NAME]", "Get or set the active machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}

	store := NewStore()

	if cmd.NArg() == 0 {
		host, err := store.GetActive()
		if err != nil {
			return err
		}
		if host != nil {
			fmt.Println(host.Name)
		}
	} else if cmd.NArg() == 1 {
		host, err := store.Load(cmd.Arg(0))
		if err != nil {
			return err
		}
		if err := store.SetActive(host); err != nil {
			return err
		}
	} else {
		cmd.Usage()
	}

	return nil

}

func (cli *DockerCli) CmdInspect(args ...string) error {
	cmd := cli.Subcmd("machines inspect", "[NAME]", "Get detailed information about a machine")
	if err := cmd.Parse(args); err != nil {
		return err
	}

	if cmd.NArg() == 0 {
		cmd.Usage()
		return nil
	}

	store := NewStore()
	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}

	prettyJson, err := json.MarshalIndent(host, "", "    ")
	if err != nil {
		return err
	}

	fmt.Println(string(prettyJson))

	return nil
}

func (cli *DockerCli) CmdUpgrade(args ...string) error {
	cmd := cli.Subcmd("machines upgrade", "[NAME]", "Upgrade a machine to the latest version of Docker")
	if err := cmd.Parse(args); err != nil {
		return err
	}

	if cmd.NArg() == 0 {
		cmd.Usage()
		return nil
	}

	store := NewStore()
	host, err := store.Load(cmd.Arg(0))
	if err != nil {
		return err
	}

	return host.Driver.Upgrade()
}
<|endoftext|>


<commit_before>package connectors

import (
	"fmt"
	"github.com/Jeffail/gabs"
	"github.com/projectjane/jane/models"
	"github.com/projectjane/jane/parse"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"strings"
)

type Webhook struct {
	CommandMsgs chan<- models.Message
	Connector   models.Connector
}

var webhook Webhook

func (x Webhook) Listen(commandMsgs chan<- models.Message, connector models.Connector) {
	defer Recovery(connector)

	webhook = x
	webhook.CommandMsgs = commandMsgs
	webhook.Connector = connector

	port, _ := strconv.Atoi(connector.Port)

	server := &http.Server{
		Addr:    fmt.Sprintf(":%d", port),
		Handler: nil,
	}

	if connector.Debug {
		log.Printf("Starting Webhook connector at: %s", server.Addr)
	}

	http.HandleFunc("/", webhookHandler)

	err := server.ListenAndServe()
	if err != nil {
		panic(err)
	}
}

func (x Webhook) Command(message models.Message, publishMsgs chan<- models.Message, connector models.Connector) {
	return
}

func (x Webhook) Publish(connector models.Connector, message models.Message, target string) {
	return
}

func (x Webhook) Help(connector models.Connector) (help string) {
	return
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
	url := r.URL.Path
	if webhook.Connector.Debug {
		log.Print("Webhook Incoming URL: " + url)
	}
	rawbody, err := ioutil.ReadAll(r.Body)
	body := string(rawbody)
	if err != nil {
		log.Print(err)
	}
	defer r.Body.Close()
	if webhook.Connector.Debug {
		log.Print("Webhook Incoming Body: " + body)
	}
	bodyParsed, err := gabs.ParseJSON([]byte(body))
	if err != nil {
		log.Print(err)
	}
	for _, c := range webhook.Connector.Commands {
		if match, _ := parse.Match(c.Match, url); match {
			if webhook.Connector.Debug {
				log.Print("Webhook Match: " + c.Match)
			}
			out := c.Output
			re := regexp.MustCompile("{(.*)}")
			subs := re.FindAllString(c.Output, -1)
			for _, sub := range subs {
				if webhook.Connector.Debug {
					log.Print("Webhook Sub: " + sub)
				}
				sub_clean := strings.Replace(sub, "{", "", -1)
				sub_clean = strings.Replace(sub_clean, "}", "", -1)
				value, ok := bodyParsed.Path(sub_clean).Data().(string)
				if ok {
					if webhook.Connector.Debug {
						log.Print("Webhook Val: " + value)
					}
					out = strings.Replace(out, sub, value, -1)
				}
			}
			out = strings.Replace(out, "{}", body, -1)
			if c.Process {
				var m models.Message
				m.Routes = webhook.Connector.Routes
				m.In.Source = webhook.Connector.ID
				m.In.Text = out
				m.In.Process = true
				webhook.CommandMsgs <- m
			} else {
				var color = "NONE"
				var match = false
				if match, _ = parse.Match(c.Green, out); match {
					color = "SUCCESS"
				}
				if match, _ = parse.Match(c.Yellow, out); match {
					color = "WARN"
				}
				if match, _ = parse.Match(c.Red, out); match {
					color = "FAIL"
				}
				var m models.Message
				m.Routes = webhook.Connector.Routes
				m.In.Source = webhook.Connector.ID
				m.In.Text = r.URL.Path
				m.In.Process = false
				m.Out.Text = c.Name
				m.Out.Detail = out
				m.Out.Status = color
				webhook.CommandMsgs <- m
			}
		}
	}
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("JaneBot"))
}
<commit_msg>adding query string param<commit_after>package connectors

import (
	"fmt"
	"github.com/Jeffail/gabs"
	"github.com/projectjane/jane/models"
	"github.com/projectjane/jane/parse"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
)

type Webhook struct {
	CommandMsgs chan<- models.Message
	Connector   models.Connector
}

var webhook Webhook

func (x Webhook) Listen(commandMsgs chan<- models.Message, connector models.Connector) {
	defer Recovery(connector)

	webhook = x
	webhook.CommandMsgs = commandMsgs
	webhook.Connector = connector

	port, _ := strconv.Atoi(connector.Port)

	server := &http.Server{
		Addr:    fmt.Sprintf(":%d", port),
		Handler: nil,
	}

	if connector.Debug {
		log.Printf("Starting Webhook connector at: %s", server.Addr)
	}

	http.HandleFunc("/", webhookHandler)

	err := server.ListenAndServe()
	if err != nil {
		panic(err)
	}
}

func (x Webhook) Command(message models.Message, publishMsgs chan<- models.Message, connector models.Connector) {
	return
}

func (x Webhook) Publish(connector models.Connector, message models.Message, target string) {
	return
}

func (x Webhook) Help(connector models.Connector) (help string) {
	return
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
	reqUrl := r.URL.Path
	reqQs, err := url.QueryUnescape(r.URL.RawQuery)
	if err != nil {
		log.Print(err)
	}
	if webhook.Connector.Debug {
		log.Print("Webhook Incoming URL: " + reqUrl)
	}
	rawbody, err := ioutil.ReadAll(r.Body)
	body := string(rawbody)
	if err != nil {
		log.Print(err)
	}
	defer r.Body.Close()
	if webhook.Connector.Debug {
		log.Print("Webhook Incoming Body: " + body)
	}
	bodyParsed, err := gabs.ParseJSON([]byte(body))
	isJson := true
	if err != nil {
		log.Print(err)
		isJson = false
	}
	for _, c := range webhook.Connector.Commands {
		if match, _ := parse.Match(c.Match, reqUrl); match {
			if webhook.Connector.Debug {
				log.Print("Webhook Match: " + c.Match)
			}
			out := c.Output
			if isJson {
				re := regexp.MustCompile("{(.*)}")
				subs := re.FindAllString(c.Output, -1)
				for _, sub := range subs {
					if webhook.Connector.Debug {
						log.Print("Webhook Sub: " + sub)
					}
					sub_clean := strings.Replace(sub, "{", "", -1)
					sub_clean = strings.Replace(sub_clean, "}", "", -1)
					value, ok := bodyParsed.Path(sub_clean).Data().(string)
					if ok {
						if webhook.Connector.Debug {
							log.Print("Webhook Val: " + value)
						}
						out = strings.Replace(out, sub, value, -1)
					}
				}
			}
			out = strings.Replace(out, "{?}", reqQs, -1)
			out = strings.Replace(out, "{}", body, -1)
			if c.Process {
				var m models.Message
				m.Routes = webhook.Connector.Routes
				m.In.Source = webhook.Connector.ID
				m.In.Text = out
				m.In.Process = true
				webhook.CommandMsgs <- m
			} else {
				var color = "NONE"
				var match = false
				if match, _ = parse.Match(c.Green, out); match {
					color = "SUCCESS"
				}
				if match, _ = parse.Match(c.Yellow, out); match {
					color = "WARN"
				}
				if match, _ = parse.Match(c.Red, out); match {
					color = "FAIL"
				}
				var m models.Message
				m.Routes = webhook.Connector.Routes
				m.In.Source = webhook.Connector.ID
				m.In.Text = reqUrl
				m.In.Process = false
				m.Out.Text = c.Name
				m.Out.Detail = out
				m.Out.Status = color
				webhook.CommandMsgs <- m
			}
		}
	}
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("JaneBot"))
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.

package constant

import (
	"os"

	"github.com/ian-kent/go-log/levels"
)

const (
	DEFAULT_LOG_LEVEL = levels.WARN

	PATH_SEPARATOR = string(os.PathSeparator)
	PLUGINS_DIRECTORY = "repository" + PATH_SEPARATOR + "components" + PATH_SEPARATOR + "plugins" + PATH_SEPARATOR

	//constants to store resource file names
	README_FILE = "README.txt"
	LICENSE_FILE = "LICENSE.txt"
	NOT_A_CONTRIBUTION_FILE = "NOT_A_CONTRIBUTION.txt"
	INSTRUCTIONS_FILE = "instructions.txt"
	UPDATE_DESCRIPTOR_FILE = "update-descriptor.yaml"

	//Temporary directory to copy files before creating the new zip
	TEMP_DIR = "temp"
	//This is used to store carbon.home string
	CARBON_HOME = "carbon.home"
	//Prefix of the update file and the root directory of the update zip
	UPDATE_NAME_PREFIX = "WSO2-CARBON-UPDATE"

	//Constants to store configs in viper
	DISTRIBUTION_ROOT = "DISTRIBUTION_ROOT"
	UPDATE_ROOT = "UPDATE_ROOT"
	UPDATE_NAME = "_UPDATE_NAME"
	PRODUCT_NAME = "_PRODUCT_NAME"

	UPDATE_NUMBER_REGEX = "^\\d{4}$"
	KERNEL_VERSION_REGEX = "^\\d+\\.\\d+\\.\\d+$"
	FILENAME_REGEX = "^WSO2-CARBON-UPDATE-\\d+\\.\\d+\\.\\d+-\\d{4}.zip$"

	OTHER = 0
	YES = 1
	NO = 2
	REENTER = 3

	SAMPLE = "SAMPLE"
	CHECK_MD5_DISABLED = "CHECK_MD5_DISABLED"
	//resource_files
	RESOURCE_FILES = "RESOURCE_FILES"
	MANDATORY = "MANDATORY"
	OPTIONAL = "OPTIONAL"
	SKIP = "SKIP"
	RESOURCE_FILES_MANDATORY = RESOURCE_FILES + "." + MANDATORY
	RESOURCE_FILES_OPTIONAL = RESOURCE_FILES + "." + OPTIONAL
	RESOURCE_FILES_SKIP = RESOURCE_FILES + "." + SKIP

	PLATFORM_VERSIONS = "PLATFORM_VERSIONS"

	PATCH_ID_REGEX = "WSO2-CARBON-PATCH-(\\d+\\.\\d+\\.\\d+)-(\\d{4})"
	APPLIES_TO_REGEX = "(?s)Applies To.*?:(.*)Associated JIRA|Applies To.*?:(.*)DESCRIPTION"
	ASSOCIATED_JIRAS_REGEX = "https:\\/\\/wso2\\.org\\/jira\\/browse\\/([A-Z]*?-\\d+)"
	DESCRIPTION_REGEX = "(?s)DESCRIPTION(.*)"
	JIRA_SUMMARY_REGEX = "summary\":(\"(.*?)\")"

	PATCH_REGEX = "(?m).*patch.*"

	JIRA_API_URL = "https://wso2.org/jira/rest/api/latest/issue/"

	UPDATE_NO_DEFAULT = "ADD_UPDATE_NUMBER_HERE"
	PLATFORM_NAME_DEFAULT = "ADD_PLATFORM_NAME_HERE"
	PLATFORM_VERSION_DEFAULT = "ADD_PLATFORM_VERSION_HERE"
	APPLIES_TO_DEFAULT = "ADD_APPLIES_TO_HERE"
	DESCRIPTION_DEFAULT = "ADD_DESCRIPTION_HERE\n"

	JIRA_KEY_DEFAULT = "ADD_JIRA_KEY_HERE"
	JIRA_NA = "N/A"
	JIRA_SUMMARY_DEFAULT = "ADD_JIRA_SUMMARY_HERE"
)
<commit_msg>Change DESCRIPTION_REGEX<commit_after>// Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.

package constant

import (
	"os"

	"github.com/ian-kent/go-log/levels"
)

const (
	DEFAULT_LOG_LEVEL = levels.WARN

	PATH_SEPARATOR = string(os.PathSeparator)
	PLUGINS_DIRECTORY = "repository" + PATH_SEPARATOR + "components" + PATH_SEPARATOR + "plugins" + PATH_SEPARATOR

	//constants to store resource file names
	README_FILE = "README.txt"
	LICENSE_FILE = "LICENSE.txt"
	NOT_A_CONTRIBUTION_FILE = "NOT_A_CONTRIBUTION.txt"
	INSTRUCTIONS_FILE = "instructions.txt"
	UPDATE_DESCRIPTOR_FILE = "update-descriptor.yaml"

	//Temporary directory to copy files before creating the new zip
	TEMP_DIR = "temp"
	//This is used to store carbon.home string
	CARBON_HOME = "carbon.home"
	//Prefix of the update file and the root directory of the update zip
	UPDATE_NAME_PREFIX = "WSO2-CARBON-UPDATE"

	//Constants to store configs in viper
	DISTRIBUTION_ROOT = "DISTRIBUTION_ROOT"
	UPDATE_ROOT = "UPDATE_ROOT"
	UPDATE_NAME = "_UPDATE_NAME"
	PRODUCT_NAME = "_PRODUCT_NAME"

	UPDATE_NUMBER_REGEX = "^\\d{4}$"
	KERNEL_VERSION_REGEX = "^\\d+\\.\\d+\\.\\d+$"
	FILENAME_REGEX = "^WSO2-CARBON-UPDATE-\\d+\\.\\d+\\.\\d+-\\d{4}.zip$"

	OTHER = 0
	YES = 1
	NO = 2
	REENTER = 3

	SAMPLE = "SAMPLE"
	CHECK_MD5_DISABLED = "CHECK_MD5_DISABLED"
	//resource_files
	RESOURCE_FILES = "RESOURCE_FILES"
	MANDATORY = "MANDATORY"
	OPTIONAL = "OPTIONAL"
	SKIP = "SKIP"
	RESOURCE_FILES_MANDATORY = RESOURCE_FILES + "." + MANDATORY
	RESOURCE_FILES_OPTIONAL = RESOURCE_FILES + "." + OPTIONAL
	RESOURCE_FILES_SKIP = RESOURCE_FILES + "." + SKIP

	PLATFORM_VERSIONS = "PLATFORM_VERSIONS"

	PATCH_ID_REGEX = "WSO2-CARBON-PATCH-(\\d+\\.\\d+\\.\\d+)-(\\d{4})"
	APPLIES_TO_REGEX = "(?s)Applies To.*?:(.*)Associated JIRA|Applies To.*?:(.*)DESCRIPTION"
	ASSOCIATED_JIRAS_REGEX = "https:\\/\\/wso2\\.org\\/jira\\/browse\\/([A-Z]*?-\\d+)"
	DESCRIPTION_REGEX = "(?s)DESCRIPTION\n-*\n(.*)INSTALLATION INSTRUCTIONS"

	PATCH_REGEX = "(?m).*patch.*"

	JIRA_API_URL = "https://wso2.org/jira/rest/api/latest/issue/"

	UPDATE_NO_DEFAULT = "ADD_UPDATE_NUMBER_HERE"
	PLATFORM_NAME_DEFAULT = "ADD_PLATFORM_NAME_HERE"
	PLATFORM_VERSION_DEFAULT = "ADD_PLATFORM_VERSION_HERE"
	APPLIES_TO_DEFAULT = "ADD_APPLIES_TO_HERE"
	DESCRIPTION_DEFAULT = "ADD_DESCRIPTION_HERE\n"

	JIRA_KEY_DEFAULT = "ADD_JIRA_KEY_HERE"
	JIRA_NA = "N/A"
	JIRA_SUMMARY_DEFAULT = "ADD_JIRA_SUMMARY_HERE"
)
<|endoftext|>