<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package http

import (
	"context"
	"crypto/tls"
	"encoding/json"

	"fmt"
	"github.com/dop251/goja"
	"github.com/loadimpact/k6/js/common"
	"github.com/loadimpact/k6/js/modules/k6/html"
	"github.com/loadimpact/k6/lib"
	"golang.org/x/crypto/ocsp"
	"net/url"
	"strings"
)

type OCSP struct {
	ProducedAt, ThisUpdate, NextUpdate, RevokedAt int64
	RevocationReason                              string
	Status                                        string
}

type HTTPResponseTimings struct {
	Duration, Blocked, LookingUp, Connecting, Sending, Waiting, Receiving float64
}

type HTTPResponse struct {
	ctx context.Context

	RemoteIP       string
	RemotePort     int
	URL            string
	Status         int
	Proto          string
	Headers        map[string]string
	Cookies        map[string][]*HTTPCookie
	Body           string
	Timings        HTTPResponseTimings
	TLSVersion     string
	TLSCipherSuite string
	OCSP           OCSP `js:"ocsp"`
	Error          string

	cachedJSON goja.Value
}

func (res *HTTPResponse) setTLSInfo(tlsState *tls.ConnectionState) {
	switch tlsState.Version {
	case tls.VersionSSL30:
		res.TLSVersion = SSL_3_0
	case tls.VersionTLS10:
		res.TLSVersion = TLS_1_0
	case tls.VersionTLS11:
		res.TLSVersion = TLS_1_1
	case tls.VersionTLS12:
		res.TLSVersion = TLS_1_2
	}

	res.TLSCipherSuite = lib.SupportedTLSCipherSuitesToString[tlsState.CipherSuite]
	ocspStapledRes := OCSP{Status: OCSP_STATUS_UNKNOWN}

	if ocspRes, err := ocsp.ParseResponse(tlsState.OCSPResponse, nil); err == nil {
		switch ocspRes.Status {
		case ocsp.Good:
			ocspStapledRes.Status = OCSP_STATUS_GOOD
		case ocsp.Revoked:
			ocspStapledRes.Status = OCSP_STATUS_REVOKED
		case ocsp.ServerFailed:
			ocspStapledRes.Status = OCSP_STATUS_SERVER_FAILED
		case ocsp.Unknown:
			ocspStapledRes.Status = OCSP_STATUS_UNKNOWN
		}
		switch ocspRes.RevocationReason {
		case ocsp.Unspecified:
			ocspStapledRes.RevocationReason = OCSP_REASON_UNSPECIFIED
		case ocsp.KeyCompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_KEY_COMPROMISE
		case ocsp.CACompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_CA_COMPROMISE
		case ocsp.AffiliationChanged:
			ocspStapledRes.RevocationReason = OCSP_REASON_AFFILIATION_CHANGED
		case ocsp.Superseded:
			ocspStapledRes.RevocationReason = OCSP_REASON_SUPERSEDED
		case ocsp.CessationOfOperation:
			ocspStapledRes.RevocationReason = OCSP_REASON_CESSATION_OF_OPERATION
		case ocsp.CertificateHold:
			ocspStapledRes.RevocationReason = OCSP_REASON_CERTIFICATE_HOLD
		case ocsp.RemoveFromCRL:
			ocspStapledRes.RevocationReason = OCSP_REASON_REMOVE_FROM_CRL
		case ocsp.PrivilegeWithdrawn:
			ocspStapledRes.RevocationReason = OCSP_REASON_PRIVILEGE_WITHDRAWN
		case ocsp.AACompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_AA_COMPROMISE
		}
		ocspStapledRes.ProducedAt = ocspRes.ProducedAt.Unix()
		ocspStapledRes.ThisUpdate = ocspRes.ThisUpdate.Unix()
		ocspStapledRes.NextUpdate = ocspRes.NextUpdate.Unix()
		ocspStapledRes.RevokedAt = ocspRes.RevokedAt.Unix()
	}

	res.OCSP = ocspStapledRes
}

func (res *HTTPResponse) Json() goja.Value {
	if res.cachedJSON == nil {
		var v interface{}
		if err := json.Unmarshal([]byte(res.Body), &v); err != nil {
			common.Throw(common.GetRuntime(res.ctx), err)
		}
		res.cachedJSON = common.GetRuntime(res.ctx).ToValue(v)
	}
	return res.cachedJSON
}

func (res *HTTPResponse) Html(selector ...string) html.Selection {
	sel, err := html.HTML{}.ParseHTML(res.ctx, res.Body)
	if err != nil {
		common.Throw(common.GetRuntime(res.ctx), err)
	}
	sel.URL = res.URL
	if len(selector) > 0 {
		sel = sel.Find(selector[0])
	}
	return sel
}

func (res *HTTPResponse) SubmitForm(args ...goja.Value) (*HTTPResponse, error) {
	rt := common.GetRuntime(res.ctx)

	formSelector := "form"
	submitSelector := "[type=\"submit\"]"
	var fields map[string]goja.Value
	var requestParams goja.Value
	if len(args) > 0 {
		params := args[0].ToObject(rt)
		for _, k := range params.Keys() {
			switch k {
			case "formSelector":
				formSelector = params.Get(k).String()
			case "submitSelector":
				submitSelector = params.Get(k).String()
			case "fields":
				rt.ExportTo(params.Get(k), &fields)
			case "params":
				requestParams = params.Get(k)
			}
		}
	}

	form := res.Html(formSelector)
	if form.Size() == 0 {
		common.Throw(rt, fmt.Errorf("no form found for selector '%s' in response '%s'", formSelector, res.URL))
	}

	methodAttr := form.Attr("method")
	var requestMethod string
	if methodAttr == goja.Undefined() {
		// Use GET by default
		requestMethod = HTTP_METHOD_GET
	} else {
		requestMethod = strings.ToUpper(methodAttr.String())
	}

	actionAttr := form.Attr("action")
	var requestUrl goja.Value
	if actionAttr == goja.Undefined() {
		// Use the url of the response if no action is set
		requestUrl = rt.ToValue(res.URL)
	} else {
		// Resolve the action url from the response url
		responseUrl, _ := url.Parse(res.URL)
		actionUrl, _ := url.Parse(actionAttr.String())
		requestUrl = rt.ToValue(responseUrl.ResolveReference(actionUrl).String())
	}

	// Set the body based on the form values
	body := form.SerializeObject()

	// Set the name + value of the submit button
	submit := form.Find(submitSelector)
	submitName := submit.Attr("name")
	submitValue := submit.Val()
	if submitName != goja.Undefined() && submitValue != goja.Undefined() {
		body[submitName.String()] = submitValue
	}

	// Set the values supplied in the arguments, overriding automatically set values
	if fields != nil {
		for k, v := range fields {
			body[k] = v
		}
	}

	if requestParams == nil {
		return New().Request(res.ctx, requestMethod, requestUrl, rt.ToValue(body))
	} else {
		return New().Request(res.ctx, requestMethod, requestUrl, rt.ToValue(body), requestParams)
	}
}
<commit_msg>Added error check<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package http

import (
	"context"
	"crypto/tls"
	"encoding/json"

	"fmt"
	"github.com/dop251/goja"
	"github.com/loadimpact/k6/js/common"
	"github.com/loadimpact/k6/js/modules/k6/html"
	"github.com/loadimpact/k6/lib"
	"golang.org/x/crypto/ocsp"
	"net/url"
	"strings"
)

type OCSP struct {
	ProducedAt, ThisUpdate, NextUpdate, RevokedAt int64
	RevocationReason                              string
	Status                                        string
}

type HTTPResponseTimings struct {
	Duration, Blocked, LookingUp, Connecting, Sending, Waiting, Receiving float64
}

type HTTPResponse struct {
	ctx context.Context

	RemoteIP       string
	RemotePort     int
	URL            string
	Status         int
	Proto          string
	Headers        map[string]string
	Cookies        map[string][]*HTTPCookie
	Body           string
	Timings        HTTPResponseTimings
	TLSVersion     string
	TLSCipherSuite string
	OCSP           OCSP `js:"ocsp"`
	Error          string

	cachedJSON goja.Value
}

func (res *HTTPResponse) setTLSInfo(tlsState *tls.ConnectionState) {
	switch tlsState.Version {
	case tls.VersionSSL30:
		res.TLSVersion = SSL_3_0
	case tls.VersionTLS10:
		res.TLSVersion = TLS_1_0
	case tls.VersionTLS11:
		res.TLSVersion = TLS_1_1
	case tls.VersionTLS12:
		res.TLSVersion = TLS_1_2
	}

	res.TLSCipherSuite = lib.SupportedTLSCipherSuitesToString[tlsState.CipherSuite]
	ocspStapledRes := OCSP{Status: OCSP_STATUS_UNKNOWN}

	if ocspRes, err := ocsp.ParseResponse(tlsState.OCSPResponse, nil); err == nil {
		switch ocspRes.Status {
		case ocsp.Good:
			ocspStapledRes.Status = OCSP_STATUS_GOOD
		case ocsp.Revoked:
			ocspStapledRes.Status = OCSP_STATUS_REVOKED
		case ocsp.ServerFailed:
			ocspStapledRes.Status = OCSP_STATUS_SERVER_FAILED
		case ocsp.Unknown:
			ocspStapledRes.Status = OCSP_STATUS_UNKNOWN
		}
		switch ocspRes.RevocationReason {
		case ocsp.Unspecified:
			ocspStapledRes.RevocationReason = OCSP_REASON_UNSPECIFIED
		case ocsp.KeyCompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_KEY_COMPROMISE
		case ocsp.CACompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_CA_COMPROMISE
		case ocsp.AffiliationChanged:
			ocspStapledRes.RevocationReason = OCSP_REASON_AFFILIATION_CHANGED
		case ocsp.Superseded:
			ocspStapledRes.RevocationReason = OCSP_REASON_SUPERSEDED
		case ocsp.CessationOfOperation:
			ocspStapledRes.RevocationReason = OCSP_REASON_CESSATION_OF_OPERATION
		case ocsp.CertificateHold:
			ocspStapledRes.RevocationReason = OCSP_REASON_CERTIFICATE_HOLD
		case ocsp.RemoveFromCRL:
			ocspStapledRes.RevocationReason = OCSP_REASON_REMOVE_FROM_CRL
		case ocsp.PrivilegeWithdrawn:
			ocspStapledRes.RevocationReason = OCSP_REASON_PRIVILEGE_WITHDRAWN
		case ocsp.AACompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_AA_COMPROMISE
		}
		ocspStapledRes.ProducedAt = ocspRes.ProducedAt.Unix()
		ocspStapledRes.ThisUpdate = ocspRes.ThisUpdate.Unix()
		ocspStapledRes.NextUpdate = ocspRes.NextUpdate.Unix()
		ocspStapledRes.RevokedAt = ocspRes.RevokedAt.Unix()
	}

	res.OCSP = ocspStapledRes
}

func (res *HTTPResponse) Json() goja.Value {
	if res.cachedJSON == nil {
		var v interface{}
		if err := json.Unmarshal([]byte(res.Body), &v); err != nil {
			common.Throw(common.GetRuntime(res.ctx), err)
		}
		res.cachedJSON = common.GetRuntime(res.ctx).ToValue(v)
	}
	return res.cachedJSON
}

func (res *HTTPResponse) Html(selector ...string) html.Selection {
	sel, err := html.HTML{}.ParseHTML(res.ctx, res.Body)
	if err != nil {
		common.Throw(common.GetRuntime(res.ctx), err)
	}
	sel.URL = res.URL
	if len(selector) > 0 {
		sel = sel.Find(selector[0])
	}
	return sel
}

