<commit_before>package nerd

import (
	"fmt"
	"os"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/evalphobia/logrus_sentry"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/nerdalize/nerd/nerd/conf"
	"github.com/rifflock/lfshook"
)

//PlainFormatter is a Logrus formatter that only includes the log message.
type PlainFormatter struct {
}

func (f *PlainFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	msg := fmt.Sprintf("%s\n", entry.Message)
	return []byte(msg), nil
}

func SetupLogging() {
	logrus.SetLevel(logrus.InfoLevel)
	logrus.SetFormatter(new(PlainFormatter))
	addFSHook()
	addSentryHook()
}

//addFSHook adds a filesystem logging hook to Logrus
func addFSHook() {
	c, err := conf.Read()
	if err == nil && c.EnableLogging {
		f, err := homedir.Expand("~/.nerd/log")
		if err == nil {
			logrus.AddHook(lfshook.NewHook(lfshook.PathMap{
				logrus.DebugLevel: f,
				logrus.InfoLevel:  f,
				logrus.WarnLevel:  f,
				logrus.ErrorLevel: f,
			}))
		}
	}
}

//addSentryHook adds a remote logging hook (Sentry.io) to Logrus
func addSentryHook() {
	user := os.Getenv("SENTRY_USER")
	pass := os.Getenv("SENTRY_PASS")
	if user != "" && pass != "" {
		// TODO: Add tags such as JWT and user ID
		dsn := fmt.Sprintf("https://%s:%s@sentry.io/144116", user, pass)
		hook, err := logrus_sentry.NewSentryHook(dsn, []logrus.Level{
			logrus.InfoLevel,
			logrus.WarnLevel,
		})
		if err == nil {
			hook.Timeout = time.Second
			logrus.AddHook(hook)
		}
	}
}
<commit_msg>Remove fshook dependency<commit_after>package nerd

import (
	"fmt"
	"io"
	"os"

	"github.com/Sirupsen/logrus"
	homedir "github.com/mitchellh/go-homedir"
	"github.com/nerdalize/nerd/nerd/conf"
)

//PlainFormatter is a Logrus formatter that only includes the log message.
type PlainFormatter struct {
}

func (f *PlainFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	msg := fmt.Sprintf("%s\n", entry.Message)
	return []byte(msg), nil
}

func SetupLogging() {
	logrus.SetLevel(logrus.InfoLevel)
	logrus.SetFormatter(new(PlainFormatter))
	addFSHook()
}

//addFSHook adds a filesystem logging hook to Logrus
func addFSHook() {
	c, err := conf.Read()
	if err == nil && c.EnableLogging {
		filename, err := homedir.Expand("~/.nerd/log")
		if err != nil {
			return
		}
		f, err := os.Open(filename)
		defer f.Close()
		if err != nil && os.IsNotExist(err) {
			f, err = os.Create(filename)
			defer f.Close()
			if err != nil {
				return
			}
		}
		if err != nil {
			return
		}
		logrus.SetOutput(io.MultiWriter(os.Stdout, f))
	}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package simplepush

import (
	"fmt"
	"time"
)

var (
	ErrInvalidID    StorageError = "Invalid UUID"
	AvailableStores              = make(AvailableExtensions)
)

// StorageError represents an adapter storage error.
type StorageError string

// Error implements the `error` interface.
func (err StorageError) Error() string {
	return fmt.Sprintf("StorageError: %s", err)
}

// Update represents a notification sent on a channel.
type Update struct {
	ChannelID string `json:"channelID"`
	Version   uint64 `json:"version"`
}

// DbConf specifies generic database adapter options.
type DbConf struct {
	// TimeoutLive is the active channel record timeout. Defaults to 3 days.
	TimeoutLive int64 `toml:"timeout_live"`

	// TimeoutReg is the registered channel record timeout. Defaults to 3 hours;
	// an app server should send a notification on a registered channel before
	// this timeout.
	TimeoutReg int64 `toml:"timeout_reg"`

	// TimeoutDel is the deleted channel record timeout. Defaults to 1 day;
	// deleted records will be pruned after this timeout.
	TimeoutDel int64 `toml:"timeout_del"`

	// HandleTimeout is the maximum time to wait when acquiring a connection from
	// the pool. Defaults to 5 seconds.
	HandleTimeout string `toml:"handle_timeout"`

	// HostPrefix is the key prefix for client hosts. Defaults to `"_h-"`.
	HostPrefix string `toml:"shard_prefix"`

	// PingPrefix is the key prefix for proprietary (GCM, etc.) pings. Defaults to
	// `"_pc-"`.
	PingPrefix string `toml:"prop_prefix"`

	// MaxChannels is the maximum number of allowed channels. Defaults to 200.
	MaxChannels int `toml:"max_channels"`
}

// Store describes a storage adapter.
type Store interface {
	// MaxChannels returns the maximum number of channel registrations allowed
	// per client.
	MaxChannels() int

	// Close closes a storage adapter. Any resources (e.g., connections, open
	// files) associated with the adapter should be cleaned up, and all pending
	// operations unblocked.
	Close() error

	// KeyToIDs extracts the device and channel IDs from a storage key.
	KeyToIDs(key string) (suaid, schid string, ok bool)

	// IDsToKey encodes the device and channel IDs into a composite key.
	IDsToKey(suaid, schid string) (key string, ok bool)

	// Status indicates whether the adapter's backing store is healthy.
	Status() (bool, error)

	// Exists determines whether a device has previously registered with the
	// Simple Push server.
	Exists(suaid string) bool

	// Register creates a channel record in the backing store.
	Register(suaid, schid string, version int64) error

	// Update updates the channel record version.
	Update(key string, version int64) error

	// Unregister marks a channel record as inactive.
	Unregister(suaid, schid string) error

	// Drop removes a channel record from the backing store.
	Drop(suaid, schid string) error

	// FetchAll returns all channel updates and expired channels for a device
	// since the specified cutoff time. If the cutoff time is 0, all pending
	// updates will be retrieved.
	FetchAll(suaid string, since time.Time) (updates []Update, expired []string, err error)

	// DropAll removes all channel records for a device from the backing store.
	DropAll(suaid string) error

	// FetchPing retrieves proprietary ping information (e.g., GCM request data)
	// for the given device. The returned value is an opaque string parsed by the
	// ping handler.
	FetchPing(suaid string) (string, error)

	// PutPing stores a proprietary ping info blob for the given device.
	PutPing(suaid, connect string) error

	// DropPing removes all proprietary ping info for the given device.
	DropPing(suaid string) error

	// FetchHost returns the host name of the Simple Push server that currently
	// maintains a connection to the device.
	FetchHost(suaid string) (host string, err error)

	// PutHost updates the host name associated with the device ID.
	PutHost(suaid, host string) error

	// DropHost removes the host mapping for the given device
	DropHost(suaid string) error
}
<commit_msg>`StorageError.Error()`: Avoid infinite recursion.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package simplepush

import (
	"fmt"
	"time"
)

var (
	ErrInvalidID    StorageError = "Invalid UUID"
	AvailableStores              = make(AvailableExtensions)
)

// StorageError represents an adapter storage error.
type StorageError string

// Error implements the `error` interface.
func (err StorageError) Error() string {
	return fmt.Sprintf("StorageError: %s", string(err))
}

// Update represents a notification sent on a channel.
type Update struct {
	ChannelID string `json:"channelID"`
	Version   uint64 `json:"version"`
}

// DbConf specifies generic database adapter options.
type DbConf struct {
	// TimeoutLive is the active channel record timeout. Defaults to 3 days.
	TimeoutLive int64 `toml:"timeout_live"`

	// TimeoutReg is the registered channel record timeout. Defaults to 3 hours;
	// an app server should send a notification on a registered channel before
	// this timeout.
	TimeoutReg int64 `toml:"timeout_reg"`

	// TimeoutDel is the deleted channel record timeout. Defaults to 1 day;
	// deleted records will be pruned after this timeout.
	TimeoutDel int64 `toml:"timeout_del"`

	// HandleTimeout is the maximum time to wait when acquiring a connection from
	// the pool. Defaults to 5 seconds.
	HandleTimeout string `toml:"handle_timeout"`

	// HostPrefix is the key prefix for client hosts. Defaults to `"_h-"`.
	HostPrefix string `toml:"shard_prefix"`

	// PingPrefix is the key prefix for proprietary (GCM, etc.) pings. Defaults to
	// `"_pc-"`.
	PingPrefix string `toml:"prop_prefix"`

	// MaxChannels is the maximum number of allowed channels. Defaults to 200.
	MaxChannels int `toml:"max_channels"`
}

// Store describes a storage adapter.
type Store interface {
	// MaxChannels returns the maximum number of channel registrations allowed
	// per client.
	MaxChannels() int

	// Close closes a storage adapter. Any resources (e.g., connections, open
	// files) associated with the adapter should be cleaned up, and all pending
	// operations unblocked.
	Close() error

	// KeyToIDs extracts the device and channel IDs from a storage key.
	KeyToIDs(key string) (suaid, schid string, ok bool)

	// IDsToKey encodes the device and channel IDs into a composite key.
	IDsToKey(suaid, schid string) (key string, ok bool)

	// Status indicates whether the adapter's backing store is healthy.
	Status() (bool, error)

	// Exists determines whether a device has previously registered with the
	// Simple Push server.
	Exists(suaid string) bool

	// Register creates a channel record in the backing store.
	Register(suaid, schid string, version int64) error

	// Update updates the channel record version.
	Update(key string, version int64) error

	// Unregister marks a channel record as inactive.
	Unregister(suaid, schid string) error

	// Drop removes a channel record from the backing store.
	Drop(suaid, schid string) error

	// FetchAll returns all channel updates and expired channels for a device
	// since the specified cutoff time. If the cutoff time is 0, all pending
	// updates will be retrieved.
	FetchAll(suaid string, since time.Time) (updates []Update, expired []string, err error)

	// DropAll removes all channel records for a device from the backing store.
	DropAll(suaid string) error

	// FetchPing retrieves proprietary ping information (e.g., GCM request data)
	// for the given device. The returned value is an opaque string parsed by the
	// ping handler.
	FetchPing(suaid string) (string, error)

	// PutPing stores a proprietary ping info blob for the given device.
	PutPing(suaid, connect string) error

	// DropPing removes all proprietary ping info for the given device.
	DropPing(suaid string) error

	// FetchHost returns the host name of the Simple Push server that currently
	// maintains a connection to the device.
	FetchHost(suaid string) (host string, err error)

	// PutHost updates the host name associated with the device ID.
	PutHost(suaid, host string) error

	// DropHost removes the host mapping for the given device
	DropHost(suaid string) error
}
<|endoftext|>


<commit_before>package http

import (
	"context"
	"database/sql/driver"

	"github.com/pkg/errors"
)

// SQL statement struct
type stmt struct {
	connection *conn
	query      string
}

// See https://golang.org/pkg/database/sql/driver/#Stmt for more details
func (s *stmt) Close() error {
	s.connection = nil
	s.query = ""
	return nil
}

// See https://golang.org/pkg/database/sql/driver/#Stmt for more details
func (s *stmt) NumInput() int {
	return -1
}

// See https://golang.org/pkg/database/sql/driver/#Stmt for more details
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	return s.ExecContext(context.Background(), s.values2NamedValues(args))
}

// See https://golang.org/pkg/database/sql/driver/#StmtExecContext for more details
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
	if s.connection == nil {
		return nil, errors.WithStack(errors.New("Statement is closed"))
	}
	return s.connection.ExecContext(ctx, s.query, args)
}

// See https://golang.org/pkg/database/sql/driver/#Stmt for more details
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	return s.QueryContext(context.Background(), s.values2NamedValues(args))
}

// See https://golang.org/pkg/database/sql/driver/#StmtQueryContext for more details
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
	if s.connection == nil {
		return nil, errors.WithStack(errors.New("Statement is closed"))
	}
	return s.connection.QueryContext(ctx, s.query, args)
}

// values2NamedValues converts []driver.Value slice to []driver.NamedValue slice
func (s *stmt) values2NamedValues(v []driver.Value) []driver.NamedValue {
	c := len(v)
	nv := make([]driver.NamedValue, c, c)
	for i, val := range v {
		nv[i] = driver.NamedValue{Value: val}
	}
	return nv
}
<commit_msg>removed useless code<commit_after>package http

import (
	"context"
	"database/sql/driver"

	"github.com/pkg/errors"
)

// SQL statement struct
type stmt struct {
	connection *conn
	query      string
}

// See https://golang.org/pkg/database/sql/driver/#Stmt for more details
func (s *stmt) Close() error {
	s.connection = nil
	return nil
}

// See https://golang.org/pkg/database/sql/driver/#Stmt for more details
func (s *stmt) NumInput() int {
	return -1
}

// See https://golang.org/pkg/database/sql/driver/#Stmt for more details
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
	return s.ExecContext(context.Background(), s.values2NamedValues(args))
}

// See https://golang.org/pkg/database/sql/driver/#StmtExecContext for more details
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
	if s.connection == nil {
		return nil, errors.WithStack(errors.New("Statement is closed"))
	}
	return s.connection.ExecContext(ctx, s.query, args)
}

// See https://golang.org/pkg/database/sql/driver/#Stmt for more details
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
	return s.QueryContext(context.Background(), s.values2NamedValues(args))
}

// See https://golang.org/pkg/database/sql/driver/#StmtQueryContext for more details
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
	if s.connection == nil {
		return nil, errors.WithStack(errors.New("Statement is closed"))
	}
	return s.connection.QueryContext(ctx, s.query, args)
}

// values2NamedValues converts []driver.Value slice to []driver.NamedValue slice
func (s *stmt) values2NamedValues(v []driver.Value) []driver.NamedValue {
	c := len(v)
	nv := make([]driver.NamedValue, c, c)
	for i, val := range v {
		nv[i] = driver.NamedValue{Value: val}
	}
	return nv
}
<|endoftext|>


<commit_before>package tests

import (
	"fmt"
	"testing"

	"github.com/deis/deis/tests/dockercliutils"
	"github.com/deis/deis/tests/etcdutils"
	"github.com/deis/deis/tests/mockserviceutils"
	"github.com/deis/deis/tests/utils"
)

func runDeisControllerTest(
	t *testing.T, testID string, etcdPort string, servicePort string) {
	var err error
	cli, stdout, stdoutPipe := dockercliutils.GetNewClient()
	go func() {
		err = dockercliutils.RunContainer(cli,
			"--name", "deis-controller-"+testID,
			"--rm",
			"-p", servicePort+":8000",
			"-e", "PUBLISH="+servicePort,
			"-e", "HOST="+utils.GetHostIPAddress(),
			"-e", "ETCD_PORT="+etcdPort,
			"deis/controller:"+testID)
	}()
	dockercliutils.PrintToStdout(t, stdout, stdoutPipe, "Booting")
	if err != nil {
		t.Fatal(err)
	}
}

func TestController(t *testing.T) {
	setkeys := []string{
		"/deis/registry/protocol",
		"deis/registry/host",
		"/deis/registry/port",
		"/deis/cache/host",
		"/deis/cache/port",
	}
	setdir := []string{
		"/deis/controller",
		"/deis/cache",
		"/deis/database",
		"/deis/registry",
		"/deis/domains",
	}
	testID := utils.NewUuid()
	err := dockercliutils.BuildImage(t, "../", "deis/controller:"+testID)
	if err != nil {
		t.Fatal(err)
	}
	etcdPort := utils.GetRandomPort()
	dockercliutils.RunEtcdTest(t, testID, etcdPort)
	handler := etcdutils.InitetcdValues(setdir, setkeys, etcdPort)
	etcdutils.Publishvalues(t, handler)
	dbPort := utils.GetRandomPort()
	mockserviceutils.RunMockDatabase(t, testID, etcdPort, dbPort)
	servicePort := utils.GetRandomPort()
	fmt.Printf("--- Test deis-controller-%s at port %s\n", testID, servicePort)
	runDeisControllerTest(t, testID, etcdPort, servicePort)
	dockercliutils.DeisServiceTest(
		t, "deis-controller-"+testID, servicePort, "http")
	dockercliutils.ClearTestSession(t, testID)
}
<commit_msg>ref(tests): move exported Test* funcs to top of files and tidy up code<commit_after>package tests

import (
	"fmt"
	"testing"

	"github.com/deis/deis/tests/dockercliutils"
	"github.com/deis/deis/tests/etcdutils"
	"github.com/deis/deis/tests/mockserviceutils"
	"github.com/deis/deis/tests/utils"
)

func runDeisControllerTest(
	t *testing.T, testID string, etcdPort string, servicePort string) {
	var err error
	cli, stdout, stdoutPipe := dockercliutils.GetNewClient()
	go func() {
		err = dockercliutils.RunContainer(cli,
			"--name", "deis-controller-"+testID,
			"--rm",
			"-p", servicePort+":8000",
			"-e", "PUBLISH="+servicePort,
			"-e", "HOST="+utils.GetHostIPAddress(),
			"-e", "ETCD_PORT="+etcdPort,
			"deis/controller:"+testID)
	}()
	dockercliutils.PrintToStdout(t, stdout, stdoutPipe, "Booting")
	if err != nil {
		t.Fatal(err)
	}
}

func TestController(t *testing.T) {
	setkeys := []string{
		"/deis/registry/protocol",
		"deis/registry/host",
		"/deis/registry/port",
		"/deis/cache/host",
		"/deis/cache/port",
	}
	setdir := []string{
		"/deis/controller",
		"/deis/cache",
		"/deis/database",
		"/deis/registry",
		"/deis/domains",
	}
	testID := utils.NewID()
	err := dockercliutils.BuildImage(t, "../", "deis/controller:"+testID)
	if err != nil {
		t.Fatal(err)
	}
	etcdPort := utils.GetRandomPort()
	dockercliutils.RunEtcdTest(t, testID, etcdPort)
	handler := etcdutils.InitetcdValues(setdir, setkeys, etcdPort)
	etcdutils.Publishvalues(t, handler)
	dbPort := utils.GetRandomPort()
	mockserviceutils.RunMockDatabase(t, testID, etcdPort, dbPort)
	servicePort := utils.GetRandomPort()
	fmt.Printf("--- Test deis-controller-%s at port %s\n", testID, servicePort)
	runDeisControllerTest(t, testID, etcdPort, servicePort)
	dockercliutils.DeisServiceTest(
		t, "deis-controller-"+testID, servicePort, "http")
	dockercliutils.ClearTestSession(t, testID)
}
<|endoftext|>


<commit_before>package ciolite

// Api functions that support: https://context.io/docs/lite/users/webhooks

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/contextio/contextio-go/cioutil"
)

// GetUsersWebhooksResponse data struct
// 	https://context.io/docs/lite/users/webhooks#get
// 	https://context.io/docs/lite/users/webhooks#id-get
type GetUsersWebhooksResponse struct {
	CallbackURL        string `json:"callback_url,omitempty"`
	FailureNotifURL    string `json:"failure_notif_url,omitempty"`
	WebhookID          string `json:"webhook_id,omitempty"`
	FilterTo           string `json:"filter_to,omitempty"`
	FilterFrom         string `json:"filter_from,omitempty"`
	FilterCc           string `json:"filter_cc,omitempty"`
	FilterSubject      string `json:"filter_subject,omitempty"`
	FilterThread       string `json:"filter_thread,omitempty"`
	FilterNewImportant string `json:"filter_new_important,omitempty"`
	FilterFileName     string `json:"filter_file_name,omitempty"`
	FilterFolderAdded  string `json:"filter_folder_added,omitempty"`
	FilterToDomain     string `json:"filter_to_domain,omitempty"`
	FilterFromDomain   string `json:"filter_from_domain,omitempty"`
	BodyType           string `json:"body_type,omitempty"`
	ResourceURL        string `json:"resource_url,omitempty"`

	Active      bool `json:"active,omitempty"`
	Failure     bool `json:"failure,omitempty"`
	IncludeBody bool `json:"include_body,omitempty"`
}

// CreateUserWebhookParams form values data struct.
// Requires: CallbackURL, FailureNotifUrl, and may optionally contain
// FilterTo, FilterFrom, FilterCC, FilterSubject, FilterThread,
// FilterNewImportant, FilterFileName, FilterFolderAdded, FilterToDomain,
// FilterFromDomain, IncludeBody, BodyType
// 	https://context.io/docs/lite/users/webhooks#post
type CreateUserWebhookParams struct {
	// Requires:
	CallbackURL     string `json:"callback_url"`
	FailureNotifURL string `json:"failure_notif_url"`

	// Optional:
	FilterTo           string `json:"filter_to,omitempty"`
	FilterFrom         string `json:"filter_from,omitempty"`
	FilterCC           string `json:"filter_cc,omitempty"`
	FilterSubject      string `json:"filter_subject,omitempty"`
	FilterThread       string `json:"filter_thread,omitempty"`
	FilterNewImportant string `json:"filter_new_important,omitempty"`
	FilterFileName     string `json:"filter_file_name,omitempty"`
	FilterFolderAdded  string `json:"filter_folder_added,omitempty"`
	FilterToDomain     string `json:"filter_to_domain,omitempty"`
	FilterFromDomain   string `json:"filter_from_domain,omitempty"`
	BodyType           string `json:"body_type,omitempty"`
	IncludeBody        bool   `json:"include_body,omitempty"`
}

// CreateUserWebhookResponse data struct
// 	https://context.io/docs/lite/users/webhooks#post
type CreateUserWebhookResponse struct {
	WebhookID   string `json:"webhook_id,omitempty"`
	ResourceURL string `json:"resource_url,omitempty"`

	Success bool `json:"success,omitempty"`
}

// ModifyUserWebhookParams form values data struct.
// formValues requires Active
// 	https://context.io/docs/lite/users/webhooks#id-post
type ModifyUserWebhookParams struct {
	// Required:
	Active bool `json:"active"`
}

// ModifyWebhookResponse data struct
// 	https://context.io/docs/lite/users/webhooks#id-post
type ModifyWebhookResponse struct {
	ResourceURL string `json:"resource_url,omitempty"`

	Success bool `json:"success,omitempty"`
}

// DeleteWebhookResponse data struct
// 	https://context.io/docs/lite/users/webhooks#id-delete
type DeleteWebhookResponse struct {
	Success bool `json:"success,omitempty"`
}

// WebhookCallback data struct that will be received from CIO
// 	https://context.io/docs/lite/users/webhooks#callbacks
type WebhookCallback struct {
	AccountID string `json:"account_id,omitempty"`
	WebhookID string `json:"webhook_id,omitempty"`
	Token     string `json:"token,omitempty" valid:"required"`
	Signature string `json:"signature,omitempty" valid:"required"`

	Timestamp int `json:"timestamp,omitempty" valid:"required"`

	// Data is an error message that gives more information about the cause of failure
	Data string `json:"data,omitempty"`

	MessageData WebhookMessageData `json:"message_data,omitempty"`
}

// WebhookMessageData data struct within WebhookCallback
// 	https://context.io/docs/lite/users/webhooks#callbacks
type WebhookMessageData struct {
	MessageID      string `json:"message_id,omitempty"`
	EmailMessageID string `json:"email_message_id,omitempty"`
	Subject        string `json:"subject,omitempty"`

	References []string `json:"references,omitempty"`
	Folders    []string `json:"folders,omitempty"`

	Date         int `json:"date,omitempty"`
	DateReceived int `json:"date_received,omitempty"`

	Addresses WebhookMessageDataAddresses `json:"addresses,omitempty"`

	PersonInfo PersonInfo `json:"person_info,omitempty"`

	Flags struct {
		Flagged  bool `json:"flagged,omitempty"`
		Answered bool `json:"answered,omitempty"`
		Draft    bool `json:"draft,omitempty"`
		Seen     bool `json:"seen,omitempty"`
	} `json:"flags,omitempty"`

	Sources []struct {
		Label  string `json:"label,omitempty"`
		Folder string `json:"folder,omitempty"`
		UID    int    `json:"uid,omitempty"`
	} `json:"sources,omitempty"`

	EmailAccounts []struct {
		Label  string `json:"label,omitempty"`
		Folder string `json:"folder,omitempty"`
		UID    int    `json:"uid,omitempty"`
	} `json:"email_accounts,omitempty"`

	Files []struct {
		ContentID          string `json:"content_id,omitempty"`
		Type               string `json:"type,omitempty"`
		FileName           string `json:"file_name,omitempty"`
		BodySection        string `json:"body_section,omitempty"`
		ContentDisposition string `json:"content_disposition,omitempty"`
		MainFileName       string `json:"main_file_name,omitempty"`

		// XAttachmentID []string `json:"x_attachment_id,omitempty"` // might also be just a string, in addition to a string array?

		FileNameStructure [][]string `json:"file_name_structure,omitempty"`

		AttachmentID int `json:"attachment_id,omitempty"`
		Size         int `json:"size,omitempty"`

		IsEmbedded bool `json:"is_embedded,omitempty"`
	} `json:"files,omitempty"`
}