func (res *HTTPResponse) SubmitForm(args ...goja.Value) (*HTTPResponse, error) {
	rt := common.GetRuntime(res.ctx)

	formSelector := "form"
	submitSelector := "[type=\"submit\"]"
	var fields map[string]goja.Value
	var requestParams goja.Value
	if len(args) > 0 {
		params := args[0].ToObject(rt)
		for _, k := range params.Keys() {
			switch k {
			case "formSelector":
				formSelector = params.Get(k).String()
			case "submitSelector":
				submitSelector = params.Get(k).String()
			case "fields":
				if rt.ExportTo(params.Get(k), &fields) != nil {
					fields = nil
				}
			case "params":
				requestParams = params.Get(k)
			}
		}
	}

	form := res.Html(formSelector)
	if form.Size() == 0 {
		common.Throw(rt, fmt.Errorf("no form found for selector '%s' in response '%s'", formSelector, res.URL))
	}

	methodAttr := form.Attr("method")
	var requestMethod string
	if methodAttr == goja.Undefined() {
		// Use GET by default
		requestMethod = HTTP_METHOD_GET
	} else {
		requestMethod = strings.ToUpper(methodAttr.String())
	}

	actionAttr := form.Attr("action")
	var requestUrl goja.Value
	if actionAttr == goja.Undefined() {
		// Use the url of the response if no action is set
		requestUrl = rt.ToValue(res.URL)
	} else {
		// Resolve the action url from the response url
		responseUrl, _ := url.Parse(res.URL)
		actionUrl, _ := url.Parse(actionAttr.String())
		requestUrl = rt.ToValue(responseUrl.ResolveReference(actionUrl).String())
	}

	// Set the body based on the form values
	body := form.SerializeObject()

	// Set the name + value of the submit button
	submit := form.Find(submitSelector)
	submitName := submit.Attr("name")
	submitValue := submit.Val()
	if submitName != goja.Undefined() && submitValue != goja.Undefined() {
		body[submitName.String()] = submitValue
	}

	// Set the values supplied in the arguments, overriding automatically set values
	if fields != nil {
		for k, v := range fields {
			body[k] = v
		}
	}

	if requestParams == nil {
		return New().Request(res.ctx, requestMethod, requestUrl, rt.ToValue(body))
	} else {
		return New().Request(res.ctx, requestMethod, requestUrl, rt.ToValue(body), requestParams)
	}
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package http

import (
	"context"
	"crypto/tls"
	"encoding/json"

	"github.com/dop251/goja"
	"github.com/loadimpact/k6/js/common"
	"github.com/loadimpact/k6/js/modules/k6/html"
	"github.com/loadimpact/k6/lib"
	"golang.org/x/crypto/ocsp"
	"strings"
	"net/url"
)

type OCSP struct {
	ProducedAt, ThisUpdate, NextUpdate, RevokedAt int64
	RevocationReason                              string
	Status                                        string
}

type HTTPResponseTimings struct {
	Duration, Blocked, LookingUp, Connecting, Sending, Waiting, Receiving float64
}

type HTTPResponse struct {
	ctx context.Context

	RemoteIP       string
	RemotePort     int
	URL            string
	Status         int
	Proto          string
	Headers        map[string]string
	Cookies        map[string][]*HTTPCookie
	Body           string
	Timings        HTTPResponseTimings
	TLSVersion     string
	TLSCipherSuite string
	OCSP           OCSP `js:"ocsp"`
	Error          string

	cachedJSON goja.Value
}

func (res *HTTPResponse) setTLSInfo(tlsState *tls.ConnectionState) {
	switch tlsState.Version {
	case tls.VersionSSL30:
		res.TLSVersion = SSL_3_0
	case tls.VersionTLS10:
		res.TLSVersion = TLS_1_0
	case tls.VersionTLS11:
		res.TLSVersion = TLS_1_1
	case tls.VersionTLS12:
		res.TLSVersion = TLS_1_2
	}

	res.TLSCipherSuite = lib.SupportedTLSCipherSuitesToString[tlsState.CipherSuite]
	ocspStapledRes := OCSP{Status: OCSP_STATUS_UNKNOWN}

	if ocspRes, err := ocsp.ParseResponse(tlsState.OCSPResponse, nil); err == nil {
		switch ocspRes.Status {
		case ocsp.Good:
			ocspStapledRes.Status = OCSP_STATUS_GOOD
		case ocsp.Revoked:
			ocspStapledRes.Status = OCSP_STATUS_REVOKED
		case ocsp.ServerFailed:
			ocspStapledRes.Status = OCSP_STATUS_SERVER_FAILED
		case ocsp.Unknown:
			ocspStapledRes.Status = OCSP_STATUS_UNKNOWN
		}
		switch ocspRes.RevocationReason {
		case ocsp.Unspecified:
			ocspStapledRes.RevocationReason = OCSP_REASON_UNSPECIFIED
		case ocsp.KeyCompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_KEY_COMPROMISE
		case ocsp.CACompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_CA_COMPROMISE
		case ocsp.AffiliationChanged:
			ocspStapledRes.RevocationReason = OCSP_REASON_AFFILIATION_CHANGED
		case ocsp.Superseded:
			ocspStapledRes.RevocationReason = OCSP_REASON_SUPERSEDED
		case ocsp.CessationOfOperation:
			ocspStapledRes.RevocationReason = OCSP_REASON_CESSATION_OF_OPERATION
		case ocsp.CertificateHold:
			ocspStapledRes.RevocationReason = OCSP_REASON_CERTIFICATE_HOLD
		case ocsp.RemoveFromCRL:
			ocspStapledRes.RevocationReason = OCSP_REASON_REMOVE_FROM_CRL
		case ocsp.PrivilegeWithdrawn:
			ocspStapledRes.RevocationReason = OCSP_REASON_PRIVILEGE_WITHDRAWN
		case ocsp.AACompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_AA_COMPROMISE
		}
		ocspStapledRes.ProducedAt = ocspRes.ProducedAt.Unix()
		ocspStapledRes.ThisUpdate = ocspRes.ThisUpdate.Unix()
		ocspStapledRes.NextUpdate = ocspRes.NextUpdate.Unix()
		ocspStapledRes.RevokedAt = ocspRes.RevokedAt.Unix()
	}

	res.OCSP = ocspStapledRes
}

func (res *HTTPResponse) Json() goja.Value {
	if res.cachedJSON == nil {
		var v interface{}
		if err := json.Unmarshal([]byte(res.Body), &v); err != nil {
			common.Throw(common.GetRuntime(res.ctx), err)
		}
		res.cachedJSON = common.GetRuntime(res.ctx).ToValue(v)
	}
	return res.cachedJSON
}

func (res *HTTPResponse) Html(selector ...string) html.Selection {
	sel, err := html.HTML{}.ParseHTML(res.ctx, res.Body)
	if err != nil {
		common.Throw(common.GetRuntime(res.ctx), err)
	}
	sel.URL = res.URL
	if len(selector) > 0 {
		sel = sel.Find(selector[0])
	}
	return sel
}

func (res *HTTPResponse) SubmitForm(args... goja.Value) (*HTTPResponse, error) {
	rt := common.GetRuntime(res.ctx)

	selector := "form"
	var fields map[string]goja.Value
	var requestOptions goja.Value
	if len(args) > 0{
		params := args[0].ToObject(rt)
		for _, k := range params.Keys() {
			switch k {
			case "selector":
				selector = params.Get(k).Export().(string)
			case "fields":
				rt.ExportTo(params.Get(k), &fields)
			case "options":
				requestOptions = params.Get(k)
			}
		}
	}

	form := res.Html(selector)

	methodAttr := form.Attr("method")
	var requestMethod string
	if methodAttr == goja.Undefined(){
		requestMethod = "GET"
	} else{
		requestMethod = strings.ToUpper(methodAttr.Export().(string))
	}

	actionAttr := form.Attr("action")
	var requestUrl goja.Value
	if actionAttr == goja.Undefined(){
		requestUrl = rt.ToValue(res.URL)
	} else{
		responseUrl, _ := url.Parse(res.URL)
		actionUrl, _ := url.Parse(actionAttr.Export().(string))
		requestUrl = rt.ToValue(responseUrl.ResolveReference(actionUrl).String())
	}

	body := form.SerializeObject()
	if fields != nil{
		for k, v := range fields {
			body[k] = rt.ToValue(v)
		}
	}

	if requestOptions == nil {
		return New().Request(res.ctx, requestMethod, requestUrl, rt.ToValue(body))
	} else {
		return New().Request(res.ctx, requestMethod, requestUrl, rt.ToValue(body), requestOptions)
	}
}
<commit_msg>Added name/value of submit button.<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package http

import (
	"context"
	"crypto/tls"
	"encoding/json"

	"github.com/dop251/goja"
	"github.com/loadimpact/k6/js/common"
	"github.com/loadimpact/k6/js/modules/k6/html"
	"github.com/loadimpact/k6/lib"
	"golang.org/x/crypto/ocsp"
	"strings"
	"net/url"
)

type OCSP struct {
	ProducedAt, ThisUpdate, NextUpdate, RevokedAt int64
	RevocationReason                              string
	Status                                        string
}

type HTTPResponseTimings struct {
	Duration, Blocked, LookingUp, Connecting, Sending, Waiting, Receiving float64
}

type HTTPResponse struct {
	ctx context.Context

	RemoteIP       string
	RemotePort     int
	URL            string
	Status         int
	Proto          string
	Headers        map[string]string
	Cookies        map[string][]*HTTPCookie
	Body           string
	Timings        HTTPResponseTimings
	TLSVersion     string
	TLSCipherSuite string
	OCSP           OCSP `js:"ocsp"`
	Error          string

	cachedJSON goja.Value
}

func (res *HTTPResponse) setTLSInfo(tlsState *tls.ConnectionState) {
	switch tlsState.Version {
	case tls.VersionSSL30:
		res.TLSVersion = SSL_3_0
	case tls.VersionTLS10:
		res.TLSVersion = TLS_1_0
	case tls.VersionTLS11:
		res.TLSVersion = TLS_1_1
	case tls.VersionTLS12:
		res.TLSVersion = TLS_1_2
	}

	res.TLSCipherSuite = lib.SupportedTLSCipherSuitesToString[tlsState.CipherSuite]
	ocspStapledRes := OCSP{Status: OCSP_STATUS_UNKNOWN}

	if ocspRes, err := ocsp.ParseResponse(tlsState.OCSPResponse, nil); err == nil {
		switch ocspRes.Status {
		case ocsp.Good:
			ocspStapledRes.Status = OCSP_STATUS_GOOD
		case ocsp.Revoked:
			ocspStapledRes.Status = OCSP_STATUS_REVOKED
		case ocsp.ServerFailed:
			ocspStapledRes.Status = OCSP_STATUS_SERVER_FAILED
		case ocsp.Unknown:
			ocspStapledRes.Status = OCSP_STATUS_UNKNOWN
		}
		switch ocspRes.RevocationReason {
		case ocsp.Unspecified:
			ocspStapledRes.RevocationReason = OCSP_REASON_UNSPECIFIED
		case ocsp.KeyCompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_KEY_COMPROMISE
		case ocsp.CACompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_CA_COMPROMISE
		case ocsp.AffiliationChanged:
			ocspStapledRes.RevocationReason = OCSP_REASON_AFFILIATION_CHANGED
		case ocsp.Superseded:
			ocspStapledRes.RevocationReason = OCSP_REASON_SUPERSEDED
		case ocsp.CessationOfOperation:
			ocspStapledRes.RevocationReason = OCSP_REASON_CESSATION_OF_OPERATION
		case ocsp.CertificateHold:
			ocspStapledRes.RevocationReason = OCSP_REASON_CERTIFICATE_HOLD
		case ocsp.RemoveFromCRL:
			ocspStapledRes.RevocationReason = OCSP_REASON_REMOVE_FROM_CRL
		case ocsp.PrivilegeWithdrawn:
			ocspStapledRes.RevocationReason = OCSP_REASON_PRIVILEGE_WITHDRAWN
		case ocsp.AACompromise:
			ocspStapledRes.RevocationReason = OCSP_REASON_AA_COMPROMISE
		}
		ocspStapledRes.ProducedAt = ocspRes.ProducedAt.Unix()
		ocspStapledRes.ThisUpdate = ocspRes.ThisUpdate.Unix()
		ocspStapledRes.NextUpdate = ocspRes.NextUpdate.Unix()
		ocspStapledRes.RevokedAt = ocspRes.RevokedAt.Unix()
	}

	res.OCSP = ocspStapledRes
}

func (res *HTTPResponse) Json() goja.Value {
	if res.cachedJSON == nil {
		var v interface{}
		if err := json.Unmarshal([]byte(res.Body), &v); err != nil {
			common.Throw(common.GetRuntime(res.ctx), err)
		}
		res.cachedJSON = common.GetRuntime(res.ctx).ToValue(v)
	}
	return res.cachedJSON
}

func (res *HTTPResponse) Html(selector ...string) html.Selection {
	sel, err := html.HTML{}.ParseHTML(res.ctx, res.Body)
	if err != nil {
		common.Throw(common.GetRuntime(res.ctx), err)
	}
	sel.URL = res.URL
	if len(selector) > 0 {
		sel = sel.Find(selector[0])
	}
	return sel
}

func (res *HTTPResponse) SubmitForm(args... goja.Value) (*HTTPResponse, error) {
	rt := common.GetRuntime(res.ctx)

	formSelector := "form"
	submitSelector := "[type=\"submit\"]"
	var fields map[string]goja.Value
	var requestOptions goja.Value
	if len(args) > 0{
		params := args[0].ToObject(rt)
		for _, k := range params.Keys() {
			switch k {
			case "formSelector":
				formSelector = params.Get(k).Export().(string)
			case "submitSelector":
				formSelector = params.Get(k).Export().(string)
			case "fields":
				rt.ExportTo(params.Get(k), &fields)
			case "options":
				requestOptions = params.Get(k)
			}
		}
	}

	form := res.Html(formSelector)

	methodAttr := form.Attr("method")
	var requestMethod string
	if methodAttr == goja.Undefined(){
		// Use GET by default
		requestMethod = "GET"
	} else{
		requestMethod = strings.ToUpper(methodAttr.Export().(string))
	}

	actionAttr := form.Attr("action")
	var requestUrl goja.Value
	if actionAttr == goja.Undefined(){
		// Use the url of the response if no action is set
		requestUrl = rt.ToValue(res.URL)
	} else{
		// Resolve the action url from the response url
		responseUrl, _ := url.Parse(res.URL)
		actionUrl, _ := url.Parse(actionAttr.Export().(string))
		requestUrl = rt.ToValue(responseUrl.ResolveReference(actionUrl).String())
	}

	body := form.SerializeObject()
	if fields != nil{
		for k, v := range fields {
			body[k] = rt.ToValue(v)
		}
	}

	// Add the name + value of the submit button to the fields
	submit := form.Find(submitSelector)
	submitName := submit.Attr("name")
	submitValue := submit.Val()
	if submitName != goja.Undefined() && submitValue != goja.Undefined(){
		body[submitName.String()] = submitValue
	}

	if requestOptions == nil {
		return New().Request(res.ctx, requestMethod, requestUrl, rt.ToValue(body))
	} else {
		return New().Request(res.ctx, requestMethod, requestUrl, rt.ToValue(body), requestOptions)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014, Truveris Inc. All Rights Reserved.
// Use of this source code is governed by the ISC license in the LICENSE file.

package main

import (
	"strings"

	"github.com/truveris/ygor"
)

type SayModule struct{}

func (module SayModule) PrivMsg(msg *ygor.PrivMsg) {}

func SayCommand(msg *ygor.Message) {
	body := "say " + strings.Join(msg.Args, " ")
	SendToChannelMinions(msg.ReplyTo, body)
}

func (module SayModule) Init() {
	ygor.RegisterCommand(ygor.Command{
		Name:            "say",
		PrivMsgFunction: SayCommand,
		Addressed:       true,
		AllowPrivate:    false,
		AllowChannel:    true,
	})
}
<commit_msg>castrate the 'say' module temporarily<commit_after>// Copyright 2014, Truveris Inc. All Rights Reserved.
// Use of this source code is governed by the ISC license in the LICENSE file.

package main

import (
	"strings"

	"github.com/truveris/ygor"
)

type SayModule struct{}

func (module SayModule) PrivMsg(msg *ygor.PrivMsg) {}

func SayCommand(msg *ygor.Message) {
	args := make([]string, 0)
	if len(msg.Args) > 3 {
		IRCPrivAction(msg.ReplyTo, "cropping to 3 words until someone works on ticket 7651")
		args = msg.Args[:3]
	} else {
		args = msg.Args
	}
	body := "say " + strings.Join(args, " ")
	SendToChannelMinions(msg.ReplyTo, body)
}

func (module SayModule) Init() {
	ygor.RegisterCommand(ygor.Command{
		Name:            "say",
		PrivMsgFunction: SayCommand,
		Addressed:       true,
		AllowPrivate:    false,
		AllowChannel:    true,
	})
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"log"
	"time"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/robfig/cron"
)

type (
	Task interface {
		run(f *FollowUp) func()
		spec() string
	}

	Tasks []Task

	GatherTask    struct{}
	RemindersTask struct{}
	HelpMeTask    struct{}
	YouThereTask  struct{}

	FollowUp struct {
		c *cron.Cron
		*session
		users Users
	}
)

func NewFollowUp(s *session) *FollowUp {
	sched := &FollowUp{
		session: s,
		c:       cron.New(),
		users:   Users{},
	}
	sched.setup([]Task{&GatherTask{}, &RemindersTask{}, &HelpMeTask{}, &YouThereTask{}})

	return sched
}

func (this *FollowUp) setup(t Tasks) {
	for i := range t {
		this.c.AddFunc(t[i].spec(), t[i].run(this))
	}
}

func (this *FollowUp) Start() {
	this.c.Start()
	return
}

func (this *FollowUp) Stop() {
	this.c.Stop()
	return
}

func (this *GatherTask) run(fu *FollowUp) func() {
	return func() {
		log.Println("Running gather info ....")
		users, err := fu.session.Storage.GetUsers()
		if err != nil {
			log.Println("Trying to run scheduled task ", err.Error())
			log.Println("Will bail ...")
			return
		}

		for i := range users {

			n, err := fu.session.Storage.GetLatest(users[i], 10)

			if err != nil {
				log.Println("Error getting latest ", err.Error())
				break
			}

			user := fu.users.GetUser(users[i])
			if user == nil {
				user = &User{}
			}
			user.id = users[i]
			if len(n) > 0 {
				log.Println("Adding last 10 notes for user ...", len(n))
				user.recent = n
				user.lastChat = n.SortByDate()[0].AddedOn
			}
			log.Println("Adding user after gathering info", user)
			fu.users.AddUser(user)
		}
		return
	}
}

func (this *GatherTask) spec() string {
	return "@daily"
}

func (this *RemindersTask) run(fu *FollowUp) func() {
	return func() {
		log.Println("Running reminders ....")
		for i := range fu.users {
			for r := range fu.users[i].GetReminders() {
				reminder := fu.users[i].GetReminders()[r]
				if reminder.RemindToday() {
					fu.session.User = fu.users[i].ToBotUser()
					fu.session.send(reminder.Text)
				}
			}

		}
	}
}

func (this *RemindersTask) spec() string {
	return "0 0/15 * * *"
}

func (this *HelpMeTask) run(fu *FollowUp) func() {
	return func() {
		log.Println("Running `Help me` ....")
		return
	}
}

func (this *HelpMeTask) spec() string {
	return "@daily"
}

func (this *YouThereTask) run(fu *FollowUp) func() {
	return func() {
		log.Println("Running `you there?` ....")
		now := time.Now()
		for i := range fu.users {

			fu.session.User = fu.users[i].ToBotUser()

			last := fu.users[i].lastChat
			if now.YearDay()-last.YearDay() > 7 {
				fu.session.send(fmt.Sprintf("Long time no chat! Wanna %s or %s something?", chat_action, say_action))
			}
		}
	}
}

func (this *YouThereTask) spec() string {
	return "@daily"
}
<commit_msg>every five while testing ...<commit_after>package lib

import (
	"fmt"
	"log"
	"time"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/robfig/cron"
)

type (
	Task interface {
		run(f *FollowUp) func()
		spec() string
	}

	Tasks []Task

	GatherTask    struct{}
	RemindersTask struct{}
	HelpMeTask    struct{}
	YouThereTask  struct{}

	FollowUp struct {
		c *cron.Cron
		*session
		users Users
	}
)

func NewFollowUp(s *session) *FollowUp {
	sched := &FollowUp{
		session: s,
		c:       cron.New(),
		users:   Users{},
	}
	sched.setup([]Task{&GatherTask{}, &RemindersTask{}, &HelpMeTask{}, &YouThereTask{}})

	return sched
}

func (this *FollowUp) setup(t Tasks) {
	for i := range t {
		this.c.AddFunc(t[i].spec(), t[i].run(this))
	}
}

func (this *FollowUp) Start() {
	this.c.Start()
	return
}

func (this *FollowUp) Stop() {
	this.c.Stop()
	return
}

func (this *GatherTask) run(fu *FollowUp) func() {
	return func() {
		log.Println("Running gather info ....")
		users, err := fu.session.Storage.GetUsers()
		if err != nil {
			log.Println("Trying to run scheduled task ", err.Error())
			log.Println("Will bail ...")
			return
		}

		for i := range users {

			n, err := fu.session.Storage.GetLatest(users[i], 10)

			if err != nil {
				log.Println("Error getting latest ", err.Error())
				break
			}

			user := fu.users.GetUser(users[i])
			if user == nil {
				user = &User{}
			}
			user.id = users[i]
			if len(n) > 0 {
				log.Println("Adding last 10 notes for user ...", len(n))
				user.recent = n
				user.lastChat = n.SortByDate()[0].AddedOn
			}
			log.Println("Adding user after gathering info", user)
			fu.users.AddUser(user)
		}
		return
	}
}

func (this *GatherTask) spec() string {
	return "0 0/5 * * *"
}

func (this *RemindersTask) run(fu *FollowUp) func() {
	return func() {
		log.Println("Running reminders ....")
		for i := range fu.users {
			for r := range fu.users[i].GetReminders() {
				reminder := fu.users[i].GetReminders()[r]
				if reminder.RemindToday() {
					fu.session.User = fu.users[i].ToBotUser()
					fu.session.send(reminder.Text)
				}
			}

		}
	}
}

func (this *RemindersTask) spec() string {
	return "0 0/5 * * *"
}

func (this *HelpMeTask) run(fu *FollowUp) func() {
	return func() {
		log.Println("Running `Help me` ....")
		return
	}
}

func (this *HelpMeTask) spec() string {
	return "0 0/5 * * *"
}

func (this *YouThereTask) run(fu *FollowUp) func() {
	return func() {
		log.Println("Running `you there?` ....")
		now := time.Now()
		for i := range fu.users {

			fu.session.User = fu.users[i].ToBotUser()

			last := fu.users[i].lastChat
			if now.YearDay()-last.YearDay() > 7 {
				fu.session.send(fmt.Sprintf("Long time no chat! Wanna %s or %s something?", chat_action, say_action))
			}
		}
	}
}

func (this *YouThereTask) spec() string {
	return "0 0/5 * * *"
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
)

const (
	//being a good citizen
	start_cmd, help_cmd = "/start", "/help"

	chat_cmd, remind_cmd, say_cmd, reminders_cmd, said_cmd = "/chat", "/remind", "/say", "/reminders", "/said"

	remind_cmd_hint, say_cmd_hint = "/remind me in <days> to <message>", "/say [what you want to say]"

	default_script = "default"
	stickers_chat  = "chat"
)

type (
	Info struct {
		App       []string `json:"appInfo"`
		Reminders []string `json:"remindersInfo"`
		Chat      []string `json:"chatInfo"`
		Said      []string `json:"saidInfo"`
	}

	QProcess struct {
		s    *session
		lang struct {
			questions `json:"QandA"`
		}
		info     *Info
		next     *Question
		lastTime Notes
		sLib     Stickers
	}
)

func NewQProcess(b *telebot.Bot, s *Storage) *QProcess {
	q := &QProcess{s: newSession(b, s), sLib: LoadKnownStickers()}
	q.loadInfo()
	return q
}

func (this *QProcess) Run(input <-chan telebot.Message) {
	for msg := range input {

		this.s.User = msg.Sender
		if msg.Sticker.Exists() {
			log.Println("incoming sticker", msg.Sticker.FileID)

			s := this.sLib.FindSticker(msg.Sticker.FileID)
			if s != nil {
				log.Println("We know what to do with this sticker ...", s.Meaning, s.SaveTag)
				this.loadScript(stickers_chat)
				//get going
				this.findNextStickerQ(s, msg).andChat()
			}
		} else {
			this.
				quickWinFirst(msg).
				determineScript(msg).
				findNextQuestion(msg).
				andChat()
		}
	}
}

func (this *QProcess) quickWinFirst(msg telebot.Message) *QProcess {

	if isCmd(msg.Text, start_cmd, help_cmd) {

		appInfo := fmt.Sprintf("%s %s %s %s %s %s",
			this.info.App,
			chat_cmd+" - to have a *quick chat* about what your upto \n\n",
			say_cmd_hint+" - to say *anything* thats on your mind \n\n",
			said_cmd+" - to show all the things you have said \n\n",
			remind_cmd_hint+" - to keep track of the things you need to be reminded about \n\n",
			reminders_cmd+" - to show all the reminders you have made",
		)

		this.s.send(appInfo)
	} else if hasSubmisson(msg.Text, say_cmd) {
		log.Println("save something said ", msg.Text)
		this.s.save(NewNote(msg, say_cmd))
	} else if hasSubmisson(msg.Text, remind_cmd) {
		log.Println("save a reminder ", msg.Text)
		this.s.save(NewReminderNote(msg))
	} else if hasSubmisson(msg.Text, reminders_cmd) || isCmd(msg.Text, reminders_cmd) {
		log.Println("showing reminders ", msg.Text)
		r := this.s.getReminders(msg)
		this.s.send(this.info.Reminders...)
		for i := range r {
			this.s.send(r[i].ToString())

		}
	} else if hasSubmisson(msg.Text, said_cmd) || isCmd(msg.Text, said_cmd) {
		log.Println("showing things said ", msg.Text)
		r := this.s.getNotes(msg)
		this.s.send(this.info.Said...)
		for i := range r {
			this.s.send(r[i].ToString())
		}
	}
	return this
}

func (this *QProcess) loadInfo() {
	file, err := os.Open("./config/fantail.json")
	if err != nil {
		log.Panic("could not load App info", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.info)
	if err != nil {
		log.Panic("could not decode App info ", err.Error())
	}
}

func (this *QProcess) loadScript(scriptName string) {
	file, err := os.Open(fmt.Sprintf("./config/%s.json", scriptName))
	if err != nil {
		log.Panic("could not load QandA language file ", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.lang)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}
}

func (this *QProcess) determineScript(msg telebot.Message) *QProcess {

	if hasSubmisson(msg.Text, remind_cmd, say_cmd) {
		log.Println("load default script after submisson")
		this.loadScript(default_script)
	} else if isCmd(msg.Text, chat_cmd, said_cmd, say_cmd, remind_cmd, reminders_cmd) {
		words := strings.Fields(msg.Text)
		scriptName := strings.SplitAfter(words[0], "/")[1]
		log.Println("load command script", scriptName)
		this.loadScript(scriptName)
	}
	return this
}

func (this *QProcess) findNextQuestion(msg telebot.Message) *QProcess {
	this.next = nil

	if isCmd(msg.Text, chat_cmd, said_cmd, say_cmd, remind_cmd, reminders_cmd) {
		//start at the beginning - covers submissons also
		this.next = this.lang.questions[0]
		return this
	} else {
		//find the next question
		for i := range this.lang.questions {
			for a := range this.lang.questions[i].RelatesTo.Answers {
				if this.lang.questions[i].RelatesTo.Answers[a] == msg.Text {
					//was the answer a remainder to save?
					if this.lang.questions[i].RelatesTo.Save {
						this.s.save(NewNote(msg, chat_cmd, this.lang.questions[i].RelatesTo.SaveTag))
						this.lastTime = append(this.lastTime, this.s.getLastChat(this.lang.questions[i].RelatesTo.SaveTag))
					}
					this.next = this.lang.questions[i]
					return this
				}
			}
		}
	}
	log.Println("looks like we are all done!")
	return this
}

func (this *QProcess) findNextStickerQ(s *Sticker, msg telebot.Message) *QProcess {
	this.next = nil

	for i := range this.lang.questions {
		for a := range this.lang.questions[i].RelatesTo.Answers {
			for si := range s.Ids {
				if this.lang.questions[i].RelatesTo.Answers[a] == s.Ids[si] {
					if this.lang.questions[i].RelatesTo.Save {
						this.s.save(s.ToNote(msg))
					}
					this.next = this.lang.questions[i]
					return this
				}
			}
		}
	}

	log.Println("looks like we are all done!")
	return this
}

func (this *QProcess) makeKeyboard() Keyboard {
	keyboard := Keyboard{}
	for i := range this.next.PossibleAnswers {
		keyboard = append(keyboard, []string{this.next.PossibleAnswers[i]})
	}
	return keyboard
}

func (this *QProcess) andChat() {
	if this.next != nil {
		this.s.send(this.next.Context...)
		this.s.sendWithKeyboard(this.next.QuestionText, this.makeKeyboard())
		return
	}

	if len(this.lastTime) > 0 {
		lastTimeTxt := strings.Join(this.info.Chat, "\n\n")
		for i := range this.lastTime {
			lastTimeTxt = lastTimeTxt + "\n\n" + this.lastTime[i].ToString()
		}
		this.s.send(lastTimeTxt)
		this.lastTime = nil
	}
	return
}
<commit_msg>just log last time for now<commit_after>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
)

const (
	//being a good citizen
	start_cmd, help_cmd = "/start", "/help"

	chat_cmd, remind_cmd, say_cmd, reminders_cmd, said_cmd = "/chat", "/remind", "/say", "/reminders", "/said"

	remind_cmd_hint, say_cmd_hint = "/remind me in <days> to <message>", "/say [what you want to say]"

	default_script = "default"
	stickers_chat  = "chat"
)

type (
	Info struct {
		App       []string `json:"appInfo"`
		Reminders []string `json:"remindersInfo"`
		Chat      []string `json:"chatInfo"`
		Said      []string `json:"saidInfo"`
	}

	QProcess struct {
		s    *session
		lang struct {
			questions `json:"QandA"`
		}
		info     *Info
		next     *Question
		lastTime Notes
		sLib     Stickers
	}
)

func NewQProcess(b *telebot.Bot, s *Storage) *QProcess {
	q := &QProcess{s: newSession(b, s), sLib: LoadKnownStickers()}
	q.loadInfo()
	return q
}

func (this *QProcess) Run(input <-chan telebot.Message) {
	for msg := range input {

		this.s.User = msg.Sender
		if msg.Sticker.Exists() {
			log.Println("incoming sticker", msg.Sticker.FileID)

			s := this.sLib.FindSticker(msg.Sticker.FileID)
			if s != nil {
				log.Println("We know what to do with this sticker ...", s.Meaning, s.SaveTag)
				this.loadScript(stickers_chat)
				//get going
				this.findNextStickerQ(s, msg).andChat()
			}
		} else {
			this.
				quickWinFirst(msg).
				determineScript(msg).
				findNextQuestion(msg).
				andChat()
		}
	}
}

func (this *QProcess) quickWinFirst(msg telebot.Message) *QProcess {

	if isCmd(msg.Text, start_cmd, help_cmd) {

		appInfo := fmt.Sprintf("%s %s %s %s %s %s",
			this.info.App,
			chat_cmd+" - to have a *quick chat* about what your upto \n\n",
			say_cmd_hint+" - to say *anything* thats on your mind \n\n",
			said_cmd+" - to show all the things you have said \n\n",
			remind_cmd_hint+" - to keep track of the things you need to be reminded about \n\n",
			reminders_cmd+" - to show all the reminders you have made",
		)

		this.s.send(appInfo)
	} else if hasSubmisson(msg.Text, say_cmd) {
		log.Println("save something said ", msg.Text)
		this.s.save(NewNote(msg, say_cmd))
	} else if hasSubmisson(msg.Text, remind_cmd) {
		log.Println("save a reminder ", msg.Text)
		this.s.save(NewReminderNote(msg))
	} else if hasSubmisson(msg.Text, reminders_cmd) || isCmd(msg.Text, reminders_cmd) {
		log.Println("showing reminders ", msg.Text)
		r := this.s.getReminders(msg)
		this.s.send(this.info.Reminders...)
		for i := range r {
			this.s.send(r[i].ToString())

		}
	} else if hasSubmisson(msg.Text, said_cmd) || isCmd(msg.Text, said_cmd) {
		log.Println("showing things said ", msg.Text)
		r := this.s.getNotes(msg)
		this.s.send(this.info.Said...)
		for i := range r {
			this.s.send(r[i].ToString())
		}
	}
	return this
}

func (this *QProcess) loadInfo() {
	file, err := os.Open("./config/fantail.json")
	if err != nil {
		log.Panic("could not load App info", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.info)
	if err != nil {
		log.Panic("could not decode App info ", err.Error())
	}
}

func (this *QProcess) loadScript(scriptName string) {
	file, err := os.Open(fmt.Sprintf("./config/%s.json", scriptName))
	if err != nil {
		log.Panic("could not load QandA language file ", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.lang)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}
}

func (this *QProcess) determineScript(msg telebot.Message) *QProcess {

	if hasSubmisson(msg.Text, remind_cmd, say_cmd) {
		log.Println("load default script after submisson")
		this.loadScript(default_script)
	} else if isCmd(msg.Text, chat_cmd, said_cmd, say_cmd, remind_cmd, reminders_cmd) {
		words := strings.Fields(msg.Text)
		scriptName := strings.SplitAfter(words[0], "/")[1]
		log.Println("load command script", scriptName)
		this.loadScript(scriptName)
	}
	return this
}

func (this *QProcess) findNextQuestion(msg telebot.Message) *QProcess {
	this.next = nil

	if isCmd(msg.Text, chat_cmd, said_cmd, say_cmd, remind_cmd, reminders_cmd) {
		//start at the beginning - covers submissons also
		this.next = this.lang.questions[0]
		return this
	} else {
		//find the next question
		for i := range this.lang.questions {
			for a := range this.lang.questions[i].RelatesTo.Answers {
				if this.lang.questions[i].RelatesTo.Answers[a] == msg.Text {
					//was the answer a remainder to save?
					if this.lang.questions[i].RelatesTo.Save {
						this.s.save(NewNote(msg, chat_cmd, this.lang.questions[i].RelatesTo.SaveTag))
						this.lastTime = append(this.lastTime, this.s.getLastChat(this.lang.questions[i].RelatesTo.SaveTag))
					}
					this.next = this.lang.questions[i]
					return this
				}
			}
		}
	}
	log.Println("looks like we are all done!")
	return this
}

func (this *QProcess) findNextStickerQ(s *Sticker, msg telebot.Message) *QProcess {
	this.next = nil

	for i := range this.lang.questions {
		for a := range this.lang.questions[i].RelatesTo.Answers {
			for si := range s.Ids {
				if this.lang.questions[i].RelatesTo.Answers[a] == s.Ids[si] {
					if this.lang.questions[i].RelatesTo.Save {
						this.s.save(s.ToNote(msg))
					}
					this.next = this.lang.questions[i]
					return this
				}
			}
		}
	}

	log.Println("looks like we are all done!")
	return this
}

func (this *QProcess) makeKeyboard() Keyboard {
	keyboard := Keyboard{}
	for i := range this.next.PossibleAnswers {
		keyboard = append(keyboard, []string{this.next.PossibleAnswers[i]})
	}
	return keyboard
}

func (this *QProcess) andChat() {
	if this.next != nil {
		this.s.send(this.next.Context...)
		this.s.sendWithKeyboard(this.next.QuestionText, this.makeKeyboard())
		return
	}

	if len(this.lastTime) > 0 {
		lastTimeTxt := strings.Join(this.info.Chat, "\n\n")
		for i := range this.lastTime {
			lastTimeTxt = lastTimeTxt + "\n\n" + this.lastTime[i].ToString()
		}
		//this.s.send(lastTimeTxt)
		log.Println("Last Time ", lastTimeTxt)
		this.lastTime = nil
	}
	return
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
)

const (
	chat_cmd, remind_cmd, reminders_cmd, notes_cmd = "/chat", "/remind", "/reminders", "/notes"

	free_form      = "JUST_SAYING"
	default_script = "default"
)

type QProcess struct {
	Details *Details
	lang    struct {
		questions `json:"QandA"`
	}
	next *Question
	done bool
}

func NewQProcess(d *Details) *QProcess {
	return &QProcess{Details: d}
}

func (this *QProcess) Run(input <-chan telebot.Message) {
	for msg := range input {
		this.Details.User = msg.Chat
		this.quickWin(msg).determineScript(msg).findNextQuestion(msg).andAsk()
	}
}

func (this *QProcess) quickWin(msg telebot.Message) *QProcess {

	/*if hasSubmisson(msg.Text, remind_cmd) {
		log.Println("making submisson ", msg.Text)
		this.Details.saveAsReminder(msg)
	} else */
	if isCmd(msg.Text, reminders_cmd) {
		log.Println("showing reminders ", msg.Text)

		r := this.Details.getReminders(fmt.Sprintf("%d", this.Details.User.ID))
		for i := range r {
			if r[i].RemindToday() {
				this.Details.send(r[i].Text)

			}
		}
	} else if isCmd(msg.Text, notes_cmd) {
		log.Println("showing notes ", msg.Text)

		r := this.Details.getNotes(fmt.Sprintf("%d", this.Details.User.ID))
		for i := range r {
			if r[i].IsCurrent() {
				this.Details.send(r[i].Text)
			}
		}
	}
	return this
}

func (this *QProcess) loadScript(scriptName string) {
	file, err := os.Open(fmt.Sprintf("./config/%s.json", scriptName))
	if err != nil {
		log.Panic("could not load QandA language file ", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.lang)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}
}

func (this *QProcess) determineScript(msg telebot.Message) *QProcess {

	if isCmd(msg.Text, chat_cmd, notes_cmd) { //remind_cmd, chat_cmd, reminders_cmd, notes_cmd) {
		words := strings.Fields(msg.Text)
		scriptName := strings.SplitAfter(words[0], "/")[1]
		this.loadScript(scriptName)
	}
	return this
}

func (this *QProcess) findNextQuestion(msg telebot.Message) *QProcess {
	this.next = nil

	/*if this.done || this.lang.questions == nil {
		log.Println("unknown so will save as", free_form)
		this.Details.save(msg, free_form)
		//load default and start at the beginning
		this.loadScript(default_script)
		this.next = this.lang.questions[0]
		this.done = false
		return this
	} else */
	if isCmd(msg.Text, chat_cmd, notes_cmd) { //|| hasSubmisson(msg.Text, remind_cmd) {
		//start at the beginning
		this.next = this.lang.questions[0]
		//this.done = false
		return this
	} else {
		//find the next question
		searched := false
		for i := range this.lang.questions {
			log.Println("looking next q ...")
			for a := range this.lang.questions[i].RelatesTo.Answers {
				searched = true
				if this.lang.questions[i].RelatesTo.Answers[a] == msg.Text {
					//was the answer a remainder to save?
					if this.lang.questions[i].RelatesTo.Save {
						this.Details.save(msg, chat_cmd, this.lang.questions[i].RelatesTo.SaveTag)
					}
					this.next = this.lang.questions[i]
					//this.done = false
					return this
				}
			}
		}
		if !searched {
			log.Println("was just saying so we are just saving")
			this.Details.save(msg, free_form)
			//load default and start at the beginning
			this.loadScript(default_script)
			this.next = this.lang.questions[0]
			//this.done = false
			return this
		}
	}
	log.Println("looks like we are all done!")
	//this.done = true
	return this
}

func (this *QProcess) makeKeyboard() Keyboard {
	keyboard := Keyboard{}
	for i := range this.next.PossibleAnswers {
		keyboard = append(keyboard, []string{this.next.PossibleAnswers[i]})
	}
	return keyboard
}

func (this *QProcess) andAsk() {
	if this.next != nil {
		//context
		for i := range this.next.Context {
			this.Details.send(this.next.Context[i])
		}
		//the actual question
		this.Details.sendWithKeyboard(this.next.QuestionText, this.makeKeyboard())
	}

	return
}
<commit_msg>save notes<commit_after>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
)

const (
	chat_cmd, remind_cmd, reminders_cmd, notes_cmd = "/chat", "/remind", "/reminders", "/notes"

	free_form      = "JUST_SAYING"
	default_script = "default"
)

type QProcess struct {
	Details *Details
	lang    struct {
		questions `json:"QandA"`
	}
	next *Question
	done bool
}

func NewQProcess(d *Details) *QProcess {
	return &QProcess{Details: d}
}

func (this *QProcess) Run(input <-chan telebot.Message) {
	for msg := range input {
		this.Details.User = msg.Chat
		this.quickWinFirst(msg).
			determineScript(msg).
			findNextQuestion(msg).
			orSaveNote(msg).
			andAsk()
	}
}

func (this *QProcess) quickWinFirst(msg telebot.Message) *QProcess {

	/*if hasSubmisson(msg.Text, remind_cmd) {
		log.Println("making submisson ", msg.Text)
		this.Details.saveAsReminder(msg)
	} else */
	if isCmd(msg.Text, reminders_cmd) {
		log.Println("showing reminders ", msg.Text)

		r := this.Details.getReminders(fmt.Sprintf("%d", this.Details.User.ID))
		for i := range r {
			if r[i].RemindToday() {
				this.Details.send(r[i].Text)

			}
		}
	} else if isCmd(msg.Text, notes_cmd) {
		log.Println("showing notes ", msg.Text)

		r := this.Details.getNotes(fmt.Sprintf("%d", this.Details.User.ID))
		for i := range r {
			if r[i].IsCurrent() {
				this.Details.send(r[i].Text)
			}
		}
	}
	return this
}

func (this *QProcess) loadScript(scriptName string) {
	file, err := os.Open(fmt.Sprintf("./config/%s.json", scriptName))
	if err != nil {
		log.Panic("could not load QandA language file ", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.lang)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}
}

func (this *QProcess) determineScript(msg telebot.Message) *QProcess {

	if isCmd(msg.Text, chat_cmd, notes_cmd) { //remind_cmd, chat_cmd, reminders_cmd, notes_cmd) {
		words := strings.Fields(msg.Text)
		scriptName := strings.SplitAfter(words[0], "/")[1]
		this.loadScript(scriptName)
	}
	return this
}

func (this *QProcess) findNextQuestion(msg telebot.Message) *QProcess {
	this.next = nil

	/*if this.done || this.lang.questions == nil {
		log.Println("unknown so will save as", free_form)
		this.Details.save(msg, free_form)
		//load default and start at the beginning
		this.loadScript(default_script)
		this.next = this.lang.questions[0]
		this.done = false
		return this
	} else */
	if isCmd(msg.Text, chat_cmd, notes_cmd) { //|| hasSubmisson(msg.Text, remind_cmd) {
		//start at the beginning
		this.next = this.lang.questions[0]
		//this.done = false
		return this
	} else {
		//find the next question
		for i := range this.lang.questions {
			log.Println("looking next q ...")
			for a := range this.lang.questions[i].RelatesTo.Answers {
				if this.lang.questions[i].RelatesTo.Answers[a] == msg.Text {
					//was the answer a remainder to save?
					if this.lang.questions[i].RelatesTo.Save {
						this.Details.save(msg, chat_cmd, this.lang.questions[i].RelatesTo.SaveTag)
					}
					this.next = this.lang.questions[i]
					//this.done = false
					return this
				}
			}
		}
	}
	log.Println("looks like we are all done!")
	//this.done = true
	return this
}

func (this *QProcess) orSaveNote(msg telebot.Message) *QProcess {
	if this.next == nil {
		log.Println("was just saying so we are just saving")
		this.Details.save(msg, free_form)
		this.loadScript(default_script)
		this.next = this.lang.questions[0]
	}
	return this
}

func (this *QProcess) makeKeyboard() Keyboard {
	keyboard := Keyboard{}
	for i := range this.next.PossibleAnswers {
		keyboard = append(keyboard, []string{this.next.PossibleAnswers[i]})
	}
	return keyboard
}

func (this *QProcess) andAsk() {
	if this.next != nil {
		//context
		for i := range this.next.Context {
			this.Details.send(this.next.Context[i])
		}
		//the actual question
		this.Details.sendWithKeyboard(this.next.QuestionText, this.makeKeyboard())
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"io/ioutil"

	plus "github.com/google/google-api-go-client/plus/v1"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

var cfg *oauth2.Config

func main() {
	// read oauth2 config
	env := "GOOGLE_OAUTH2_CONFIG"
	if os.Getenv(env) == "" {
		panic(errors.New(env + " is not set"))
	}
	b, err := ioutil.ReadFile(os.Getenv(env))
	if err != nil {
		panic(errors.Wrap(err, "failed to parse config file"))
	}
	authConf, err := google.ConfigFromJSON(b)
	if err != nil {
		panic(errors.Wrap(err, "failed to parse config file"))
	}
	cfg = authConf

	// set up server
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "We log coffee!")
	})
	mux.HandleFunc("/login", login)
	mux.HandleFunc("/oauth2callback", oauth2Callback)
	srv := http.Server{
		Addr:    "127.0.0.1:8000",
		Handler: mux,
	}
	log.Printf("listening at %s", srv.Addr)
	log.Fatal(errors.Wrap(srv.ListenAndServe(), "failed to listen/serve"))
}

func login(w http.ResponseWriter, r *http.Request) {
	url := cfg.AuthCodeURL("todo_rand_state",
		oauth2.SetAuthURLParam("access_type", "offline"),
		oauth2.SetAuthURLParam("scope", "profile"))
	w.Header().Set("Location", url)
	w.WriteHeader(http.StatusFound)
}

func oauth2Callback(w http.ResponseWriter, r *http.Request) {
	if state := r.URL.Query().Get("state"); state != "todo_rand_state" {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "wrong state")
	}

	code := r.URL.Query().Get("code")
	if code == "" {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "missing oauth2 grant code")
		return
	}

	tok, err := cfg.Exchange(oauth2.NoContext, code)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, errors.Wrap(err, "oauth2 token exchange failed"))
		return
	}
	fmt.Printf("%v\n", tok)

	svc, err := plus.New(oauth2.NewClient(oauth2.NoContext,
		cfg.TokenSource(oauth2.NoContext, tok)))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, errors.Wrap(err, "failed to construct g+ client"))
		return
	}
	me, err := plus.NewPeopleService(svc).Get("me").Do()
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, errors.Wrap(err, "failed to query user g+ profile"))
		return
	}
	log.Printf("Logged in user id: %s", me.Id)
	fmt.Fprintf(w, "Hello "+me.DisplayName)
}
<commit_msg>web: use oauth2.Client for auto token refreshing<commit_after>package main

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

	plus "github.com/google/google-api-go-client/plus/v1"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