// WebhookMessageDataAddresses struct within WebhookMessageData
// 	https://context.io/docs/lite/users/webhooks#callbacks
type WebhookMessageDataAddresses struct {
	From struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"from,omitempty"`

	To []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"to,omitempty"`

	Cc []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"cc,omitempty"`

	Bcc []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"bcc,omitempty"`

	Sender []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"sender,omitempty"`

	ReplyTo []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"reply_to,omitempty"`
}

// UnmarshalJSON is here because the empty state is an array in the json, and is a object/map when populated
func (m *WebhookMessageDataAddresses) UnmarshalJSON(b []byte) error {
	if bytes.Equal([]byte(`[]`), b) {
		// its the empty array, set an empty struct
		*m = WebhookMessageDataAddresses{}
		return nil
	}
	// avoid recursion
	type webhookMessageDataAddressesTemp WebhookMessageDataAddresses
	var tmp webhookMessageDataAddressesTemp

	if err := json.Unmarshal(b, &tmp); err != nil {
		return err
	}
	*m = WebhookMessageDataAddresses(tmp)
	return nil
}

// GetUserWebhooks gets listings of Webhooks configured for a user.
// 	https://context.io/docs/lite/users/webhooks#get
func (cioLite CioLite) GetUserWebhooks(userID string) ([]GetUsersWebhooksResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method: "GET",
		Path:   fmt.Sprintf("/users/%s/webhooks", userID),
	}

	// Make response
	var response []GetUsersWebhooksResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}

// GetUserWebhook gets the properties of a given Webhook.
// 	https://context.io/docs/lite/users/webhooks#id-get
func (cioLite CioLite) GetUserWebhook(userID string, webhookID string) (GetUsersWebhooksResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method: "GET",
		Path:   fmt.Sprintf("/users/%s/webhooks/%s", userID, webhookID),
	}

	// Make response
	var response GetUsersWebhooksResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}

// CreateUserWebhook creates a new Webhook on a user.
// formValues requires CallbackURL, FailureNotifUrl, and may optionally contain
// FilterTo, FilterFrom, FilterCC, FilterSubject, FilterThread,
// FilterNewImportant, FilterFileName, FilterFolderAdded, FilterToDomain,
// FilterFromDomain, IncludeBody, BodyType
// 	https://context.io/docs/lite/users/webhooks#post
func (cioLite CioLite) CreateUserWebhook(userID string, formValues CreateUserWebhookParams) (CreateUserWebhookResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method:     "POST",
		Path:       fmt.Sprintf("/users/%s/webhooks", userID),
		FormValues: formValues,
	}

	// Make response
	var response CreateUserWebhookResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}

// ModifyUserWebhook changes the properties of a given Webhook.
// formValues requires Active
// 	https://context.io/docs/lite/users/webhooks#id-post
func (cioLite CioLite) ModifyUserWebhook(userID string, webhookID string, formValues ModifyUserWebhookParams) (ModifyWebhookResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method:     "POST",
		Path:       fmt.Sprintf("/users/%s/webhooks/%s", userID, webhookID),
		FormValues: formValues,
	}

	// Make response
	var response ModifyWebhookResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}

// DeleteUserWebhookAccount cancels a Webhook.
// 	https://context.io/docs/lite/users/webhooks#id-delete
func (cioLite CioLite) DeleteUserWebhookAccount(userID string, webhookID string) (DeleteWebhookResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method: "DELETE",
		Path:   fmt.Sprintf("/users/%s/webhooks/%s", userID, webhookID),
	}

	// Make response
	var response DeleteWebhookResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}
<commit_msg>use an interface for x_attachment_id for now<commit_after>package ciolite

// Api functions that support: https://context.io/docs/lite/users/webhooks

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/contextio/contextio-go/cioutil"
)

// GetUsersWebhooksResponse data struct
// 	https://context.io/docs/lite/users/webhooks#get
// 	https://context.io/docs/lite/users/webhooks#id-get
type GetUsersWebhooksResponse struct {
	CallbackURL        string `json:"callback_url,omitempty"`
	FailureNotifURL    string `json:"failure_notif_url,omitempty"`
	WebhookID          string `json:"webhook_id,omitempty"`
	FilterTo           string `json:"filter_to,omitempty"`
	FilterFrom         string `json:"filter_from,omitempty"`
	FilterCc           string `json:"filter_cc,omitempty"`
	FilterSubject      string `json:"filter_subject,omitempty"`
	FilterThread       string `json:"filter_thread,omitempty"`
	FilterNewImportant string `json:"filter_new_important,omitempty"`
	FilterFileName     string `json:"filter_file_name,omitempty"`
	FilterFolderAdded  string `json:"filter_folder_added,omitempty"`
	FilterToDomain     string `json:"filter_to_domain,omitempty"`
	FilterFromDomain   string `json:"filter_from_domain,omitempty"`
	BodyType           string `json:"body_type,omitempty"`
	ResourceURL        string `json:"resource_url,omitempty"`

	Active      bool `json:"active,omitempty"`
	Failure     bool `json:"failure,omitempty"`
	IncludeBody bool `json:"include_body,omitempty"`
}

// CreateUserWebhookParams form values data struct.
// Requires: CallbackURL, FailureNotifUrl, and may optionally contain
// FilterTo, FilterFrom, FilterCC, FilterSubject, FilterThread,
// FilterNewImportant, FilterFileName, FilterFolderAdded, FilterToDomain,
// FilterFromDomain, IncludeBody, BodyType
// 	https://context.io/docs/lite/users/webhooks#post
type CreateUserWebhookParams struct {
	// Requires:
	CallbackURL     string `json:"callback_url"`
	FailureNotifURL string `json:"failure_notif_url"`

	// Optional:
	FilterTo           string `json:"filter_to,omitempty"`
	FilterFrom         string `json:"filter_from,omitempty"`
	FilterCC           string `json:"filter_cc,omitempty"`
	FilterSubject      string `json:"filter_subject,omitempty"`
	FilterThread       string `json:"filter_thread,omitempty"`
	FilterNewImportant string `json:"filter_new_important,omitempty"`
	FilterFileName     string `json:"filter_file_name,omitempty"`
	FilterFolderAdded  string `json:"filter_folder_added,omitempty"`
	FilterToDomain     string `json:"filter_to_domain,omitempty"`
	FilterFromDomain   string `json:"filter_from_domain,omitempty"`
	BodyType           string `json:"body_type,omitempty"`
	IncludeBody        bool   `json:"include_body,omitempty"`
}

// CreateUserWebhookResponse data struct
// 	https://context.io/docs/lite/users/webhooks#post
type CreateUserWebhookResponse struct {
	WebhookID   string `json:"webhook_id,omitempty"`
	ResourceURL string `json:"resource_url,omitempty"`

	Success bool `json:"success,omitempty"`
}

// ModifyUserWebhookParams form values data struct.
// formValues requires Active
// 	https://context.io/docs/lite/users/webhooks#id-post
type ModifyUserWebhookParams struct {
	// Required:
	Active bool `json:"active"`
}

// ModifyWebhookResponse data struct
// 	https://context.io/docs/lite/users/webhooks#id-post
type ModifyWebhookResponse struct {
	ResourceURL string `json:"resource_url,omitempty"`

	Success bool `json:"success,omitempty"`
}

// DeleteWebhookResponse data struct
// 	https://context.io/docs/lite/users/webhooks#id-delete
type DeleteWebhookResponse struct {
	Success bool `json:"success,omitempty"`
}

// WebhookCallback data struct that will be received from CIO
// 	https://context.io/docs/lite/users/webhooks#callbacks
type WebhookCallback struct {
	AccountID string `json:"account_id,omitempty"`
	WebhookID string `json:"webhook_id,omitempty"`
	Token     string `json:"token,omitempty" valid:"required"`
	Signature string `json:"signature,omitempty" valid:"required"`

	Timestamp int `json:"timestamp,omitempty" valid:"required"`

	// Data is an error message that gives more information about the cause of failure
	Data string `json:"data,omitempty"`

	MessageData WebhookMessageData `json:"message_data,omitempty"`
}

// WebhookMessageData data struct within WebhookCallback
// 	https://context.io/docs/lite/users/webhooks#callbacks
type WebhookMessageData struct {
	MessageID      string `json:"message_id,omitempty"`
	EmailMessageID string `json:"email_message_id,omitempty"`
	Subject        string `json:"subject,omitempty"`

	References []string `json:"references,omitempty"`
	Folders    []string `json:"folders,omitempty"`

	Date         int `json:"date,omitempty"`
	DateReceived int `json:"date_received,omitempty"`

	Addresses WebhookMessageDataAddresses `json:"addresses,omitempty"`

	PersonInfo PersonInfo `json:"person_info,omitempty"`

	Flags struct {
		Flagged  bool `json:"flagged,omitempty"`
		Answered bool `json:"answered,omitempty"`
		Draft    bool `json:"draft,omitempty"`
		Seen     bool `json:"seen,omitempty"`
	} `json:"flags,omitempty"`

	Sources []struct {
		Label  string `json:"label,omitempty"`
		Folder string `json:"folder,omitempty"`
		UID    int    `json:"uid,omitempty"`
	} `json:"sources,omitempty"`

	EmailAccounts []struct {
		Label  string `json:"label,omitempty"`
		Folder string `json:"folder,omitempty"`
		UID    int    `json:"uid,omitempty"`
	} `json:"email_accounts,omitempty"`

	Files []struct {
		ContentID          string `json:"content_id,omitempty"`
		Type               string `json:"type,omitempty"`
		FileName           string `json:"file_name,omitempty"`
		BodySection        string `json:"body_section,omitempty"`
		ContentDisposition string `json:"content_disposition,omitempty"`
		MainFileName       string `json:"main_file_name,omitempty"`

		XAttachmentID interface{} `json:"x_attachment_id,omitempty"` // appears to be a single string and also an array of strings

		FileNameStructure [][]string `json:"file_name_structure,omitempty"`

		AttachmentID int `json:"attachment_id,omitempty"`
		Size         int `json:"size,omitempty"`

		IsEmbedded bool `json:"is_embedded,omitempty"`
	} `json:"files,omitempty"`
}

// WebhookMessageDataAddresses struct within WebhookMessageData
// 	https://context.io/docs/lite/users/webhooks#callbacks
type WebhookMessageDataAddresses struct {
	From struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"from,omitempty"`

	To []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"to,omitempty"`

	Cc []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"cc,omitempty"`

	Bcc []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"bcc,omitempty"`

	Sender []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"sender,omitempty"`

	ReplyTo []struct {
		Email string `json:"email,omitempty"`
		Name  string `json:"name,omitempty"`
	} `json:"reply_to,omitempty"`
}

// UnmarshalJSON is here because the empty state is an array in the json, and is a object/map when populated
func (m *WebhookMessageDataAddresses) UnmarshalJSON(b []byte) error {
	if bytes.Equal([]byte(`[]`), b) {
		// its the empty array, set an empty struct
		*m = WebhookMessageDataAddresses{}
		return nil
	}
	// avoid recursion
	type webhookMessageDataAddressesTemp WebhookMessageDataAddresses
	var tmp webhookMessageDataAddressesTemp

	if err := json.Unmarshal(b, &tmp); err != nil {
		return err
	}
	*m = WebhookMessageDataAddresses(tmp)
	return nil
}

// GetUserWebhooks gets listings of Webhooks configured for a user.
// 	https://context.io/docs/lite/users/webhooks#get
func (cioLite CioLite) GetUserWebhooks(userID string) ([]GetUsersWebhooksResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method: "GET",
		Path:   fmt.Sprintf("/users/%s/webhooks", userID),
	}

	// Make response
	var response []GetUsersWebhooksResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}

// GetUserWebhook gets the properties of a given Webhook.
// 	https://context.io/docs/lite/users/webhooks#id-get
func (cioLite CioLite) GetUserWebhook(userID string, webhookID string) (GetUsersWebhooksResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method: "GET",
		Path:   fmt.Sprintf("/users/%s/webhooks/%s", userID, webhookID),
	}

	// Make response
	var response GetUsersWebhooksResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}

// CreateUserWebhook creates a new Webhook on a user.
// formValues requires CallbackURL, FailureNotifUrl, and may optionally contain
// FilterTo, FilterFrom, FilterCC, FilterSubject, FilterThread,
// FilterNewImportant, FilterFileName, FilterFolderAdded, FilterToDomain,
// FilterFromDomain, IncludeBody, BodyType
// 	https://context.io/docs/lite/users/webhooks#post
func (cioLite CioLite) CreateUserWebhook(userID string, formValues CreateUserWebhookParams) (CreateUserWebhookResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method:     "POST",
		Path:       fmt.Sprintf("/users/%s/webhooks", userID),
		FormValues: formValues,
	}

	// Make response
	var response CreateUserWebhookResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}

// ModifyUserWebhook changes the properties of a given Webhook.
// formValues requires Active
// 	https://context.io/docs/lite/users/webhooks#id-post
func (cioLite CioLite) ModifyUserWebhook(userID string, webhookID string, formValues ModifyUserWebhookParams) (ModifyWebhookResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method:     "POST",
		Path:       fmt.Sprintf("/users/%s/webhooks/%s", userID, webhookID),
		FormValues: formValues,
	}

	// Make response
	var response ModifyWebhookResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}

// DeleteUserWebhookAccount cancels a Webhook.
// 	https://context.io/docs/lite/users/webhooks#id-delete
func (cioLite CioLite) DeleteUserWebhookAccount(userID string, webhookID string) (DeleteWebhookResponse, error) {

	// Make request
	request := cioutil.ClientRequest{
		Method: "DELETE",
		Path:   fmt.Sprintf("/users/%s/webhooks/%s", userID, webhookID),
	}

	// Make response
	var response DeleteWebhookResponse

	// Request
	err := cioLite.DoFormRequest(request, &response)

	return response, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/microcosm-cc/bluemonday"
	"github.com/russross/blackfriday"
	"io/ioutil"
	"os"
	// "path/filepath"
	"encoding/json"
	"strings"
)

// For holding our post's configuration settings
type Config struct {
	Title string `json:"title`
	Date  string `json:"date`
}

func main() {

	// root := "./content"
	// err := filepath.Walk(root, visit)
	// fmt.Printf("filepath.Walk() returned %v\n", err)
	// os.Exit(1)

	args := os.Args[1:] // Reason: index 0 contains the program path
	if len(args) != 2 {
		fmt.Println("ERROR: Wrong number of arguments provided. We're expecting:")
		fmt.Println("1. Input file")
		fmt.Println("2. Output file")
		os.Exit(1)
	}
	inputFile := args[0]
	outputFile := args[1]

	// Get the configs for this page:
	// First read the post's corresponding .json file
	configPath := "./content/" + strings.Replace(inputFile, ".md", "", 1) + ".json"
	configJson, err := ioutil.ReadFile(configPath)
	check(err)

	// Then parse out our relevant config values for later use
	var config Config
	err = json.Unmarshal(configJson, &config)
	check(err)
	fmt.Println("postTitle: " + config.Title)
	fmt.Println("postDate: " + config.Date)
	fmt.Println("Leaving off here")
	// os.Exit(1)

	// Get the Markdown input
	rawInput, err := ioutil.ReadFile("./content/" + inputFile)
	check(err)

	// fmt.Println(rawInput)
	// os.Exit(1)

	// Convert and sanitize our content
	unsafe := blackfriday.MarkdownCommon(rawInput)
	content := bluemonday.UGCPolicy().SanitizeBytes(unsafe)

	// Get the shared markup pieces
	topHTML, err := ioutil.ReadFile("./shared_markup/top.html")
	check(err)
	bottomHTML, err := ioutil.ReadFile("./shared_markup/bottom.html")
	check(err)

	// Incrementally building a singular byte array via append()
	finalOutput := append(topHTML[:], content[:]...)
	finalOutput = append(finalOutput, bottomHTML[:]...)

	// Write our output to an HTML file
	err = ioutil.WriteFile("./content/"+outputFile, finalOutput, 0644)
	check(err)

	fmt.Println("Program finished, check result.")
}

func visit(path string, f os.FileInfo, err error) error {
	fmt.Printf("Visited: %s\n", path)
	return nil
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}
<commit_msg>Prototype for config value interpolation into the output file. It's working for the post title, although I might re-evaluate this to see if there is a more efficient technique than casting from byte array to string to byte array.<commit_after>package main

import (
	"fmt"
	"github.com/microcosm-cc/bluemonday"
	"github.com/russross/blackfriday"
	"io/ioutil"
	"os"
	// "path/filepath"
	"encoding/json"
	"strings"
)

// For holding our post's configuration settings
type Config struct {
	Title string `json:"title`
	Date  string `json:"date`
}

func main() {

	// root := "./content"
	// err := filepath.Walk(root, visit)
	// fmt.Printf("filepath.Walk() returned %v\n", err)
	// os.Exit(1)

	args := os.Args[1:] // Reason: index 0 contains the program path
	if len(args) != 2 {
		fmt.Println("ERROR: Wrong number of arguments provided. We're expecting:")
		fmt.Println("1. Input file")
		fmt.Println("2. Output file")
		os.Exit(1)
	}
	inputFile := args[0]
	outputFile := args[1]

	// Get the configs for this page:
	// First read the post's corresponding .json file
	configPath := "./content/" + strings.Replace(inputFile, ".md", "", 1) + ".json"
	configJson, err := ioutil.ReadFile(configPath)
	check(err)

	// Then parse out our relevant config values for later use
	var config Config
	err = json.Unmarshal(configJson, &config)
	check(err)
	fmt.Println("postTitle: " + config.Title)
	fmt.Println("postDate: " + config.Date)
	fmt.Println("Leaving off here")
	// os.Exit(1)

	// Get the Markdown input
	rawInput, err := ioutil.ReadFile("./content/" + inputFile)
	check(err)

	// Convert and sanitize our content
	unsafe := blackfriday.MarkdownCommon(rawInput)
	content := bluemonday.UGCPolicy().SanitizeBytes(unsafe)

	// Get the shared markup pieces
	topHTML, err := ioutil.ReadFile("./shared_markup/top.html")
	check(err)
	bottomHTML, err := ioutil.ReadFile("./shared_markup/bottom.html")
	check(err)

	// Incrementally building a singular byte array via append()
	finalOutput := append(topHTML[:], content[:]...)
	finalOutput = append(finalOutput, bottomHTML[:]...)

	temp := string(finalOutput[:])
	temp = strings.Replace(temp, "{{title}}", config.Title, -1)
	finalOutput = []byte(temp)

	// Write our output to an HTML file
	err = ioutil.WriteFile("./content/"+outputFile, finalOutput, 0644)
	check(err)

	fmt.Println("Program finished, check result.")
}

func visit(path string, f os.FileInfo, err error) error {
	fmt.Printf("Visited: %s\n", path)
	return nil
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}
<|endoftext|>


<commit_before>package sprok

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"syscall"
)

// Process is the configuration for running a process
type Process struct {
	Chdir  string            `json:"chdir"  yaml:"chdir"`  // cd /tmp
	Env    map[string]string `json:"env"    yaml:"env"`    // env FOO=BAR
	Argv   []string          `json:"argv"   yaml:"argv"`   // "/bin/dd" "if=/dev/zero" "count=10"
	Stdin  string            `json:"stdin"  yaml:"stdin"`  // <"/dev/null"
	Stdout string            `json:"stdout" yaml:"stdout"` // >"zero.bin"
	Stderr string            `json:"stderr" yaml:"stderr"` // 2>"errors.log"
	Uid    int               `json:"uid"    yaml:"uid"`    // 1337
	Gid    int               `json:"gid"    yaml:"gid"`    // 1337
}

// NewProcess returns a Process struct with the env map and argv allocated
// and all stdio pointed at /dev/null.
// argv is allocated with a length of 1 to hold the command.
// Use append to add arguments.
func NewProcess() Process {
	return Process{
		Env:    map[string]string{},
		Argv:   []string{},
		Chdir:  "/",
		Stdin:  "",
		Stdout: "",
		Stderr: "",
		Uid:    -1,
		Gid:    -1,
	}
}

// Exec executes Argv with environment Env and file descriptors
// 1, 2, and 3 open on the files specified in Stdin, Stdout,
// and Stderr. When output files are unspecified or an empty
// string, the file descriptors are left unmodified.
// If argv[0] is stat-able (absolute or relative path), it is used as-is.
// When that fails the PATH searched using exec.LookPath().
func (p *Process) Exec() error {
	var stdin, stdout, stderr *os.File
	var err error

	// Always chdir before doing anything else, making relative paths
	// relative to the provided directory.
	err = os.Chdir(p.Chdir)
	if err != nil {
		log.Fatalf("Could not chdir to '%s': %s\n", p.Chdir, err)
	}

	// Check if it's relative to Chdir or an absolute path, either
	// way it will stat and return not-nil.
	fi, err := os.Stat(p.Argv[0])
	if err != nil {
		fpath, err := exec.LookPath(p.Argv[0])
		if err != nil {
			log.Fatalf("'%s' could not be found in PATH: %s\n", p.Argv[0], err)
		}
		p.Argv[0] = fpath
	}

	// Make sure argv[0] is an actual file before proceeding.
	fi, err = os.Stat(p.Argv[0])
	if err != nil {
		log.Fatalf("BUG: '%s' is not a valid path to a file: %s\n", p.Argv[0], err)
	}
	m := fi.Mode()
	if !m.IsRegular() {
		log.Fatalf("'%s' is not a file!\n", p.Argv[0])
	}

	// If stdin is set, remap it to the file specified on an fd opened read-only.
	if p.Stdin != "" {
		stdin, err = os.OpenFile(p.Stdin, os.O_RDONLY, 0644)
		if err != nil {
			log.Fatalf("Could not open stdin target '%s': %s\n", p.Stdin, err)
		}

		err = syscall.Dup2(int(stdin.Fd()), int(os.Stdin.Fd()))
		if err != nil {
			log.Fatalf("Failed to redirect stdin: %s\n", err)
		}
	}

	// If stdout is set, remap it to the file specified on an fd opened
	// for append and write-only, it will be created if it does not exist.
	if p.Stdout != "" {
		stdout, err = os.OpenFile(p.Stdout, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatalf("Could not open stdout target '%s': %s\n", p.Stdout, err)
		}

		err = syscall.Dup2(int(stdout.Fd()), int(os.Stdout.Fd()))
		if err != nil {
			log.Fatalf("Failed to redirect stdout: %s\n", err)
		}
	}

	// Same deal as stdout except if stdin and stdout have the same target, in
	// which case they will share the fd like 2>&1 does.
	if p.Stderr != "" {
		// there is no reason to open the file twice if they're the same file
		if p.Stderr == p.Stdout {
			stderr = stdout // will get dup2'd to the same fd
		} else {
			stderr, err = os.OpenFile(p.Stderr, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
			if err != nil {
				log.Fatalf("Could not open stderr target '%s': %s\n", p.Stderr, err)
			}
		}

		err = syscall.Dup2(int(stderr.Fd()), int(os.Stderr.Fd()))
		if err != nil {
			log.Fatalf("Failed to redirect stderr: %s\n", err)
		}
	}

	if p.Uid >= 0 {
		err = syscall.Setresuid(p.Uid, p.Uid, p.Uid)
		if err != nil {
			log.Fatalf("setresuid(%d...) failed: %s\n", p.Uid, err)
		}
	}

	if p.Gid >= 0 {
		err = syscall.Setresgid(p.Gid, p.Gid, p.Gid)
		if err != nil {
			log.Fatalf("setresgid(%d...) failed: %s\n", p.Gid, err)
		}
	}

	return syscall.Exec(p.Argv[0], p.Argv, p.envPairs())
}

// String returns the process settings as a Bourne shell command.
func (p *Process) String() string {
	env := strings.Join(p.envPairs(), " ")
	cmd := strings.Join(p.Argv, " ")
	// cd / && env FOO=BAR cmd -arg1 -arg2 foo < /dev/null 1>/dev/null 2>/dev/null
	return fmt.Sprintf("cd %s && env %s %s <%s 1>%s 2>%s",
		p.Chdir, env, cmd, p.Stdin, p.Stdout, p.Stderr)
}

// envPairs converts the key:value map into an array of key=val which
// is what execve(3P) uses.
func (p *Process) envPairs() []string {
	env := make([]string, len(p.Env))
	i := 0
	for key, value := range p.Env {
		env[i] = fmt.Sprintf("%s=%s", key, value)
		i++
	}
	return env
}
<commit_msg>whoops, set gid before uid<commit_after>package sprok

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"syscall"
)