var cfg *oauth2.Config

func main() {
	// read oauth2 config
	env := "GOOGLE_OAUTH2_CONFIG"
	if os.Getenv(env) == "" {
		panic(errors.New(env + " is not set"))
	}
	b, err := ioutil.ReadFile(os.Getenv(env))
	if err != nil {
		panic(errors.Wrap(err, "failed to parse config file"))
	}
	authConf, err := google.ConfigFromJSON(b)
	if err != nil {
		panic(errors.Wrap(err, "failed to parse config file"))
	}
	cfg = authConf

	// set up server
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "We log coffee!")
	})
	mux.HandleFunc("/login", login)
	mux.HandleFunc("/oauth2callback", oauth2Callback)
	srv := http.Server{
		Addr:    "127.0.0.1:8000",
		Handler: mux,
	}
	log.Printf("listening at %s", srv.Addr)
	log.Fatal(errors.Wrap(srv.ListenAndServe(), "failed to listen/serve"))
}

func login(w http.ResponseWriter, r *http.Request) {
	url := cfg.AuthCodeURL("todo_rand_state",
		oauth2.SetAuthURLParam("access_type", "offline"),
		oauth2.SetAuthURLParam("scope", "profile"))
	w.Header().Set("Location", url)
	w.WriteHeader(http.StatusFound)
}

func oauth2Callback(w http.ResponseWriter, r *http.Request) {
	if state := r.URL.Query().Get("state"); state != "todo_rand_state" {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "wrong state")
	}

	code := r.URL.Query().Get("code")
	if code == "" {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "missing oauth2 grant code")
		return
	}

	tok, err := cfg.Exchange(oauth2.NoContext, code)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, errors.Wrap(err, "oauth2 token exchange failed"))
		return
	}
	svc, err := plus.New(cfg.Client(oauth2.NoContext, tok))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, errors.Wrap(err, "failed to construct g+ client"))
		return
	}
	me, err := plus.NewPeopleService(svc).Get("me").Do()
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, errors.Wrap(err, "failed to query user g+ profile"))
		return
	}
	log.Printf("Logged in user id: %s", me.Id)
	fmt.Fprintf(w, "Hello "+me.DisplayName)
}
<|endoftext|>


<commit_before>package engine

import (
	"adexchange/lib"
	m "adexchange/models"
	"github.com/astaxie/beego"
	"gopkg.in/vmihailenco/msgpack.v2"
)

var _demandLogPool chan *m.AdResponse
var _mhQueuePool chan *MHQueueData
var _reqLogPool chan *m.AdRequest
var _impLogPool chan *m.AdRequest
var _clkLogPool chan *m.AdRequest