// Process is the configuration for running a process
type Process struct {
	Chdir  string            `json:"chdir"  yaml:"chdir"`  // cd /tmp
	Env    map[string]string `json:"env"    yaml:"env"`    // env FOO=BAR
	Argv   []string          `json:"argv"   yaml:"argv"`   // "/bin/dd" "if=/dev/zero" "count=10"
	Stdin  string            `json:"stdin"  yaml:"stdin"`  // <"/dev/null"
	Stdout string            `json:"stdout" yaml:"stdout"` // >"zero.bin"
	Stderr string            `json:"stderr" yaml:"stderr"` // 2>"errors.log"
	Uid    int               `json:"uid"    yaml:"uid"`    // 1337
	Gid    int               `json:"gid"    yaml:"gid"`    // 1337
}

// NewProcess returns a Process struct with the env map and argv allocated
// and all stdio pointed at /dev/null.
// argv is allocated with a length of 1 to hold the command.
// Use append to add arguments.
func NewProcess() Process {
	return Process{
		Env:    map[string]string{},
		Argv:   []string{},
		Chdir:  "/",
		Stdin:  "",
		Stdout: "",
		Stderr: "",
		Uid:    -1,
		Gid:    -1,
	}
}

// Exec executes Argv with environment Env and file descriptors
// 1, 2, and 3 open on the files specified in Stdin, Stdout,
// and Stderr. When output files are unspecified or an empty
// string, the file descriptors are left unmodified.
// If argv[0] is stat-able (absolute or relative path), it is used as-is.
// When that fails the PATH searched using exec.LookPath().
func (p *Process) Exec() error {
	var stdin, stdout, stderr *os.File
	var err error

	// Always chdir before doing anything else, making relative paths
	// relative to the provided directory.
	err = os.Chdir(p.Chdir)
	if err != nil {
		log.Fatalf("Could not chdir to '%s': %s\n", p.Chdir, err)
	}

	// Check if it's relative to Chdir or an absolute path, either
	// way it will stat and return not-nil.
	fi, err := os.Stat(p.Argv[0])
	if err != nil {
		fpath, err := exec.LookPath(p.Argv[0])
		if err != nil {
			log.Fatalf("'%s' could not be found in PATH: %s\n", p.Argv[0], err)
		}
		p.Argv[0] = fpath
	}

	// Make sure argv[0] is an actual file before proceeding.
	fi, err = os.Stat(p.Argv[0])
	if err != nil {
		log.Fatalf("BUG: '%s' is not a valid path to a file: %s\n", p.Argv[0], err)
	}
	m := fi.Mode()
	if !m.IsRegular() {
		log.Fatalf("'%s' is not a file!\n", p.Argv[0])
	}

	// If stdin is set, remap it to the file specified on an fd opened read-only.
	if p.Stdin != "" {
		stdin, err = os.OpenFile(p.Stdin, os.O_RDONLY, 0644)
		if err != nil {
			log.Fatalf("Could not open stdin target '%s': %s\n", p.Stdin, err)
		}

		err = syscall.Dup2(int(stdin.Fd()), int(os.Stdin.Fd()))
		if err != nil {
			log.Fatalf("Failed to redirect stdin: %s\n", err)
		}
	}

	// If stdout is set, remap it to the file specified on an fd opened
	// for append and write-only, it will be created if it does not exist.
	if p.Stdout != "" {
		stdout, err = os.OpenFile(p.Stdout, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatalf("Could not open stdout target '%s': %s\n", p.Stdout, err)
		}

		err = syscall.Dup2(int(stdout.Fd()), int(os.Stdout.Fd()))
		if err != nil {
			log.Fatalf("Failed to redirect stdout: %s\n", err)
		}
	}

	// Same deal as stdout except if stdin and stdout have the same target, in
	// which case they will share the fd like 2>&1 does.
	if p.Stderr != "" {
		// there is no reason to open the file twice if they're the same file
		if p.Stderr == p.Stdout {
			stderr = stdout // will get dup2'd to the same fd
		} else {
			stderr, err = os.OpenFile(p.Stderr, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
			if err != nil {
				log.Fatalf("Could not open stderr target '%s': %s\n", p.Stderr, err)
			}
		}

		err = syscall.Dup2(int(stderr.Fd()), int(os.Stderr.Fd()))
		if err != nil {
			log.Fatalf("Failed to redirect stderr: %s\n", err)
		}
	}

	if p.Gid >= 0 {
		err = syscall.Setresgid(p.Gid, p.Gid, p.Gid)
		if err != nil {
			log.Fatalf("setresgid(%d...) failed: %s\n", p.Gid, err)
		}
	}

	if p.Uid >= 0 {
		err = syscall.Setresuid(p.Uid, p.Uid, p.Uid)
		if err != nil {
			log.Fatalf("setresuid(%d...) failed: %s\n", p.Uid, err)
		}
	}

	return syscall.Exec(p.Argv[0], p.Argv, p.envPairs())
}

// String returns the process settings as a Bourne shell command.
func (p *Process) String() string {
	env := strings.Join(p.envPairs(), " ")
	cmd := strings.Join(p.Argv, " ")
	// cd / && env FOO=BAR cmd -arg1 -arg2 foo < /dev/null 1>/dev/null 2>/dev/null
	return fmt.Sprintf("cd %s && env %s %s <%s 1>%s 2>%s",
		p.Chdir, env, cmd, p.Stdin, p.Stdout, p.Stderr)
}

// envPairs converts the key:value map into an array of key=val which
// is what execve(3P) uses.
func (p *Process) envPairs() []string {
	env := make([]string, len(p.Env))
	i := 0
	for key, value := range p.Env {
		env[i] = fmt.Sprintf("%s=%s", key, value)
		i++
	}
	return env
}
<|endoftext|>


<commit_before>package orm

import (
	"context"
	"database/sql"
	"fmt"
	"os"

	"github.com/pkg/errors"

	"github.com/rbastic/dyndao/object"
)

// GetParentsViaChild retrieves all direct (one-level 'up') parents for a given child object.
// If a child contains multiple parent tables (possibility?) then this would return an Array
// of objects with multiple potential values for their obj.Type fields.
func (o ORM) GetParentsViaChild(ctx context.Context, childObj *object.Object) (object.Array, error) {
	table := childObj.Type

	objTable := o.s.Tables[table]
	if objTable == nil {
		return nil, errors.New("GetParentsViaChild: unknown object table " + table)
	}

	var parentObjs object.Array

	if objTable.ParentTables == nil {
		return nil, errors.New("GetParentsViaChild: cannot retrieve parents for table " + table + ", schema ParentTables is nil")
	}
	for _, pt := range objTable.ParentTables {
		pkQueryVals, err := pkQueryValsFromKV(childObj, o.s, pt)
		if err != nil {
			return nil, err
		}
		objs, err := o.RetrieveObjects(ctx, pt, pkQueryVals)
		if err != nil {
			return nil, err
		}
		parentObjs = append(parentObjs, objs...)
	}

	return parentObjs, nil
}

// TODO: For foreign key filling, we do not check to see if there are conflicts
// with regards to the uniqueness of primary key names.

// RetrieveWithChildren function will fleshen an *entire* object structure, given some primary keys
func (o ORM) RetrieveWithChildren(ctx context.Context, table string, pkValues map[string]interface{}) (*object.Object, error) {
	objTable := o.s.Tables[table]
	if objTable == nil {
		return nil, errors.New("RetrieveWithChildren: unknown object table " + table)
	}

	obj, err := o.RetrieveObject(ctx, table, pkValues)
	if err != nil {
		return nil, errors.Wrap(err, "RetrieveWithChildren/RetrieveObject")
	}

	for name := range objTable.Children {
		childPkValues := make(map[string]interface{})

		childSchemaTable := o.s.Tables[name]

		pVal, ok := pkValues[childSchemaTable.Primary]
		if ok {
			childPkValues[childSchemaTable.Primary] = pVal
		}

		if childSchemaTable.MultiKey && childSchemaTable.ForeignKeys != nil {
			for _, fk := range childSchemaTable.ForeignKeys {
				childPkValues[fk] = pkValues[fk]
			}
		}
		// TODO: Should we do anything else with pkValues?
		childObj, err := o.RetrieveObject(ctx, name, childPkValues)
		if err != nil {
			return nil, errors.Wrap(err, "RetrieveWithChildren/RetrieveObject("+name+")")
		}
		if obj.Children[name] == nil {
			obj.Children[name] = make(object.Array, 1)
		}
		obj.Children[name][0] = childObj
	}
	return obj, nil
}

// RetrieveObject function will fleshen an object structure, given some primary keys
func (o ORM) RetrieveObject(ctx context.Context, table string, queryVals map[string]interface{}) (*object.Object, error) {
	// TODO: Implement LIMIT... That's all a singular retrieve should be underneath the hood that's different.
	objAry, err := o.RetrieveObjects(ctx, table, queryVals)
	if err != nil {
		return nil, err
	}
	if objAry == nil {
		return nil, errors.New("RetrieveObject: received empty object array from RetrieveObjects")
	}
	return objAry[0], nil
}

// FleshenChildren function accepts an object and resets it's children.
func (o ORM) FleshenChildren(ctx context.Context, obj *object.Object) (*object.Object, error) {
	schemaTable := o.s.Tables[obj.Type]
	pkKey := schemaTable.Primary
	pkVal := obj.Get(pkKey)

	if len(schemaTable.Children) > 0 {
		// Does this table have child tables?
		for childTableName := range schemaTable.Children {
			m := map[string]interface{}{}
			m[pkKey] = pkVal
			childObjs, err := o.RetrieveObjects(ctx, childTableName, m)
			if err != nil {
				return nil, err
			}
			obj.Children[childTableName] = childObjs
		}
	}
	return obj, nil
}

// RetrieveObjects function will fleshen an object structure, given some primary keys
func (o ORM) RetrieveObjects(ctx context.Context, table string, queryVals map[string]interface{}) (object.Array, error) {
	if o.s == nil {
		return nil, errors.New("RetrieveObjects: why is ORM schema set to nil?")
	}
	if o.s.Tables == nil {
		return nil, errors.New("RetrieveObjects: why is Tables nil?")
	}
	objTable := o.s.Tables[table]
	if objTable == nil {
		return nil, errors.New("RetrieveObjects: unknown object table " + table)
	}
	var objectArray object.Array

	queryObj := object.New(table)
	queryObj.KV = queryVals

	sqlStr, columnNames, bindArgs, err := o.sqlGen.BindingRetrieve(o.s, queryObj)
	if os.Getenv("DEBUG") != "" {
		fmt.Println("RetrieveObjects/sqlStr=", sqlStr, "columnNames=", columnNames, "bindArgs=", bindArgs)
	}

	if err != nil {
		return nil, err
	}

	stmt, err := o.RawConn.PrepareContext(ctx, sqlStr)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	res, err := stmt.QueryContext(ctx, bindArgs...)
	if err != nil {
		return nil, err
	}
	defer res.Close()
	columnTypes, err := res.ColumnTypes()
	if err != nil {
		return nil, err
	}

	for res.Next() {
		columnPointers, err := o.makeColumnPointers(len(columnNames), columnTypes)
		if err != nil {
			return nil, err
		}

		obj := object.New(table)
		if err := res.Scan(columnPointers...); err != nil {
			return nil, err
		}

		err = o.dynamicObjectSetter(columnNames, columnPointers, columnTypes, obj)
		if err != nil {
			return nil, err
		}
		obj.SetSaved(true)
		obj.ResetChangedFields()

		objectArray = append(objectArray, obj)
	}

	err = res.Err()
	if err != nil {
		return nil, err
	}
	return objectArray, nil
}

func (o ORM) dynamicObjectSetter(columnNames []string, columnPointers []interface{}, columnTypes []*sql.ColumnType, obj *object.Object) error {
	sqlGen := o.sqlGen
	for i, v := range columnPointers {
		ct := columnTypes[i]

		typeName := ct.DatabaseTypeName()
		if sqlGen.IsStringType(typeName) {
			nullable, _ := ct.Nullable()
			if nullable {
				val := v.(*sql.NullString)
				if val.Valid {
					obj.Set(columnNames[i], val.String)
				}
				// TODO: We don't set keys for null values. How else can we support this?
			} else {
				val := v.(*string)
				obj.Set(columnNames[i], *val)

			}
		} else if sqlGen.IsNumberType(typeName) {
			// TODO: support more than 'int64' for integer...
			nullable, _ := ct.Nullable()
			if nullable {
				val := v.(*sql.NullInt64)
				if val.Valid {
					obj.Set(columnNames[i], val.Int64)
				}
				// TODO: We don't set keys for null values. How else can we support this?
			} else {
				val := v.(*int64)
				obj.Set(columnNames[i], *val)
			}
		} else {
			return errors.New("dynamicObjectSetter: Unrecognized type: " + typeName)
		}
	}
	return nil
}

func (o ORM) makeColumnPointers(sliceLen int, columnTypes []*sql.ColumnType) ([]interface{}, error) {
	columnPointers := make([]interface{}, sliceLen)
	sqlGen := o.sqlGen
	for i := 0; i < sliceLen; i++ {
		ct := columnTypes[i]
		typeName := ct.DatabaseTypeName()
		if sqlGen.IsStringType(typeName) {
			nullable, _ := ct.Nullable()
			if nullable {
				var s sql.NullString
				columnPointers[i] = &s
			} else {
				var s string
				columnPointers[i] = &s
			}
		} else if sqlGen.IsNumberType(typeName) {
			nullable, _ := ct.Nullable()
			if nullable {
				var j sql.NullInt64
				columnPointers[i] = &j
			} else {
				var j int64
				columnPointers[i] = &j

			}
		} else {
			return nil, errors.New("makeColumnPointers: Unrecognized type: " + typeName)
		}
	}
	return columnPointers, nil
}

// TODO: Read this post for more info on the above...
// https://stackoverflow.com/questions/23507531/is-golangs-sql-package-incapable-of-ad-hoc-exploratory-queries/23507765#23507765
<commit_msg>replace TODO with NOTE<commit_after>package orm

import (
	"context"
	"database/sql"
	"fmt"
	"os"

	"github.com/pkg/errors"

	"github.com/rbastic/dyndao/object"
)

// GetParentsViaChild retrieves all direct (one-level 'up') parents for a given child object.
// If a child contains multiple parent tables (possibility?) then this would return an Array
// of objects with multiple potential values for their obj.Type fields.
func (o ORM) GetParentsViaChild(ctx context.Context, childObj *object.Object) (object.Array, error) {
	table := childObj.Type

	objTable := o.s.Tables[table]
	if objTable == nil {
		return nil, errors.New("GetParentsViaChild: unknown object table " + table)
	}

	var parentObjs object.Array

	if objTable.ParentTables == nil {
		return nil, errors.New("GetParentsViaChild: cannot retrieve parents for table " + table + ", schema ParentTables is nil")
	}
	for _, pt := range objTable.ParentTables {
		pkQueryVals, err := pkQueryValsFromKV(childObj, o.s, pt)
		if err != nil {
			return nil, err
		}
		objs, err := o.RetrieveObjects(ctx, pt, pkQueryVals)
		if err != nil {
			return nil, err
		}
		parentObjs = append(parentObjs, objs...)
	}

	return parentObjs, nil
}

// NOTE: For foreign key filling, we do not check to see if there are conflicts
// with regards to the uniqueness of primary key names.

// RetrieveWithChildren function will fleshen an *entire* object structure, given some primary keys
func (o ORM) RetrieveWithChildren(ctx context.Context, table string, pkValues map[string]interface{}) (*object.Object, error) {
	objTable := o.s.Tables[table]
	if objTable == nil {
		return nil, errors.New("RetrieveWithChildren: unknown object table " + table)
	}

	obj, err := o.RetrieveObject(ctx, table, pkValues)
	if err != nil {
		return nil, errors.Wrap(err, "RetrieveWithChildren/RetrieveObject")
	}

	for name := range objTable.Children {
		childPkValues := make(map[string]interface{})

		childSchemaTable := o.s.Tables[name]

		pVal, ok := pkValues[childSchemaTable.Primary]
		if ok {
			childPkValues[childSchemaTable.Primary] = pVal
		}

		if childSchemaTable.MultiKey && childSchemaTable.ForeignKeys != nil {
			for _, fk := range childSchemaTable.ForeignKeys {
				childPkValues[fk] = pkValues[fk]
			}
		}
		// TODO: Should we do anything else with pkValues?
		childObj, err := o.RetrieveObject(ctx, name, childPkValues)
		if err != nil {
			return nil, errors.Wrap(err, "RetrieveWithChildren/RetrieveObject("+name+")")
		}
		if obj.Children[name] == nil {
			obj.Children[name] = make(object.Array, 1)
		}
		obj.Children[name][0] = childObj
	}
	return obj, nil
}

// RetrieveObject function will fleshen an object structure, given some primary keys
func (o ORM) RetrieveObject(ctx context.Context, table string, queryVals map[string]interface{}) (*object.Object, error) {
	// TODO: Implement LIMIT... That's all a singular retrieve should be underneath the hood that's different.
	objAry, err := o.RetrieveObjects(ctx, table, queryVals)
	if err != nil {
		return nil, err
	}
	if objAry == nil {
		return nil, errors.New("RetrieveObject: received empty object array from RetrieveObjects")
	}
	return objAry[0], nil
}

// FleshenChildren function accepts an object and resets it's children.
func (o ORM) FleshenChildren(ctx context.Context, obj *object.Object) (*object.Object, error) {
	schemaTable := o.s.Tables[obj.Type]
	pkKey := schemaTable.Primary
	pkVal := obj.Get(pkKey)

	if len(schemaTable.Children) > 0 {
		// Does this table have child tables?
		for childTableName := range schemaTable.Children {
			m := map[string]interface{}{}
			m[pkKey] = pkVal
			childObjs, err := o.RetrieveObjects(ctx, childTableName, m)
			if err != nil {
				return nil, err
			}
			obj.Children[childTableName] = childObjs
		}
	}
	return obj, nil
}

// RetrieveObjects function will fleshen an object structure, given some primary keys
func (o ORM) RetrieveObjects(ctx context.Context, table string, queryVals map[string]interface{}) (object.Array, error) {
	if o.s == nil {
		return nil, errors.New("RetrieveObjects: why is ORM schema set to nil?")
	}
	if o.s.Tables == nil {
		return nil, errors.New("RetrieveObjects: why is Tables nil?")
	}
	objTable := o.s.Tables[table]
	if objTable == nil {
		return nil, errors.New("RetrieveObjects: unknown object table " + table)
	}
	var objectArray object.Array

	queryObj := object.New(table)
	queryObj.KV = queryVals

	sqlStr, columnNames, bindArgs, err := o.sqlGen.BindingRetrieve(o.s, queryObj)
	if os.Getenv("DEBUG") != "" {
		fmt.Println("RetrieveObjects/sqlStr=", sqlStr, "columnNames=", columnNames, "bindArgs=", bindArgs)
	}

	if err != nil {
		return nil, err
	}

	stmt, err := o.RawConn.PrepareContext(ctx, sqlStr)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()

	res, err := stmt.QueryContext(ctx, bindArgs...)
	if err != nil {
		return nil, err
	}
	defer res.Close()
	columnTypes, err := res.ColumnTypes()
	if err != nil {
		return nil, err
	}

	for res.Next() {
		columnPointers, err := o.makeColumnPointers(len(columnNames), columnTypes)
		if err != nil {
			return nil, err
		}

		obj := object.New(table)
		if err := res.Scan(columnPointers...); err != nil {
			return nil, err
		}

		err = o.dynamicObjectSetter(columnNames, columnPointers, columnTypes, obj)
		if err != nil {
			return nil, err
		}
		obj.SetSaved(true)
		obj.ResetChangedFields()

		objectArray = append(objectArray, obj)
	}

	err = res.Err()
	if err != nil {
		return nil, err
	}
	return objectArray, nil
}

func (o ORM) dynamicObjectSetter(columnNames []string, columnPointers []interface{}, columnTypes []*sql.ColumnType, obj *object.Object) error {
	sqlGen := o.sqlGen
	for i, v := range columnPointers {
		ct := columnTypes[i]

		typeName := ct.DatabaseTypeName()
		if sqlGen.IsStringType(typeName) {
			nullable, _ := ct.Nullable()
			if nullable {
				val := v.(*sql.NullString)
				if val.Valid {
					obj.Set(columnNames[i], val.String)
				}
				// TODO: We don't set keys for null values. How else can we support this?
			} else {
				val := v.(*string)
				obj.Set(columnNames[i], *val)

			}
		} else if sqlGen.IsNumberType(typeName) {
			// TODO: support more than 'int64' for integer...
			nullable, _ := ct.Nullable()
			if nullable {
				val := v.(*sql.NullInt64)
				if val.Valid {
					obj.Set(columnNames[i], val.Int64)
				}
				// TODO: We don't set keys for null values. How else can we support this?
			} else {
				val := v.(*int64)
				obj.Set(columnNames[i], *val)
			}
		} else {
			return errors.New("dynamicObjectSetter: Unrecognized type: " + typeName)
		}
	}
	return nil
}

func (o ORM) makeColumnPointers(sliceLen int, columnTypes []*sql.ColumnType) ([]interface{}, error) {
	columnPointers := make([]interface{}, sliceLen)
	sqlGen := o.sqlGen
	for i := 0; i < sliceLen; i++ {
		ct := columnTypes[i]
		typeName := ct.DatabaseTypeName()
		if sqlGen.IsStringType(typeName) {
			nullable, _ := ct.Nullable()
			if nullable {
				var s sql.NullString
				columnPointers[i] = &s
			} else {
				var s string
				columnPointers[i] = &s
			}
		} else if sqlGen.IsNumberType(typeName) {
			nullable, _ := ct.Nullable()
			if nullable {
				var j sql.NullInt64
				columnPointers[i] = &j
			} else {
				var j int64
				columnPointers[i] = &j

			}
		} else {
			return nil, errors.New("makeColumnPointers: Unrecognized type: " + typeName)
		}
	}
	return columnPointers, nil
}

// TODO: Read this post for more info on the above...
// https://stackoverflow.com/questions/23507531/is-golangs-sql-package-incapable-of-ad-hoc-exploratory-queries/23507765#23507765
<|endoftext|>


<commit_before>package main

import (
	"gopkg.in/mgo.v2/bson"
	"log"
	"time"
)

type Product struct {
	Id           bson.ObjectId `bson:"_id,omitempty" json:"id"`
	UserID       bson.ObjectId `bson:"user_id" json:"user_id"`
	Name         string        `json:"name"`
	Description  string        `json:"description"`
	Price        string        `json:"price"`
	Tax          string        `json:"tax"`
	Valid        bool          `json:"valid"`
	Date_Created time.Time     `json:"date_created"`
}

func (product *Product) CreateNewProduct(mConnection *MongoConnection) bool {

	if mConnection.dbSession == nil {
		return false
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	collection := session.DB("goinvoice").C("productdata")
	err := collection.Insert(product)
	if err != nil {
		log.Fatal(err)
		return false
	}

	return true
}

func (product *Product) UpdateProduct(mConnection *MongoConnection) bool {
	if mConnection.dbSession == nil {
		return false
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	c := session.DB("goinvoice").C("productdata")
	err := c.UpdateId(product.Id, product)

	if err != nil {
		log.Fatal(err)
		return false
	}

	return true
}

func (product *Product) DeleteProduct(mConnection *MongoConnection, token string, productID string) bool {
	if mConnection.dbSession == nil {
		return false
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	c := session.DB("goinvoice").C("productdata")
	err := c.Remove(bson.M{"_id": bson.ObjectIdHex(productID), "$and": []interface{}{bson.M{"user_id": bson.ObjectIdHex(token)}}})

	if err != nil {
		log.Fatal(err)
		return false
	}

	return true
}

func (product *Product) ListProducts(mConnection *MongoConnection, token string) []Product {
	if mConnection.dbSession == nil {
		return nil
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	var products Products

	c := session.DB("goinvoice").C("productdata")
	err := c.Find(bson.M{"user_id": bson.ObjectIdHex(token)}).All(&products)
	if err != nil {
		log.Fatal(err)
	}

	return products
}

type Products []Product
<commit_msg>New method implemented<commit_after>package main

import (
	"gopkg.in/mgo.v2/bson"
	"log"
	"time"
)

type Product struct {
	Id           bson.ObjectId `bson:"_id,omitempty" json:"id"`
	UserID       bson.ObjectId `bson:"user_id" json:"user_id"`
	Name         string        `json:"name"`
	Description  string        `json:"description"`
	Price        string        `json:"price"`
	Tax          string        `json:"tax"`
	Valid        bool          `json:"valid"`
	Date_Created time.Time     `json:"date_created"`
}

func (product *Product) CreateNewProduct(mConnection *MongoConnection) bool {

	if mConnection.dbSession == nil {
		return false
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	collection := session.DB("goinvoice").C("productdata")
	err := collection.Insert(product)
	if err != nil {
		log.Fatal(err)
		return false
	}

	return true
}

func (product *Product) UpdateProduct(mConnection *MongoConnection) bool {
	if mConnection.dbSession == nil {
		return false
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	c := session.DB("goinvoice").C("productdata")
	err := c.UpdateId(product.Id, product)

	if err != nil {
		log.Fatal(err)
		return false
	}

	return true
}

func (product *Product) DeleteProduct(mConnection *MongoConnection, token string, productID string) bool {
	if mConnection.dbSession == nil {
		return false
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	c := session.DB("goinvoice").C("productdata")
	err := c.Remove(bson.M{"_id": bson.ObjectIdHex(productID), "$and": []interface{}{bson.M{"user_id": bson.ObjectIdHex(token)}}})

	if err != nil {
		log.Fatal(err)
		return false
	}

	return true
}

func (product *Product) ListProducts(mConnection *MongoConnection, token string) []Product {
	if mConnection.dbSession == nil {
		return nil
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	var products Products

	c := session.DB("goinvoice").C("productdata")
	err := c.Find(bson.M{"user_id": bson.ObjectIdHex(token)}).All(&products)
	if err != nil {
		log.Fatal(err)
	}

	return products
}

func (product *Product) FindProduct(mConnection *MongoConnection, token string, productID string) *Product {
	if mConnection.dbSession == nil {
		return nil
	}

	session := mConnection.dbSession.Copy()
	defer session.Close()

	var productData *Product

	c := session.DB("goinvoice").C("productdata")
	err := c.Find(bson.M{"_id": bson.ObjectIdHex(productID), "$and": []interface{}{bson.M{"user_id": bson.ObjectIdHex(token)}}}).One(&productData)
	if err != nil {
		log.Fatal(err)
	}

	return productData
}

type Products []Product
<|endoftext|>


<commit_before>// @author       sigu-399
// @description  An implementation of JSON Schema, draft v4 - Go language
// @created      26-02-2013

package gojsonschema

import (
	"encoding/json"
	"errors"
	"fmt"
	"gojsonreference"
	"io/ioutil"
	"net/http"
	"reflect"
)

func NewJsonSchemaDocument(documentReferenceString string) (*JsonSchemaDocument, error) {

	var err error

	d := JsonSchemaDocument{}
	d.documentReference, err = gojsonreference.NewJsonReference(documentReferenceString)

	resp, err := http.Get(documentReferenceString)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New("Could not access schema " + resp.Status)
	}

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

	var document interface{}
	err = json.Unmarshal(bodyBuff, &document)
	if err != nil {
		return nil, err
	}

	err = d.parse(document)
	return &d, err
}

type JsonSchemaDocument struct {
	documentReference gojsonreference.JsonReference
	rootSchema        *JsonSchema
}

func (d *JsonSchemaDocument) parse(document interface{}) error {
	d.rootSchema = &JsonSchema{}
	return d.parseSchema(document, d.rootSchema)
}

func (d *JsonSchemaDocument) parseSchema(documentNode interface{}, currentSchema *JsonSchema) error {

	if !isKind(documentNode, reflect.Map) {
		return errors.New("Schema must be an object")
	}

	m := documentNode.(map[string]interface{})

	// id
	if existsMapKey(m, "id") && !isKind(m["id"], reflect.String) {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_MUST_BE_OF_TYPE, "id", "string"))
	}
	if k, ok := m["id"].(string); ok {
		currentSchema.id = &k
	}

	// title
	if existsMapKey(m, "title") && !isKind(m["title"], reflect.String) {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_MUST_BE_OF_TYPE, "title", "string"))
	}
	if k, ok := m["title"].(string); ok {
		currentSchema.title = &k
	}

	// description
	if existsMapKey(m, "description") && !isKind(m["description"], reflect.String) {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_MUST_BE_OF_TYPE, "description", "string"))
	}
	if k, ok := m["description"].(string); ok {
		currentSchema.description = &k
	}

	// ref
	if existsMapKey(m, "$ref") && !isKind(m["$ref"], reflect.String) {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_MUST_BE_OF_TYPE, "$ref", "string"))
	}
	if k, ok := m["$ref"].(string); ok {
		currentSchema.ref = &k
	}

	// properties
/*	if !existsMapKey(m, "properties") {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_IS_REQUIRED, "properties"))
	}
*/
	for k := range m {
		if k == "properties" {
			err := d.parseProperties(m[k], currentSchema)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (d *JsonSchemaDocument) parseProperties(documentNode interface{}, currentSchema *JsonSchema) error {

	if !isKind(documentNode, reflect.Map) {
		return errors.New("Properties must be an object")
	}

	m := documentNode.(map[string]interface{})
	for k := range m {
		newSchema := &JsonSchema{property: &k}
		err := d.parseSchema(m[k], newSchema)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>added addChild to schema<commit_after>// @author       sigu-399
// @description  An implementation of JSON Schema, draft v4 - Go language
// @created      26-02-2013

package gojsonschema

import (
	"encoding/json"
	"errors"
	"fmt"
	"gojsonreference"
	"io/ioutil"
	"net/http"
	"reflect"
)

func NewJsonSchemaDocument(documentReferenceString string) (*JsonSchemaDocument, error) {

	var err error

	d := JsonSchemaDocument{}
	d.documentReference, err = gojsonreference.NewJsonReference(documentReferenceString)

	resp, err := http.Get(documentReferenceString)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		return nil, errors.New("Could not access schema " + resp.Status)
	}

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

	var document interface{}
	err = json.Unmarshal(bodyBuff, &document)
	if err != nil {
		return nil, err
	}

	err = d.parse(document)
	return &d, err
}

type JsonSchemaDocument struct {
	documentReference gojsonreference.JsonReference
	rootSchema        *JsonSchema
}

func (d *JsonSchemaDocument) parse(document interface{}) error {
	d.rootSchema = &JsonSchema{}
	return d.parseSchema(document, d.rootSchema)
}

func (d *JsonSchemaDocument) parseSchema(documentNode interface{}, currentSchema *JsonSchema) error {

	if !isKind(documentNode, reflect.Map) {
		return errors.New("Schema must be an object")
	}

	m := documentNode.(map[string]interface{})

	// id
	if existsMapKey(m, "id") && !isKind(m["id"], reflect.String) {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_MUST_BE_OF_TYPE, "id", "string"))
	}
	if k, ok := m["id"].(string); ok {
		currentSchema.id = &k
	}

	// title
	if existsMapKey(m, "title") && !isKind(m["title"], reflect.String) {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_MUST_BE_OF_TYPE, "title", "string"))
	}
	if k, ok := m["title"].(string); ok {
		currentSchema.title = &k
	}

	// description
	if existsMapKey(m, "description") && !isKind(m["description"], reflect.String) {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_MUST_BE_OF_TYPE, "description", "string"))
	}
	if k, ok := m["description"].(string); ok {
		currentSchema.description = &k
	}

	// ref
	if existsMapKey(m, "$ref") && !isKind(m["$ref"], reflect.String) {
		return errors.New(fmt.Sprintf(ERROR_MESSAGE_MUST_BE_OF_TYPE, "$ref", "string"))
	}
	if k, ok := m["$ref"].(string); ok {
		currentSchema.ref = &k
	}

	// properties
	/*	if !existsMapKey(m, "properties") {
			return errors.New(fmt.Sprintf(ERROR_MESSAGE_IS_REQUIRED, "properties"))
		}
	*/
	for k := range m {
		if k == "properties" {
			err := d.parseProperties(m[k], currentSchema)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (d *JsonSchemaDocument) parseProperties(documentNode interface{}, currentSchema *JsonSchema) error {

	if !isKind(documentNode, reflect.Map) {
		return errors.New("Properties must be an object")
	}

	m := documentNode.(map[string]interface{})
	for k := range m {
		newSchema := &JsonSchema{property: &k}
		currentSchema.AddChild(newSchema)
		err := d.parseSchema(m[k], newSchema)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"os"

	flags "github.com/jessevdk/go-flags"
)

const version = "1.0.0"

var opts struct {
	Version bool `short:"v" long:"version" description:"Show version"`
}

func main() {
	parser := flags.NewParser(&opts, flags.Default)
	parser.Usage = "HOSTNAME [OPTIONS]"
	args, _ := parser.Parse()

	if len(args) == 0 {
		if opts.Version {
			fmt.Println("ptrhost version", version)
		}
		os.Exit(1)
	}

	hostname := args[0]
	addr, _ := net.LookupHost(hostname)
	for _, v := range addr {
		ptrAddr, _ := net.LookupAddr(v)
		fmt.Println(v, "->", ptrAddr[0])
	}
}
<commit_msg>Fix crash when none resolved hostname<commit_after>package main

import (
	"fmt"
	"net"
	"os"

	flags "github.com/jessevdk/go-flags"
)

const version = "1.0.0"

var opts struct {
	Version bool `short:"v" long:"version" description:"Show version"`
}

func main() {
	parser := flags.NewParser(&opts, flags.Default)
	parser.Usage = "HOSTNAME [OPTIONS]"
	args, _ := parser.Parse()

	if len(args) == 0 {
		if opts.Version {
			fmt.Println("ptrhost version", version)
		}
		os.Exit(1)
	}

	hostname := args[0]
	addr, _ := net.LookupHost(hostname)
	for _, v := range addr {
		resolvedHost, err := net.LookupAddr(v)
		if err == nil {
			fmt.Println(v, "->", resolvedHost[0])
		} else {
			fmt.Println(v, "->", err.(*net.DNSError).Err)
		}
	}
}
<|endoftext|>


<commit_before>/*
 Copyright 2016 Padduck, LLC

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 	http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/braintree/manners"
	"github.com/gin-gonic/gin"
	"github.com/pufferpanel/apufferi/config"
	"github.com/pufferpanel/apufferi/logging"
	"github.com/pufferpanel/pufferd/commands"
	"github.com/pufferpanel/pufferd/data"
	"github.com/pufferpanel/pufferd/data/templates"
	"github.com/pufferpanel/pufferd/install"
	"github.com/pufferpanel/pufferd/migration"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/routing"
	"github.com/pufferpanel/pufferd/sftp"
	"github.com/pufferpanel/pufferd/shutdown"
	"net/http"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"strings"
	"syscall"
)

var (
	VERSION      = "nightly"
	MAJORVERSION = "nightly"
	GITHASH      = "unknown"
)

var runService = true
var configPath string

func main() {
	var loggingLevel string
	var authRoot string
	var authToken string
	var runInstaller bool
	var version bool
	var license bool
	var regenerate bool
	var migrate bool
	var shutdownPid int
	var runDaemon bool
	var reloadPid int
	flag.StringVar(&loggingLevel, "logging", "INFO", "Lowest logging level to display")
	flag.StringVar(&authRoot, "auth", "", "Base URL to the authorization server")
	flag.StringVar(&authToken, "token", "", "Authorization token")
	flag.BoolVar(&runInstaller, "install", false, "If installing instead of running")
	flag.BoolVar(&version, "version", false, "Get the version")
	flag.BoolVar(&license, "license", false, "View license")
	flag.BoolVar(&regenerate, "regenerate", false, "Regenerate pufferd templates")
	flag.BoolVar(&migrate, "migrate", false, "Migrate Scales data to pufferd")
	flag.StringVar(&configPath, "config", "config.json", "Path to pufferd config.json")
	flag.IntVar(&shutdownPid, "shutdown", 0, "PID to shut down")
	flag.IntVar(&reloadPid, "reload", 0, "PID to shut down")
	flag.BoolVar(&runDaemon, "run", false, "Runs the daemon")
	flag.Parse()

	versionString := fmt.Sprintf("pufferd %s (%s)", VERSION, GITHASH)

	if shutdownPid != 0 {
		logging.Info("Shutting down")
		commands.Shutdown(shutdownPid)
	}

	if reloadPid != 0 {
		logging.Info("Reloading")
		commands.Reload(reloadPid)
	}

	if _, err := os.Stat(configPath); os.IsNotExist(err) && !runInstaller && !version && reloadPid == 0 && shutdownPid == 0 {
		if _, err := os.Stat("/etc/pufferd/config.json"); err == nil {
			logging.Info("No config passed, defaulting to /etc/pufferd/config.json")
			configPath = "/etc/pufferd/config.json"
		} else {
			logging.Error("Cannot find a config file!")
			return
		}
	}

	if version {
		os.Stdout.WriteString(versionString + "\r\n")
	}

	if license {
		os.Stdout.WriteString(data.LICENSE + "\r\n")
	}

	if regenerate {
		commands.Regenerate(configPath)
	}

	if migrate {
		config.Load(configPath)
		migration.MigrateFromScales()
	}

	if license || version || regenerate || migrate || shutdownPid != 0 || reloadPid != 0 {
		return
	}

	config.Load(configPath)

	logging.SetLevelByString(loggingLevel)
	var defaultLogFolder = "logs"
	if runtime.GOOS == "linux" {
		defaultLogFolder = "/var/log/pufferd"
	}
	var logPath = config.GetStringOrDefault("logPath", defaultLogFolder)
	logging.SetLogFolder(logPath)
	logging.Init()
	gin.SetMode(gin.ReleaseMode)

	logging.Info(versionString)
	logging.Info("Logging set to " + loggingLevel)

	if runInstaller {
		install.Install(configPath, authRoot, authToken)
	}

	if runInstaller || !runDaemon {
		return
	}

	programs.Initialize()

	if _, err := os.Stat(programs.TemplateFolder); os.IsNotExist(err) {
		logging.Info("No template directory found, creating")
		err = os.MkdirAll(programs.TemplateFolder, 0755)
		if err != nil {
			logging.Error("Error creating template folder", err)
		}

	}
	if files, _ := ioutil.ReadDir(programs.TemplateFolder); len(files) == 0 {
		logging.Info("Templates being copied to " + programs.TemplateFolder)
		templates.CopyTemplates()
	}

	if _, err := os.Stat(programs.ServerFolder); os.IsNotExist(err) {
		logging.Info("No server directory found, creating")
		os.MkdirAll(programs.ServerFolder, 0755)
	}

	//check if there's an update
	go CheckForUpdate()

	programs.LoadFromFolder()

	programs.InitService()

	for _, element := range programs.GetAll() {
		if element.IsEnabled() {
			element.GetEnvironment().DisplayToConsole("Daemon has been started")
			if element.IsAutoStart() {
				logging.Info("Queued server " + element.Id())
				element.GetEnvironment().DisplayToConsole("Server has been queued to start")
				programs.StartViaService(element)
			}
		}
	}

	CreateHook()

	for runService {
		runServices()
	}

	shutdown.Shutdown()
}

func runServices() {
	r := routing.ConfigureWeb()

	useHttps := false

	dataFolder := config.GetStringOrDefault("datafolder", "data")
	httpsPem := filepath.Join(dataFolder, "https.pem")
	httpsKey := filepath.Join(dataFolder, "https.key")

	if _, err := os.Stat(httpsPem); os.IsNotExist(err) {
		logging.Warn("No HTTPS.PEM found in data folder, will use http instead")
	} else if _, err := os.Stat(httpsKey); os.IsNotExist(err) {
		logging.Warn("No HTTPS.KEY found in data folder, will use http instead")
	} else {
		useHttps = true
	}

	sftp.Run()

	web := config.GetStringOrDefault("web", config.GetStringOrDefault("webhost", "0.0.0.0")+":"+config.GetStringOrDefault("webport", "5656"))

	logging.Infof("Starting web access on %s", web)
	var err error
	if useHttps {
		err = manners.ListenAndServeTLS(web, httpsPem, httpsKey, r)
	} else {
		err = manners.ListenAndServe(web, r)
	}
	if err != nil {
		logging.Error("Error starting web service", err)
	}
}

func CreateHook() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.Signal(15), syscall.Signal(1))
	go func() {
		defer func() {
			if err := recover(); err != nil {
				logging.Errorf("Error: %+v\n%s", err, debug.Stack())
			}
		}()

		var sig os.Signal

		for sig != syscall.Signal(15) {
			sig = <-c
			switch sig {
			case syscall.Signal(1):
				manners.Close()
				sftp.Stop()
				config.Load(configPath)
			}
		}

		runService = false
		shutdown.CompleteShutdown()
	}()
}

func CheckForUpdate() {
	if config.GetBoolOrDefault("update-check", true) {
		url := "https://dl.pufferpanel.com/pufferd/" + MAJORVERSION + "/version.txt"
		logging.Debug("Checking for updates using " + url)
		resp, err := http.Get(url)
		if err != nil {
			return
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return
		}
		onlineVersion := strings.TrimSpace(string(body))
		if string(onlineVersion) != GITHASH {
			logging.Infof("DL server reports a different hash than this version, an update may be available")
			logging.Infof("Installed: %s", GITHASH)
			logging.Infof("Online: %s", onlineVersion)
		}
	}
}
<commit_msg>Fix more logging<commit_after>/*
 Copyright 2016 Padduck, LLC

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 	http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/braintree/manners"
	"github.com/gin-gonic/gin"
	"github.com/pufferpanel/apufferi/config"
	"github.com/pufferpanel/apufferi/logging"
	"github.com/pufferpanel/pufferd/commands"
	"github.com/pufferpanel/pufferd/data"
	"github.com/pufferpanel/pufferd/data/templates"
	"github.com/pufferpanel/pufferd/install"
	"github.com/pufferpanel/pufferd/migration"
	"github.com/pufferpanel/pufferd/programs"
	"github.com/pufferpanel/pufferd/routing"
	"github.com/pufferpanel/pufferd/sftp"
	"github.com/pufferpanel/pufferd/shutdown"
	"net/http"
	"os/signal"
	"path/filepath"
	"runtime"
	"runtime/debug"
	"strings"
	"syscall"
)

var (
	VERSION      = "nightly"
	MAJORVERSION = "nightly"
	GITHASH      = "unknown"
)

var runService = true
var configPath string

func main() {
	var loggingLevel string
	var authRoot string
	var authToken string
	var runInstaller bool
	var version bool
	var license bool
	var regenerate bool
	var migrate bool
	var shutdownPid int
	var runDaemon bool
	var reloadPid int
	flag.StringVar(&loggingLevel, "logging", "INFO", "Lowest logging level to display")
	flag.StringVar(&authRoot, "auth", "", "Base URL to the authorization server")
	flag.StringVar(&authToken, "token", "", "Authorization token")
	flag.BoolVar(&runInstaller, "install", false, "If installing instead of running")
	flag.BoolVar(&version, "version", false, "Get the version")
	flag.BoolVar(&license, "license", false, "View license")
	flag.BoolVar(&regenerate, "regenerate", false, "Regenerate pufferd templates")
	flag.BoolVar(&migrate, "migrate", false, "Migrate Scales data to pufferd")
	flag.StringVar(&configPath, "config", "config.json", "Path to pufferd config.json")
	flag.IntVar(&shutdownPid, "shutdown", 0, "PID to shut down")
	flag.IntVar(&reloadPid, "reload", 0, "PID to shut down")
	flag.BoolVar(&runDaemon, "run", false, "Runs the daemon")
	flag.Parse()

	versionString := fmt.Sprintf("pufferd %s (%s)", VERSION, GITHASH)

	if shutdownPid != 0 {
		logging.Info("Shutting down")
		commands.Shutdown(shutdownPid)
	}

	if reloadPid != 0 {
		logging.Info("Reloading")
		commands.Reload(reloadPid)
	}

	if _, err := os.Stat(configPath); os.IsNotExist(err) && !runInstaller && !version && reloadPid == 0 && shutdownPid == 0 {
		if _, err := os.Stat("/etc/pufferd/config.json"); err == nil {
			logging.Info("No config passed, defaulting to /etc/pufferd/config.json")
			configPath = "/etc/pufferd/config.json"
		} else {
			logging.Error("Cannot find a config file!")
			return
		}
	}

	if version {
		os.Stdout.WriteString(versionString + "\r\n")
	}

	if license {
		os.Stdout.WriteString(data.LICENSE + "\r\n")
	}

	if regenerate {
		commands.Regenerate(configPath)
	}

	if migrate {
		config.Load(configPath)
		migration.MigrateFromScales()
	}

	if license || version || regenerate || migrate || shutdownPid != 0 || reloadPid != 0 {
		return
	}

	config.Load(configPath)

	logging.SetLevelByString(loggingLevel)
	var defaultLogFolder = "logs"
	if runtime.GOOS == "linux" {
		defaultLogFolder = "/var/log/pufferd"
	}
	var logPath = config.GetStringOrDefault("logPath", defaultLogFolder)
	logging.SetLogFolder(logPath)
	logging.Init()
	gin.SetMode(gin.ReleaseMode)

	logging.Info(versionString)
	logging.Info("Logging set to " + loggingLevel)

	if runInstaller {
		install.Install(configPath, authRoot, authToken)
	}

	if runInstaller || !runDaemon {
		return
	}

	programs.Initialize()

	if _, err := os.Stat(programs.TemplateFolder); os.IsNotExist(err) {
		logging.Info("No template directory found, creating")
		err = os.MkdirAll(programs.TemplateFolder, 0755)
		if err != nil {
			logging.Error("Error creating template folder", err)
		}

	}
	if files, _ := ioutil.ReadDir(programs.TemplateFolder); len(files) == 0 {
		logging.Info("Templates being copied to " + programs.TemplateFolder)
		templates.CopyTemplates()
	}

	if _, err := os.Stat(programs.ServerFolder); os.IsNotExist(err) {
		logging.Info("No server directory found, creating")
		os.MkdirAll(programs.ServerFolder, 0755)
	}

	//check if there's an update
	go CheckForUpdate()

	programs.LoadFromFolder()

	programs.InitService()

	for _, element := range programs.GetAll() {
		if element.IsEnabled() {
			element.GetEnvironment().DisplayToConsole("Daemon has been started\n")
			if element.IsAutoStart() {
				logging.Info("Queued server " + element.Id())
				element.GetEnvironment().DisplayToConsole("Server has been queued to start\n")
				programs.StartViaService(element)
			}
		}
	}

	CreateHook()

	for runService {
		runServices()
	}

	shutdown.Shutdown()
}

func runServices() {
	r := routing.ConfigureWeb()

	useHttps := false

	dataFolder := config.GetStringOrDefault("datafolder", "data")
	httpsPem := filepath.Join(dataFolder, "https.pem")
	httpsKey := filepath.Join(dataFolder, "https.key")

	if _, err := os.Stat(httpsPem); os.IsNotExist(err) {
		logging.Warn("No HTTPS.PEM found in data folder, will use http instead")
	} else if _, err := os.Stat(httpsKey); os.IsNotExist(err) {
		logging.Warn("No HTTPS.KEY found in data folder, will use http instead")
	} else {
		useHttps = true
	}

	sftp.Run()

	web := config.GetStringOrDefault("web", config.GetStringOrDefault("webhost", "0.0.0.0")+":"+config.GetStringOrDefault("webport", "5656"))

	logging.Infof("Starting web access on %s", web)
	var err error
	if useHttps {
		err = manners.ListenAndServeTLS(web, httpsPem, httpsKey, r)
	} else {
		err = manners.ListenAndServe(web, r)
	}
	if err != nil {
		logging.Error("Error starting web service", err)
	}
}

func CreateHook() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.Signal(15), syscall.Signal(1))
	go func() {
		defer func() {
			if err := recover(); err != nil {
				logging.Errorf("Error: %+v\n%s", err, debug.Stack())
			}
		}()

		var sig os.Signal

		for sig != syscall.Signal(15) {
			sig = <-c
			switch sig {
			case syscall.Signal(1):
				manners.Close()
				sftp.Stop()
				config.Load(configPath)
			}
		}

		runService = false
		shutdown.CompleteShutdown()
	}()
}

func CheckForUpdate() {
	if config.GetBoolOrDefault("update-check", true) {
		url := "https://dl.pufferpanel.com/pufferd/" + MAJORVERSION + "/version.txt"
		logging.Debug("Checking for updates using " + url)
		resp, err := http.Get(url)
		if err != nil {
			return
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return
		}
		onlineVersion := strings.TrimSpace(string(body))
		if string(onlineVersion) != GITHASH {
			logging.Infof("DL server reports a different hash than this version, an update may be available")
			logging.Infof("Installed: %s", GITHASH)
			logging.Infof("Online: %s", onlineVersion)
		}
	}
}
<|endoftext|>