func init() {

	_demandLogPool = make(chan *m.AdResponse, 5000)
	_mhQueuePool = make(chan *MHQueueData, 5000)
	_reqLogPool = make(chan *m.AdRequest, 2000)
	_impLogPool = make(chan *m.AdRequest, 1000)
	_clkLogPool = make(chan *m.AdRequest, 1000)

}

func StartDemandLogService() {

	for {
		adResponse := <-_demandLogPool
		b, err := msgpack.Marshal(adResponse)

		if err == nil {
			c := lib.GetQueuePool().Get()
			c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_DEMAND", b)
			defer c.Close()

		} else {
			beego.Critical(err.Error())
		}
	}

}

func StartMHQueueService() {

	for {
		mhQueueData := <-_mhQueuePool
		b, err := msgpack.Marshal(mhQueueData.AdResponse)

		if err == nil {
			c := lib.GetQueuePool().Get()
			c.Do("lpush", beego.AppConfig.String("runmode")+mhQueueData.QueueName, b)
			defer c.Close()

		} else {
			beego.Critical(err.Error())
		}
	}

}

func StartReqLogService() {

	for {
		adRequest := <-_reqLogPool
		b, err := msgpack.Marshal(adRequest)

		if err == nil {
			c := lib.GetQueuePool().Get()

			c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_REQ", b)
			defer c.Close()

		} else {
			beego.Critical(err.Error())
		}
	}

}

func StartImpLogService() {

	for {
		adRequest := <-_impLogPool
		b, err := msgpack.Marshal(adRequest)

		if err == nil {
			c := lib.GetQueuePool().Get()
			c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_IMP", b)
			defer c.Close()

		} else {
			beego.Critical(err.Error())
		}
	}

}

func StartClkLogService() {

	for {
		adRequest := <-_clkLogPool
		b, err := msgpack.Marshal(adRequest)

		if err == nil {
			c := lib.GetQueuePool().Get()
			c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_CLK", b)
			defer c.Close()

		} else {
			beego.Error(err.Error())
		}
	}

}

func SendDemandLog(adResponse *m.AdResponse) {
	if adResponse != nil {
		_demandLogPool <- adResponse
	}
	//c := lib.Pool.Get()
	//b, err := msgpack.Marshal(adResponse)

	//if err == nil {
	//	c = lib.Pool.Get()
	//	c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_DEMAND", b)
	//} else {
	//	beego.Error(err.Error())
	//}
}

func SendRequestLog(adRequest *m.AdRequest, logType int) {
	if adRequest != nil {
		if logType == 1 {
			_reqLogPool <- adRequest
		} else if logType == 2 {
			_impLogPool <- adRequest
		} else if logType == 3 {
			_clkLogPool <- adRequest
		} else {
			beego.Critical("logtype is wrong!")
		}
	} else {
		beego.Critical("adRequest is null")
	}

}

func SendMHQueue(adResponse *m.AdResponse, queueName string) {
	if adResponse != nil && len(queueName) > 0 {
		mhQueueData := new(MHQueueData)
		mhQueueData.AdResponse = adResponse
		mhQueueData.QueueName = queueName
		_mhQueuePool <- mhQueueData
	}
}
<commit_msg>change log method<commit_after>package engine

import (
	"adexchange/lib"
	m "adexchange/models"
	"github.com/astaxie/beego"
	"gopkg.in/vmihailenco/msgpack.v2"
)

var _demandLogPool chan *m.AdResponse
var _mhQueuePool chan *MHQueueData
var _reqLogPool chan *m.AdRequest
var _impLogPool chan *m.AdRequest
var _clkLogPool chan *m.AdRequest

func init() {

	_demandLogPool = make(chan *m.AdResponse, 5000)
	_mhQueuePool = make(chan *MHQueueData, 5000)
	_reqLogPool = make(chan *m.AdRequest, 2000)
	_impLogPool = make(chan *m.AdRequest, 1000)
	_clkLogPool = make(chan *m.AdRequest, 1000)

}

func StartDemandLogService() {

	c := lib.GetQueuePool().Get()
	for {
		adResponse := <-_demandLogPool
		b, err := msgpack.Marshal(adResponse)

		if err == nil {
			c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_DEMAND", b)
		} else {
			beego.Critical(err.Error())
		}
	}

	defer c.Close()
}

func StartMHQueueService() {

	c := lib.GetQueuePool().Get()

	for {
		mhQueueData := <-_mhQueuePool
		b, err := msgpack.Marshal(mhQueueData.AdResponse)

		if err == nil {

			c.Do("lpush", beego.AppConfig.String("runmode")+mhQueueData.QueueName, b)

		} else {
			beego.Critical(err.Error())
		}
	}
	defer c.Close()

}

func StartReqLogService() {
	c := lib.GetQueuePool().Get()
	for {
		adRequest := <-_reqLogPool
		b, err := msgpack.Marshal(adRequest)

		if err == nil {

			c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_REQ", b)

		} else {
			beego.Critical(err.Error())
		}
	}
	defer c.Close()

}

func StartImpLogService() {
	c := lib.GetQueuePool().Get()

	for {
		adRequest := <-_impLogPool
		b, err := msgpack.Marshal(adRequest)

		if err == nil {

			c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_IMP", b)

		} else {
			beego.Critical(err.Error())
		}
	}
	defer c.Close()

}

func StartClkLogService() {
	c := lib.GetQueuePool().Get()
	for {
		adRequest := <-_clkLogPool
		b, err := msgpack.Marshal(adRequest)

		if err == nil {

			c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_CLK", b)

		} else {
			beego.Error(err.Error())
		}
	}
	defer c.Close()

}

func SendDemandLog(adResponse *m.AdResponse) {
	if adResponse != nil {
		_demandLogPool <- adResponse
	}
	//c := lib.Pool.Get()
	//b, err := msgpack.Marshal(adResponse)

	//if err == nil {
	//	c = lib.Pool.Get()
	//	c.Do("lpush", beego.AppConfig.String("runmode")+"_ADMUX_DEMAND", b)
	//} else {
	//	beego.Error(err.Error())
	//}
}

func SendRequestLog(adRequest *m.AdRequest, logType int) {
	if adRequest != nil {
		if logType == 1 {
			_reqLogPool <- adRequest
		} else if logType == 2 {
			_impLogPool <- adRequest
		} else if logType == 3 {
			_clkLogPool <- adRequest
		} else {
			beego.Critical("logtype is wrong!")
		}
	} else {
		beego.Critical("adRequest is null")
	}

}

func SendMHQueue(adResponse *m.AdResponse, queueName string) {
	if adResponse != nil && len(queueName) > 0 {
		mhQueueData := new(MHQueueData)
		mhQueueData.AdResponse = adResponse
		mhQueueData.QueueName = queueName
		_mhQueuePool <- mhQueueData
	}
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"path"
	"time"

	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/coordinator/client"
	"github.com/zenoss/serviced/domain/host"
	"github.com/zenoss/serviced/domain/service"
	"github.com/zenoss/serviced/domain/servicestate"
	zkutils "github.com/zenoss/serviced/zzk/utils"
)

const (
	zkHost = "/hosts"
)

func hostpath(nodes ...string) string {
	p := append([]string{zkHost}, nodes...)
	return path.Join(p...)
}

// HostState is the zookeeper node for storing service instance information
// per host
type HostState struct {
	HostID         string
	ServiceID      string
	ServiceStateID string
	DesiredState   int
	version        interface{}
}

// NewHostState instantiates a new HostState node for client.Node
func NewHostState(state *servicestate.ServiceState) *HostState {
	return &HostState{
		HostID:         state.HostID,
		ServiceID:      state.ServiceID,
		ServiceStateID: state.ID,
		DesiredState:   service.SVCRun,
	}
}

// Version inplements client.Node
func (node *HostState) Version() interface{} {
	return node.version
}

// SetVersion implements client.Node
func (node *HostState) SetVersion(version interface{}) {
	node.version = version
}

// HostHandler is the handler for running the HostListener
type HostStateHandler interface {
	GetHost(string) (*host.Host, error)
	AttachService(chan<- interface{}, *service.Service, *servicestate.ServiceState) error
	StartService(chan<- interface{}, *service.Service, *servicestate.ServiceState) error
	StopService(*servicestate.ServiceState) error
}

// HostStateListener is the listener for monitoring service instances
type HostStateListener struct {
	conn    client.Connection
	handler HostStateHandler
	hostID  string
}

// NewHostListener instantiates a HostListener object
func NewHostStateListener(conn client.Connection, handler HostStateHandler, hostID string) *HostStateListener {
	return &HostStateListener{
		conn:    conn,
		handler: handler,
		hostID:  hostID,
	}
}

// Listen starts the HostListener by monitoring when new service instances are
// started, updated, or removed
func (l *HostStateListener) Listen(shutdown <-chan interface{}) {
	var (
		_shutdown  = make(chan interface{})
		done       = make(chan string)
		processing = make(map[string]interface{})
	)

	// Register the host
	regpath, err := l.register(shutdown)
	if err != nil {
		glog.Errorf("Could not register host %s: %s", l.hostID, err)
		return
	}

	// Housekeeping
	defer func() {
		glog.Info("HostStateListener receieved interrupt")
		close(_shutdown)
		for len(processing) > 0 {
			delete(processing, <-done)
		}
		if err := l.conn.Delete(regpath); err != nil {
			glog.Warning("Could not unregister host %s: %s", l.hostID, err)
		}
	}()

	// Monitor the instances
	hpath := hostpath(l.hostID)
	for {
		stateIDs, event, err := l.conn.ChildrenW(hpath)
		if err != nil {
			glog.Errorf("Could not watch for states on host %s: %s", l.hostID, err)
			return
		}

		for _, ssid := range stateIDs {
			if _, ok := processing[ssid]; !ok {
				glog.V(1).Info("Spawning a listener for %s", ssid)
				processing[ssid] = nil
				go l.listenHostState(_shutdown, done, ssid)
			}
		}

		select {
		case e := <-event:
			if e.Type == client.EventNodeDeleted {
				glog.Infof("Host has been removed from pool, shutting down listener")
				return
			}
			glog.V(2).Infof("Received event: %v", e)
		case ssid := <-done:
			glog.V(2).Info("Cleaning up %s", ssid)
			delete(processing, ssid)
		case <-shutdown:
			return
		}
	}
}

func (l *HostStateListener) listenHostState(shutdown <-chan interface{}, done chan<- string, ssID string) {
	defer func() {
		glog.V(2).Info("Shutting down listener for host instance ", ssID)
		done <- ssID
	}()

	var processDone <-chan interface{}
	hpath := hostpath(l.hostID, ssID)
	for {
		var hs HostState
		event, err := l.conn.GetW(hpath, &hs)
		if err != nil {
			glog.Errorf("Could not load host instance %s: %s", ssID, err)
			return
		}

		if hs.ServiceID == "" || hs.ServiceStateID == "" {
			glog.Error("Invalid host state instance: ", hpath)
			return
		}

		var state servicestate.ServiceState
		if err := l.conn.Get(servicepath(hs.ServiceID, hs.ServiceStateID), &ServiceStateNode{ServiceState: &state}); err != nil {
			glog.Error("Could not find service instance: ", hs.ServiceStateID)
			// Node doesn't exist or cannot be loaded, delete
			if err := l.conn.Delete(hpath); err != nil {
				glog.Warningf("Could not delete host state %s: %s", ssID, err)
			}
			return
		}

		var svc service.Service
		if err := l.conn.Get(servicepath(hs.ServiceID), &ServiceNode{Service: &svc}); err != nil {
			glog.Error("Could not find service: ", hs.ServiceID)
			return
		}

		glog.V(2).Infof("Processing %s (%s); Desired State: %d", svc.Name, svc.ID, hs.DesiredState)
		switch hs.DesiredState {
		case service.SVCRun:
			var err error
			if state.Started.UnixNano() <= state.Terminated.UnixNano() {
				processDone, err = l.startInstance(&svc, &state)
			} else if processDone == nil {
				processDone, err = l.attachInstance(&svc, &state)
			}
			if err != nil {
				glog.Errorf("Error trying to start or attach to service instance %s: %s", state.ID, err)
				l.stopInstance(&state)
				return
			}
		case service.SVCStop:
			if processDone != nil {
				l.detachInstance(processDone, &state)
			} else {
				l.stopInstance(&state)
			}
			return
		default:
			glog.V(2).Infof("Unhandled service %s (%s)", svc.Name, svc.ID)
		}

		select {
		case <-processDone:
			glog.V(2).Infof("Process ended for instance: ", hs.ServiceStateID)
			processDone = nil
		case e := <-event:
			glog.V(3).Info("Receieved event: ", e)
			if e.Type == client.EventNodeDeleted {
				if processDone != nil {
					l.detachInstance(processDone, &state)
				} else {
					l.stopInstance(&state)
				}
				return
			}
		case <-shutdown:
			glog.V(0).Infof("Service %s Host instance %s receieved signal to shutdown", hs.ServiceID, hs.ServiceStateID)
			if processDone != nil {
				glog.V(0).Infof("detaching from %s; %s", hs.ServiceID, hs.ServiceStateID)
				l.detachInstance(processDone, &state)
				select {
				case <-processDone:
					glog.V(0).Infof("detached from %s; %s", hs.ServiceID, hs.ServiceStateID)
				case <-Time.After(60 * time.Second):
					glog.V(0).Infof("timed out detaching from %s; %s", hs.ServiceID, hs.ServiceStateID)
				}

			} else {
				glog.V(0).Infof("stopping from %s; %s", hs.ServiceID, hs.ServiceStateID)
				l.stopInstance(&state)
				glog.V(0).Infof("stopped from %s; %s", hs.ServiceID, hs.ServiceStateID)

			}
			return
		}
	}
}

func (l *HostStateListener) updateInstance(done <-chan interface{}, state *servicestate.ServiceState) (<-chan interface{}, error) {
	wait := make(chan interface{})
	go func(path string) {
		defer close(wait)
		<-done
		var s servicestate.ServiceState
		if err := l.conn.Get(path, &ServiceStateNode{ServiceState: &s}); err != nil {
			glog.Warningf("Could not get service state %s: %s", state.ID, err)
			return
		}

		s.Terminated = time.Now()
		if err := updateInstance(l.conn, &s); err != nil {
			glog.Warningf("Could not update the service instance %s with the time terminated (%s): %s", s.ID, s.Terminated.UnixNano(), err)
			return
		}
	}(servicepath(state.ServiceID, state.ID))

	return wait, updateInstance(l.conn, state)
}

func (l *HostStateListener) startInstance(svc *service.Service, state *servicestate.ServiceState) (<-chan interface{}, error) {
	done := make(chan interface{})
	if err := l.handler.StartService(done, svc, state); err != nil {
		return nil, err
	}

	wait, err := l.updateInstance(done, state)
	if err != nil {
		return nil, err
	}

	return wait, nil
}

func (l *HostStateListener) attachInstance(svc *service.Service, state *servicestate.ServiceState) (<-chan interface{}, error) {
	done := make(chan interface{})
	if err := l.handler.AttachService(done, svc, state); err != nil {
		return nil, err
	}

	wait, err := l.updateInstance(done, state)
	if err != nil {
		return nil, err
	}

	return wait, nil
}

func (l *HostStateListener) stopInstance(state *servicestate.ServiceState) error {
	if err := l.handler.StopService(state); err != nil {
		return err
	}
	return removeInstance(l.conn, state)
}

func (l *HostStateListener) detachInstance(done <-chan interface{}, state *servicestate.ServiceState) error {
	if err := l.handler.StopService(state); err != nil {
		return err
	}
	<-done
	return removeInstance(l.conn, state)
}

// register waits for the leader to initialize the host
func (l *HostStateListener) register(shutdown <-chan interface{}) (string, error) {
	// wait for /hosts
	for {
		exists, err := zkutils.PathExists(l.conn, hostpath())
		if err != nil {
			return "", err
		}
		if exists {
			break
		}
		_, event, err := l.conn.ChildrenW("/")
		if err != nil {
			return "", err
		}
		select {
		case <-event:
		case <-shutdown:
			return "", ErrShutdown
		}
	}

	// wait for /hosts/HOSTID
	for {
		exists, err := zkutils.PathExists(l.conn, hostpath(l.hostID))
		if err != nil {
			return "", err
		}
		if exists {
			break
		}
		_, event, err := l.conn.ChildrenW(hostpath())
		if err != nil {
			return "", err
		}
		select {
		case <-event:
		case <-shutdown:
			return "", ErrShutdown
		}
	}

	host, err := l.handler.GetHost(l.hostID)
	if err != nil {
		return "", err
	} else if host == nil {
		return "", ErrHostInvalid
	}

	return l.conn.CreateEphemeral(hostregpath(l.hostID), &HostNode{Host: host})
}

func addInstance(conn client.Connection, state *servicestate.ServiceState) error {
	if state.ID == "" {
		return fmt.Errorf("missing service state id")
	} else if state.ServiceID == "" {
		return fmt.Errorf("missing service id")
	}

	var (
		spath = servicepath(state.ServiceID, state.ID)
		node  = &ServiceStateNode{ServiceState: state}
	)

	if err := conn.Create(spath, node); err != nil {
		return err
	} else if err := conn.Create(hostpath(state.HostID, state.ID), NewHostState(state)); err != nil {
		// try to clean up if create fails
		if err := conn.Delete(spath); err != nil {
			glog.Warningf("Could not remove service instance %s: %s", state.ID, err)
		}
		return err
	}
	return nil
}

func updateInstance(conn client.Connection, state *servicestate.ServiceState) error {
	return conn.Set(servicepath(state.ServiceID, state.ID), &ServiceStateNode{ServiceState: state})
}

func removeInstance(conn client.Connection, state *servicestate.ServiceState) error {
	if err := conn.Delete(hostpath(state.HostID, state.ID)); err != nil {
		glog.Warningf("Could not delete host state %s: %s", state.HostID, state.ID)
	}
	return conn.Delete(servicepath(state.ServiceID, state.ID))
}

func StopServiceInstance(conn client.Connection, hostID, stateID string) error {
	hpath := hostpath(hostID, stateID)
	var hs HostState
	if err := conn.Get(hpath, &hs); err != nil {
		return err
	}
	glog.V(2).Infof("Stopping instance %s via host %s", stateID, hostID)
	hs.DesiredState = service.SVCStop
	return conn.Set(hpath, &hs)
}
<commit_msg>fix typo<commit_after>package service

import (
	"fmt"
	"path"
	"time"

	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/coordinator/client"
	"github.com/zenoss/serviced/domain/host"
	"github.com/zenoss/serviced/domain/service"
	"github.com/zenoss/serviced/domain/servicestate"
	zkutils "github.com/zenoss/serviced/zzk/utils"
)

const (
	zkHost = "/hosts"
)

func hostpath(nodes ...string) string {
	p := append([]string{zkHost}, nodes...)
	return path.Join(p...)
}

// HostState is the zookeeper node for storing service instance information
// per host
type HostState struct {
	HostID         string
	ServiceID      string
	ServiceStateID string
	DesiredState   int
	version        interface{}
}

// NewHostState instantiates a new HostState node for client.Node
func NewHostState(state *servicestate.ServiceState) *HostState {
	return &HostState{
		HostID:         state.HostID,
		ServiceID:      state.ServiceID,
		ServiceStateID: state.ID,
		DesiredState:   service.SVCRun,
	}
}

// Version inplements client.Node
func (node *HostState) Version() interface{} {
	return node.version
}

// SetVersion implements client.Node
func (node *HostState) SetVersion(version interface{}) {
	node.version = version
}

// HostHandler is the handler for running the HostListener
type HostStateHandler interface {
	GetHost(string) (*host.Host, error)
	AttachService(chan<- interface{}, *service.Service, *servicestate.ServiceState) error
	StartService(chan<- interface{}, *service.Service, *servicestate.ServiceState) error
	StopService(*servicestate.ServiceState) error
}

// HostStateListener is the listener for monitoring service instances
type HostStateListener struct {
	conn    client.Connection
	handler HostStateHandler
	hostID  string
}

// NewHostListener instantiates a HostListener object
func NewHostStateListener(conn client.Connection, handler HostStateHandler, hostID string) *HostStateListener {
	return &HostStateListener{
		conn:    conn,
		handler: handler,
		hostID:  hostID,
	}
}

// Listen starts the HostListener by monitoring when new service instances are
// started, updated, or removed
func (l *HostStateListener) Listen(shutdown <-chan interface{}) {
	var (
		_shutdown  = make(chan interface{})
		done       = make(chan string)
		processing = make(map[string]interface{})
	)

	// Register the host
	regpath, err := l.register(shutdown)
	if err != nil {
		glog.Errorf("Could not register host %s: %s", l.hostID, err)
		return
	}

	// Housekeeping
	defer func() {
		glog.Info("HostStateListener receieved interrupt")
		close(_shutdown)
		for len(processing) > 0 {
			delete(processing, <-done)
		}
		if err := l.conn.Delete(regpath); err != nil {
			glog.Warning("Could not unregister host %s: %s", l.hostID, err)
		}
	}()

	// Monitor the instances
	hpath := hostpath(l.hostID)
	for {
		stateIDs, event, err := l.conn.ChildrenW(hpath)
		if err != nil {
			glog.Errorf("Could not watch for states on host %s: %s", l.hostID, err)
			return
		}

		for _, ssid := range stateIDs {
			if _, ok := processing[ssid]; !ok {
				glog.V(1).Info("Spawning a listener for %s", ssid)
				processing[ssid] = nil
				go l.listenHostState(_shutdown, done, ssid)
			}
		}

		select {
		case e := <-event:
			if e.Type == client.EventNodeDeleted {
				glog.Infof("Host has been removed from pool, shutting down listener")
				return
			}
			glog.V(2).Infof("Received event: %v", e)
		case ssid := <-done:
			glog.V(2).Info("Cleaning up %s", ssid)
			delete(processing, ssid)
		case <-shutdown:
			return
		}
	}
}

func (l *HostStateListener) listenHostState(shutdown <-chan interface{}, done chan<- string, ssID string) {
	defer func() {
		glog.V(2).Info("Shutting down listener for host instance ", ssID)
		done <- ssID
	}()

	var processDone <-chan interface{}
	hpath := hostpath(l.hostID, ssID)
	for {
		var hs HostState
		event, err := l.conn.GetW(hpath, &hs)
		if err != nil {
			glog.Errorf("Could not load host instance %s: %s", ssID, err)
			return
		}

		if hs.ServiceID == "" || hs.ServiceStateID == "" {
			glog.Error("Invalid host state instance: ", hpath)
			return
		}

		var state servicestate.ServiceState
		if err := l.conn.Get(servicepath(hs.ServiceID, hs.ServiceStateID), &ServiceStateNode{ServiceState: &state}); err != nil {
			glog.Error("Could not find service instance: ", hs.ServiceStateID)
			// Node doesn't exist or cannot be loaded, delete
			if err := l.conn.Delete(hpath); err != nil {
				glog.Warningf("Could not delete host state %s: %s", ssID, err)
			}
			return
		}

		var svc service.Service
		if err := l.conn.Get(servicepath(hs.ServiceID), &ServiceNode{Service: &svc}); err != nil {
			glog.Error("Could not find service: ", hs.ServiceID)
			return
		}

		glog.V(2).Infof("Processing %s (%s); Desired State: %d", svc.Name, svc.ID, hs.DesiredState)
		switch hs.DesiredState {
		case service.SVCRun:
			var err error
			if state.Started.UnixNano() <= state.Terminated.UnixNano() {
				processDone, err = l.startInstance(&svc, &state)
			} else if processDone == nil {
				processDone, err = l.attachInstance(&svc, &state)
			}
			if err != nil {
				glog.Errorf("Error trying to start or attach to service instance %s: %s", state.ID, err)
				l.stopInstance(&state)
				return
			}
		case service.SVCStop:
			if processDone != nil {
				l.detachInstance(processDone, &state)
			} else {
				l.stopInstance(&state)
			}
			return
		default:
			glog.V(2).Infof("Unhandled service %s (%s)", svc.Name, svc.ID)
		}

		select {
		case <-processDone:
			glog.V(2).Infof("Process ended for instance: ", hs.ServiceStateID)
			processDone = nil
		case e := <-event:
			glog.V(3).Info("Receieved event: ", e)
			if e.Type == client.EventNodeDeleted {
				if processDone != nil {
					l.detachInstance(processDone, &state)
				} else {
					l.stopInstance(&state)
				}
				return
			}
		case <-shutdown:
			glog.V(0).Infof("Service %s Host instance %s receieved signal to shutdown", hs.ServiceID, hs.ServiceStateID)
			if processDone != nil {
				glog.V(0).Infof("detaching from %s; %s", hs.ServiceID, hs.ServiceStateID)
				l.detachInstance(processDone, &state)
				select {
				case <-processDone:
					glog.V(0).Infof("detached from %s; %s", hs.ServiceID, hs.ServiceStateID)
				case <-time.After(60 * time.Second):
					glog.V(0).Infof("timed out detaching from %s; %s", hs.ServiceID, hs.ServiceStateID)
				}

			} else {
				glog.V(0).Infof("stopping from %s; %s", hs.ServiceID, hs.ServiceStateID)
				l.stopInstance(&state)
				glog.V(0).Infof("stopped from %s; %s", hs.ServiceID, hs.ServiceStateID)

			}
			return
		}
	}
}

func (l *HostStateListener) updateInstance(done <-chan interface{}, state *servicestate.ServiceState) (<-chan interface{}, error) {
	wait := make(chan interface{})
	go func(path string) {
		defer close(wait)
		<-done
		var s servicestate.ServiceState
		if err := l.conn.Get(path, &ServiceStateNode{ServiceState: &s}); err != nil {
			glog.Warningf("Could not get service state %s: %s", state.ID, err)
			return
		}

		s.Terminated = time.Now()
		if err := updateInstance(l.conn, &s); err != nil {
			glog.Warningf("Could not update the service instance %s with the time terminated (%s): %s", s.ID, s.Terminated.UnixNano(), err)
			return
		}
	}(servicepath(state.ServiceID, state.ID))

	return wait, updateInstance(l.conn, state)
}

func (l *HostStateListener) startInstance(svc *service.Service, state *servicestate.ServiceState) (<-chan interface{}, error) {
	done := make(chan interface{})
	if err := l.handler.StartService(done, svc, state); err != nil {
		return nil, err
	}

	wait, err := l.updateInstance(done, state)
	if err != nil {
		return nil, err
	}

	return wait, nil
}

func (l *HostStateListener) attachInstance(svc *service.Service, state *servicestate.ServiceState) (<-chan interface{}, error) {
	done := make(chan interface{})
	if err := l.handler.AttachService(done, svc, state); err != nil {
		return nil, err
	}

	wait, err := l.updateInstance(done, state)
	if err != nil {
		return nil, err
	}

	return wait, nil
}

func (l *HostStateListener) stopInstance(state *servicestate.ServiceState) error {
	if err := l.handler.StopService(state); err != nil {
		return err
	}
	return removeInstance(l.conn, state)
}

func (l *HostStateListener) detachInstance(done <-chan interface{}, state *servicestate.ServiceState) error {
	if err := l.handler.StopService(state); err != nil {
		return err
	}
	<-done
	return removeInstance(l.conn, state)
}

// register waits for the leader to initialize the host
func (l *HostStateListener) register(shutdown <-chan interface{}) (string, error) {
	// wait for /hosts
	for {
		exists, err := zkutils.PathExists(l.conn, hostpath())
		if err != nil {
			return "", err
		}
		if exists {
			break
		}
		_, event, err := l.conn.ChildrenW("/")
		if err != nil {
			return "", err
		}
		select {
		case <-event:
		case <-shutdown:
			return "", ErrShutdown
		}
	}

	// wait for /hosts/HOSTID
	for {
		exists, err := zkutils.PathExists(l.conn, hostpath(l.hostID))
		if err != nil {
			return "", err
		}
		if exists {
			break
		}
		_, event, err := l.conn.ChildrenW(hostpath())
		if err != nil {
			return "", err
		}
		select {
		case <-event:
		case <-shutdown:
			return "", ErrShutdown
		}
	}

	host, err := l.handler.GetHost(l.hostID)
	if err != nil {
		return "", err
	} else if host == nil {
		return "", ErrHostInvalid
	}

	return l.conn.CreateEphemeral(hostregpath(l.hostID), &HostNode{Host: host})
}

func addInstance(conn client.Connection, state *servicestate.ServiceState) error {
	if state.ID == "" {
		return fmt.Errorf("missing service state id")
	} else if state.ServiceID == "" {
		return fmt.Errorf("missing service id")
	}

	var (
		spath = servicepath(state.ServiceID, state.ID)
		node  = &ServiceStateNode{ServiceState: state}
	)

	if err := conn.Create(spath, node); err != nil {
		return err
	} else if err := conn.Create(hostpath(state.HostID, state.ID), NewHostState(state)); err != nil {
		// try to clean up if create fails
		if err := conn.Delete(spath); err != nil {
			glog.Warningf("Could not remove service instance %s: %s", state.ID, err)
		}
		return err
	}
	return nil
}

func updateInstance(conn client.Connection, state *servicestate.ServiceState) error {
	return conn.Set(servicepath(state.ServiceID, state.ID), &ServiceStateNode{ServiceState: state})
}

func removeInstance(conn client.Connection, state *servicestate.ServiceState) error {
	if err := conn.Delete(hostpath(state.HostID, state.ID)); err != nil {
		glog.Warningf("Could not delete host state %s: %s", state.HostID, state.ID)
	}
	return conn.Delete(servicepath(state.ServiceID, state.ID))
}

func StopServiceInstance(conn client.Connection, hostID, stateID string) error {
	hpath := hostpath(hostID, stateID)
	var hs HostState
	if err := conn.Get(hpath, &hs); err != nil {
		return err
	}
	glog.V(2).Infof("Stopping instance %s via host %s", stateID, hostID)
	hs.DesiredState = service.SVCStop
	return conn.Set(hpath, &hs)
}
<|endoftext|>


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

package runtime

import "unsafe"

var bloc uintptr
var memlock mutex

func memRound(p uintptr) uintptr {
	return (p + _PAGESIZE - 1) &^ (_PAGESIZE - 1)
}

func initBloc() {
	bloc = memRound(uintptr(unsafe.Pointer(&end)))
}

func sbrk(n uintptr) unsafe.Pointer {
	lock(&memlock)
	// Plan 9 sbrk from /sys/src/libc/9sys/sbrk.c
	bl := bloc
	n = memRound(n)
	if brk_(unsafe.Pointer(bl+n)) < 0 {
		unlock(&memlock)
		return nil
	}
	bloc += n
	unlock(&memlock)
	return unsafe.Pointer(bl)
}

func sysAlloc(n uintptr, stat *uint64) unsafe.Pointer {
	p := sbrk(n)
	if p != nil {
		xadd64(stat, int64(n))
	}
	return p
}

func sysFree(v unsafe.Pointer, n uintptr, stat *uint64) {
	xadd64(stat, -int64(n))
	lock(&memlock)
	// from tiny/mem.c
	// Push pointer back if this is a free
	// of the most recent sysAlloc.
	n = memRound(n)
	if bloc == uintptr(v)+n {
		bloc -= n
		memclr(unsafe.Pointer(bloc), n)
	}
	unlock(&memlock)
}

func sysUnused(v unsafe.Pointer, n uintptr) {
}

func sysUsed(v unsafe.Pointer, n uintptr) {
}

func sysMap(v unsafe.Pointer, n uintptr, reserved bool, stat *uint64) {
	// sysReserve has already allocated all heap memory,
	// but has not adjusted stats.
	xadd64(stat, int64(n))
}

func sysFault(v unsafe.Pointer, n uintptr) {
}

func sysReserve(v unsafe.Pointer, n uintptr, reserved *bool) unsafe.Pointer {
	*reserved = true
	return sbrk(n)
}
<commit_msg>runtime: fix memory allocator on Plan 9<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package runtime

import "unsafe"

const memDebug = false

var bloc uintptr
var memlock mutex

type memHdr struct {
	next *memHdr
	size uintptr
}

var memFreelist *memHdr // sorted in ascending order

func memAlloc(n uintptr) unsafe.Pointer {
	n = memRound(n)
	var prevp *memHdr
	for p := memFreelist; p != nil; p = p.next {
		if p.size >= n {
			if p.size == n {
				if prevp != nil {
					prevp.next = p.next
				} else {
					memFreelist = p.next
				}
			} else {
				p.size -= n
				p = (*memHdr)(add(unsafe.Pointer(p), p.size))
			}
			memclr(unsafe.Pointer(p), unsafe.Sizeof(memHdr{}))
			return unsafe.Pointer(p)
		}
		prevp = p
	}
	return sbrk(n)
}

func memFree(ap unsafe.Pointer, n uintptr) {
	n = memRound(n)
	memclr(ap, n)
	bp := (*memHdr)(ap)
	bp.size = n
	bpn := uintptr(ap)
	if memFreelist == nil {
		bp.next = nil
		memFreelist = bp
		return
	}
	p := memFreelist
	if bpn < uintptr(unsafe.Pointer(p)) {
		memFreelist = bp
		if bpn+bp.size == uintptr(unsafe.Pointer(p)) {
			bp.size += p.size
			bp.next = p.next
			memclr(unsafe.Pointer(p), unsafe.Sizeof(memHdr{}))
		} else {
			bp.next = p
		}
		return
	}
	for ; p.next != nil; p = p.next {
		if bpn > uintptr(unsafe.Pointer(p)) && bpn < uintptr(unsafe.Pointer(p.next)) {
			break
		}
	}
	if bpn+bp.size == uintptr(unsafe.Pointer(p.next)) {
		bp.size += p.next.size
		bp.next = p.next.next
		memclr(unsafe.Pointer(p.next), unsafe.Sizeof(memHdr{}))
	} else {
		bp.next = p.next
	}
	if uintptr(unsafe.Pointer(p))+p.size == bpn {
		p.size += bp.size
		p.next = bp.next
		memclr(unsafe.Pointer(bp), unsafe.Sizeof(memHdr{}))
	} else {
		p.next = bp
	}
}

func memCheck() {
	if memDebug == false {
		return
	}
	for p := memFreelist; p != nil && p.next != nil; p = p.next {
		if uintptr(unsafe.Pointer(p)) == uintptr(unsafe.Pointer(p.next)) {
			print("runtime: ", unsafe.Pointer(p), " == ", unsafe.Pointer(p.next), "\n")
			throw("mem: infinite loop")
		}
		if uintptr(unsafe.Pointer(p)) > uintptr(unsafe.Pointer(p.next)) {
			print("runtime: ", unsafe.Pointer(p), " > ", unsafe.Pointer(p.next), "\n")
			throw("mem: unordered list")
		}
		if uintptr(unsafe.Pointer(p))+p.size > uintptr(unsafe.Pointer(p.next)) {
			print("runtime: ", unsafe.Pointer(p), "+", p.size, " > ", unsafe.Pointer(p.next), "\n")
			throw("mem: overlapping blocks")
		}
		for b := add(unsafe.Pointer(p), unsafe.Sizeof(memHdr{})); uintptr(b) < uintptr(unsafe.Pointer(p))+p.size; b = add(b, 1) {
			if *(*byte)(b) != 0 {
				print("runtime: value at addr ", b, " with offset ", uintptr(b)-uintptr(unsafe.Pointer(p)), " in block ", p, " of size ", p.size, " is not zero\n")
				throw("mem: uninitialised memory")
			}
		}
	}
}

func memRound(p uintptr) uintptr {
	return (p + _PAGESIZE - 1) &^ (_PAGESIZE - 1)
}

func initBloc() {
	bloc = memRound(uintptr(unsafe.Pointer(&end)))
}

func sbrk(n uintptr) unsafe.Pointer {
	// Plan 9 sbrk from /sys/src/libc/9sys/sbrk.c
	bl := bloc
	n = memRound(n)
	if brk_(unsafe.Pointer(bl+n)) < 0 {
		return nil
	}
	bloc += n
	return unsafe.Pointer(bl)
}

func sysAlloc(n uintptr, stat *uint64) unsafe.Pointer {
	lock(&memlock)
	p := memAlloc(n)
	memCheck()
	unlock(&memlock)
	if p != nil {
		xadd64(stat, int64(n))
	}
	return p
}

func sysFree(v unsafe.Pointer, n uintptr, stat *uint64) {
	xadd64(stat, -int64(n))
	lock(&memlock)
	memFree(v, n)
	memCheck()
	unlock(&memlock)
}

func sysUnused(v unsafe.Pointer, n uintptr) {
}

func sysUsed(v unsafe.Pointer, n uintptr) {
}

func sysMap(v unsafe.Pointer, n uintptr, reserved bool, stat *uint64) {
	// sysReserve has already allocated all heap memory,
	// but has not adjusted stats.
	xadd64(stat, int64(n))
}

func sysFault(v unsafe.Pointer, n uintptr) {
}

func sysReserve(v unsafe.Pointer, n uintptr, reserved *bool) unsafe.Pointer {
	*reserved = true
	lock(&memlock)
	p := memAlloc(n)
	memCheck()
	unlock(&memlock)
	return p
}
<|endoftext|>


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

package os

import (
	"syscall"
	"time"
)

// Getpagesize returns the underlying system's memory page size.
func Getpagesize() int { return syscall.Getpagesize() }

// A FileInfo describes a file and is returned by Stat and Lstat
type FileInfo interface {
	Name() string       // base name of the file
	Size() int64        // length in bytes
	Mode() FileMode     // file mode bits
	ModTime() time.Time // modification time
	IsDir() bool        // abbreviation for Mode().IsDir()
}

// A FileMode represents a file's mode and permission bits.
// The bits have the same definition on all systems, so that
// information about files can be moved from one system
// to another portably.  Not all bits apply to all systems.
// The only required bit is ModeDir for directories.
type FileMode uint32

// The defined file mode bits are the most significant bits of the FileMode.
// The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
const (
	// The single letters are the abbreviations
	// used by the String method's formatting.
	ModeDir       FileMode = 1 << (32 - 1 - iota) // d: is a directory
	ModeAppend                                    // a: append-only
	ModeExclusive                                 // l: exclusive use
	ModeTemporary                                 // t: temporary file (not backed up)
	ModeSymlink                                   // L: symbolic link
	ModeDevice                                    // D: device file
	ModeNamedPipe                                 // p: named pipe (FIFO)
	ModeSocket                                    // S: Unix domain socket
	ModeSetuid                                    // u: setuid
	ModeSetgid                                    // g: setgid

	ModePerm FileMode = 0777 // permission bits
)

func (m FileMode) String() string {
	const str = "daltLDpSug"
	var buf [20]byte
	w := 0
	for i, c := range str {
		if m&(1<<uint(32-1-i)) != 0 {
			buf[w] = byte(c)
			w++
		}
	}
	if w == 0 {
		buf[w] = '-'
		w++
	}
	const rwx = "rwxrwxrwx"
	for i, c := range rwx {
		if m&(1<<uint(9-1-i)) != 0 {
			buf[w] = byte(c)
		} else {
			buf[w] = '-'
		}
		w++
	}
	return string(buf[:w])
}

// IsDir reports whether m describes a directory.
// That is, it tests for the ModeDir bit being set in m.
func (m FileMode) IsDir() bool {
	return m&ModeDir != 0
}

// Perm returns the Unix permission bits in m.
func (m FileMode) Perm() FileMode {
	return m & ModePerm
}

// A FileStat is the implementation of FileInfo returned by Stat and Lstat.
// Clients that need access to the underlying system-specific stat information
// can test for *os.FileStat and then consult the Sys field.
type FileStat struct {
	name    string
	size    int64
	mode    FileMode
	modTime time.Time

	Sys interface{}
}

func (fs *FileStat) Name() string       { return fs.name }
func (fs *FileStat) Size() int64        { return fs.size }
func (fs *FileStat) Mode() FileMode     { return fs.mode }
func (fs *FileStat) ModTime() time.Time { return fs.modTime }
func (fs *FileStat) IsDir() bool        { return fs.mode.IsDir() }

// SameFile reports whether fs and other describe the same file.
// For example, on Unix this means that the device and inode fields
// of the two underlying structures are identical; on other systems
// the decision may be based on the path names.
func (fs *FileStat) SameFile(other *FileStat) bool {
	return sameFile(fs, other)
}
<commit_msg>os: add ModeType constant to mask file type bits<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os

import (
	"syscall"
	"time"
)

// Getpagesize returns the underlying system's memory page size.
func Getpagesize() int { return syscall.Getpagesize() }

// A FileInfo describes a file and is returned by Stat and Lstat
type FileInfo interface {
	Name() string       // base name of the file
	Size() int64        // length in bytes
	Mode() FileMode     // file mode bits
	ModTime() time.Time // modification time
	IsDir() bool        // abbreviation for Mode().IsDir()
}

// A FileMode represents a file's mode and permission bits.
// The bits have the same definition on all systems, so that
// information about files can be moved from one system
// to another portably.  Not all bits apply to all systems.
// The only required bit is ModeDir for directories.
type FileMode uint32

// The defined file mode bits are the most significant bits of the FileMode.
// The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
const (
	// The single letters are the abbreviations
	// used by the String method's formatting.
	ModeDir       FileMode = 1 << (32 - 1 - iota) // d: is a directory
	ModeAppend                                    // a: append-only
	ModeExclusive                                 // l: exclusive use
	ModeTemporary                                 // t: temporary file (not backed up)
	ModeSymlink                                   // L: symbolic link
	ModeDevice                                    // D: device file
	ModeNamedPipe                                 // p: named pipe (FIFO)
	ModeSocket                                    // S: Unix domain socket
	ModeSetuid                                    // u: setuid
	ModeSetgid                                    // g: setgid

	// Mask for the type bits. For regular files, none will be set.
	ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice

	ModePerm FileMode = 0777 // permission bits
)

func (m FileMode) String() string {
	const str = "daltLDpSug"
	var buf [20]byte
	w := 0
	for i, c := range str {
		if m&(1<<uint(32-1-i)) != 0 {
			buf[w] = byte(c)
			w++
		}
	}
	if w == 0 {
		buf[w] = '-'
		w++
	}
	const rwx = "rwxrwxrwx"
	for i, c := range rwx {
		if m&(1<<uint(9-1-i)) != 0 {
			buf[w] = byte(c)
		} else {
			buf[w] = '-'
		}
		w++
	}
	return string(buf[:w])
}

// IsDir reports whether m describes a directory.
// That is, it tests for the ModeDir bit being set in m.
func (m FileMode) IsDir() bool {
	return m&ModeDir != 0
}

// Perm returns the Unix permission bits in m.
func (m FileMode) Perm() FileMode {
	return m & ModePerm
}

// A FileStat is the implementation of FileInfo returned by Stat and Lstat.
// Clients that need access to the underlying system-specific stat information
// can test for *os.FileStat and then consult the Sys field.
type FileStat struct {
	name    string
	size    int64
	mode    FileMode
	modTime time.Time

	Sys interface{}
}

func (fs *FileStat) Name() string       { return fs.name }
func (fs *FileStat) Size() int64        { return fs.size }
func (fs *FileStat) Mode() FileMode     { return fs.mode }
func (fs *FileStat) ModTime() time.Time { return fs.modTime }
func (fs *FileStat) IsDir() bool        { return fs.mode.IsDir() }

// SameFile reports whether fs and other describe the same file.
// For example, on Unix this means that the device and inode fields
// of the two underlying structures are identical; on other systems
// the decision may be based on the path names.
func (fs *FileStat) SameFile(other *FileStat) bool {
	return sameFile(fs, other)
}
<|endoftext|>