<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/ahaynssen/slack-go-webhook"
	"github.com/objectpartners/rancher-service-updater/utils"
	"github.com/rancher/go-rancher/client"
)

type (
	//Config is the service configuration
	Config struct {
		EnableLabel      string
		EnvironmentNames []string
		Port             int
		CattleSecretKey  string
		CattleAccessKey  string
		CattleURL        string
		SlackWebhookURL  string
		SlackBotName     string
		Debug            bool
	}

	//ServiceUpdater is the service
	ServiceUpdater struct {
		Config *Config
		// client  *client.RancherClient
		service Service
		account Account
	}

	//UpdateCommand is payload for new image availability
	UpdateCommand struct {
		Image      string `json:"docker_image"`
		StartFirst bool   `json:"start_first"`
		Confirm    bool   `json:"confirm"`
		Timeout    int    `json:"timeout"`
	}

	//Service is Rancher Service interface
	Service interface {
		ById(id string) (*client.Service, error)
		List(opts *client.ListOpts) (*client.ServiceCollection, error)
		ActionFinishupgrade(*client.Service) (*client.Service, error)
		ActionUpgrade(*client.Service, *client.ServiceUpgrade) (*client.Service, error)
	}

	//Account is Rancher Environment interface
	Account interface {
		List(opts *client.ListOpts) (*client.AccountCollection, error)
	}
)

func main() {
	config := &Config{
		EnableLabel:      utils.GetEnvOrDefault("AUTOUPDATE_ENABLE_LABEL", "autoupdate.enable"),
		EnvironmentNames: utils.GetEnvOrDefaultArray("AUTOUPDATE_ENVIRONMENT_NAMES", []string{".*"}),
		Port:             utils.GetEnvOrDefaultInt("AUTOUPDATE_HTTP_PORT", 8080),
		CattleAccessKey:  os.Getenv("CATTLE_ACCESS_KEY"),
		CattleSecretKey:  os.Getenv("CATTLE_SECRET_KEY"),
		CattleURL:        os.Getenv("CATTLE_URL"),
		SlackWebhookURL:  os.Getenv("AUTOUPDATE_SLACK_WEBHOOK_URL"),
		SlackBotName:     utils.GetEnvOrDefault("AUTOUPDATE_SLACK_BOT_NAME", "rancher-service-updater"),
		Debug:            os.Getenv("DEBUG") != "",
	}
	serviceUpdater := &ServiceUpdater{
		Config: config,
	}
	serviceUpdater.init()
	serviceUpdater.listen()
}

func (s *ServiceUpdater) init() {
	c, err := client.NewRancherClient(&client.ClientOpts{
		AccessKey: s.Config.CattleAccessKey,
		SecretKey: s.Config.CattleSecretKey,
		Url:       s.Config.CattleURL,
	})
	if err != nil {
		log.Fatalf("Unable to create Rancher client: %s\n", err)
	}
	s.service = c.Service
	s.account = c.Account
}

func (s *ServiceUpdater) listen() {
	http.HandleFunc("/upgrade", s.upgrade)
	http.HandleFunc("/ping", s.ping)
	log.Printf("Started service on port %d\n", s.Config.Port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", s.Config.Port), nil)
	if err != nil {
		log.Fatalf("Unable to start service on port %d\n", s.Config.Port)
	}
}

func (s *ServiceUpdater) ping(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Pong!"))
	return
}

func (s *ServiceUpdater) upgrade(w http.ResponseWriter, r *http.Request) {
	command := UpdateCommand{
		StartFirst: false,
		Timeout:    30,
	}

	err := json.NewDecoder(r.Body).Decode(&command)
	if err != nil {
		log.Printf("%s\n", err.Error())
		utils.SendError(w, err.Error(), 400)
		return
	}
	txt, _ := json.Marshal(command)
	// if s.Config.Debug {
	fmt.Printf("Received upgrade: %s", string(txt))
	// }
	go s.upgradeService(command)
	w.WriteHeader(200)
	return
}

func (s *ServiceUpdater) upgradeService(command UpdateCommand) {
	if !strings.HasPrefix(command.Image, "docker:") {
		command.Image = fmt.Sprintf("docker:%s", command.Image)
	}
	parts := strings.Split(command.Image, ":")
	wantedImage := parts[1]
	wantedVer := parts[2]

	services, err := s.service.List(&client.ListOpts{})
	if err != nil {
		fmt.Printf("Failed to list rancher services: %s\n", err)
		return
	}

	environments, err := s.account.List(&client.ListOpts{})
	if err != nil {
		fmt.Printf("Failed to get environments: %s\n", err)
		return
	}
	envs := make(map[string]string)
	for environments != nil {
		for _, env := range environments.Data {
			envs[env.Id] = env.Name
		}
		environments, err = environments.Next()
		if err != nil {
			fmt.Printf("Failed: %s\n", err)
			return
		}
	}

	var enabledLabel = s.Config.EnableLabel
	for services != nil {
		for _, svc := range services.Data {
			log.Printf("Checking service: %s", svc.Name)
			if svc.LaunchConfig != nil {
				if _, ok := svc.LaunchConfig.Labels[enabledLabel]; ok {
					log.Printf("Attempting to update service %s\n", svc.Name)
					parts := strings.Split(svc.LaunchConfig.ImageUuid, ":")
					foundImage := parts[1]
					foundVer := parts[2]
					if utils.EnvironmentEnabled(envs[svc.AccountId], s.Config.EnvironmentNames) {
						if foundImage == wantedImage && ((foundVer < wantedVer) || (wantedVer == "latest")) {
							fmt.Println("Trying to upgrade...")
							err := s.doUpgrade(command, svc)
							if err != nil {
								fmt.Println(err.Error())
							} else {
								if command.Confirm {
									fmt.Println("Trying to confirm...")
									err := s.confirmUpgrade(command, svc)
									url := fmt.Sprintf("%s/env/%s/apps/stacks/%s", s.Config.CattleURL, svc.AccountId, svc.EnvironmentId)
									if err != nil {
										fmt.Printf("Unable to upgrade service %s: %s\n", svc.Name, err.Error())
										message := fmt.Sprintf("Unable to confirm upgrade to `%s`.\nCheck status at <%[2]s|%[1]s>", svc.Name, url)
										s.slackMessage("danger", message)
									} else {
										fmt.Printf("Upgraded %s to %s\n", svc.Name, command.Image)
										message := fmt.Sprintf("`%[1]s` has been successfully upgraded to `%[2]s` "+
											"in %[4]s\n View in Rancher here: <%[3]s|%[1]s>", svc.Name, wantedVer, url, envs[svc.AccountId])
										s.slackMessage("good", message)

									}
								}
							}
							continue
						}
					} else {
						log.Printf("Updating not enabled for environment %s\n", envs[svc.AccountId])
					}
				}
			}
		}
		services, _ = services.Next()
	}
}

func (s *ServiceUpdater) doUpgrade(command UpdateCommand, service client.Service) error {
	service.LaunchConfig.ImageUuid = command.Image
	upgrade := &client.ServiceUpgrade{}
	upgrade.InServiceStrategy = &client.InServiceUpgradeStrategy{
		LaunchConfig:           service.LaunchConfig,
		SecondaryLaunchConfigs: service.SecondaryLaunchConfigs,
		StartFirst:             command.StartFirst,
	}
	upgrade.ToServiceStrategy = &client.ToServiceUpgradeStrategy{}
	_, err := s.service.ActionUpgrade(&service, upgrade)
	return err
}

func (s *ServiceUpdater) confirmUpgrade(command UpdateCommand, service client.Service) error {
	srv, err := utils.Retry(func() (interface{}, error) {
		s, e := s.service.ById(service.Id)
		if e != nil {
			return nil, e
		}
		if s.State != "upgraded" {
			return nil, fmt.Errorf("Service not upgraded: %s\n", s.State)
		}
		return s, nil
	}, time.Duration(command.Timeout)*time.Second, 3*time.Second)
	if err != nil {
		return err
	}

	srv, err = s.service.ActionFinishupgrade(srv.(*client.Service))
	if err != nil {
		return err
	}
	fmt.Printf("Finished upgrade on %s\n", srv.(*client.Service).Name)
	return err
}

func (s *ServiceUpdater) slackMessage(status string, message string) {
	if s.Config.SlackWebhookURL != "" {
		attachment := slack.Attachment{Color: &status, Text: &message}
		mrkdwn := "text"
		attachment.AddMrkdwn(&mrkdwn)
		payload := slack.Payload{
			Username:    s.Config.SlackBotName,
			Attachments: []slack.Attachment{attachment},
		}
		printable, _ := json.Marshal(payload)
		fmt.Println(string(printable))
		err := slack.Send(s.Config.SlackWebhookURL, "", payload)
		if len(err) > 0 {
			fmt.Printf("error sending Slack message: %s\n", err)
		}
	}
}
<commit_msg>More logging.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/ahaynssen/slack-go-webhook"
	"github.com/objectpartners/rancher-service-updater/utils"
	"github.com/rancher/go-rancher/client"
)

type (
	//Config is the service configuration
	Config struct {
		EnableLabel      string
		EnvironmentNames []string
		Port             int
		CattleSecretKey  string
		CattleAccessKey  string
		CattleURL        string
		SlackWebhookURL  string
		SlackBotName     string
		Debug            bool
	}

	//ServiceUpdater is the service
	ServiceUpdater struct {
		Config *Config
		// client  *client.RancherClient
		service Service
		account Account
	}

	//UpdateCommand is payload for new image availability
	UpdateCommand struct {
		Image      string `json:"docker_image"`
		StartFirst bool   `json:"start_first"`
		Confirm    bool   `json:"confirm"`
		Timeout    int    `json:"timeout"`
	}

	//Service is Rancher Service interface
	Service interface {
		ById(id string) (*client.Service, error)
		List(opts *client.ListOpts) (*client.ServiceCollection, error)
		ActionFinishupgrade(*client.Service) (*client.Service, error)
		ActionUpgrade(*client.Service, *client.ServiceUpgrade) (*client.Service, error)
	}

	//Account is Rancher Environment interface
	Account interface {
		List(opts *client.ListOpts) (*client.AccountCollection, error)
	}
)

func main() {
	config := &Config{
		EnableLabel:      utils.GetEnvOrDefault("AUTOUPDATE_ENABLE_LABEL", "autoupdate.enable"),
		EnvironmentNames: utils.GetEnvOrDefaultArray("AUTOUPDATE_ENVIRONMENT_NAMES", []string{".*"}),
		Port:             utils.GetEnvOrDefaultInt("AUTOUPDATE_HTTP_PORT", 8080),
		CattleAccessKey:  os.Getenv("CATTLE_ACCESS_KEY"),
		CattleSecretKey:  os.Getenv("CATTLE_SECRET_KEY"),
		CattleURL:        os.Getenv("CATTLE_URL"),
		SlackWebhookURL:  os.Getenv("AUTOUPDATE_SLACK_WEBHOOK_URL"),
		SlackBotName:     utils.GetEnvOrDefault("AUTOUPDATE_SLACK_BOT_NAME", "rancher-service-updater"),
		Debug:            os.Getenv("DEBUG") != "",
	}
	serviceUpdater := &ServiceUpdater{
		Config: config,
	}
	serviceUpdater.init()
	serviceUpdater.listen()
}

func (s *ServiceUpdater) init() {
	c, err := client.NewRancherClient(&client.ClientOpts{
		AccessKey: s.Config.CattleAccessKey,
		SecretKey: s.Config.CattleSecretKey,
		Url:       s.Config.CattleURL,
	})
	if err != nil {
		log.Fatalf("Unable to create Rancher client: %s\n", err)
	}
	s.service = c.Service
	s.account = c.Account
}

func (s *ServiceUpdater) listen() {
	http.HandleFunc("/upgrade", s.upgrade)
	http.HandleFunc("/ping", s.ping)
	log.Printf("Started service on port %d\n", s.Config.Port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", s.Config.Port), nil)
	if err != nil {
		log.Fatalf("Unable to start service on port %d\n", s.Config.Port)
	}
}

func (s *ServiceUpdater) ping(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Pong!"))
	return
}

func (s *ServiceUpdater) upgrade(w http.ResponseWriter, r *http.Request) {
	command := UpdateCommand{
		StartFirst: false,
		Timeout:    30,
	}

	err := json.NewDecoder(r.Body).Decode(&command)
	if err != nil {
		log.Printf("%s\n", err.Error())
		utils.SendError(w, err.Error(), 400)
		return
	}
	txt, _ := json.Marshal(command)
	if s.Config.Debug {
		fmt.Printf("Received upgrade: %s", string(txt))
	}
	go s.upgradeService(command)
	w.WriteHeader(200)
	return
}

func (s *ServiceUpdater) upgradeService(command UpdateCommand) {
	if !strings.HasPrefix(command.Image, "docker:") {
		command.Image = fmt.Sprintf("docker:%s", command.Image)
	}
	parts := strings.Split(command.Image, ":")
	wantedImage := parts[1]
	wantedVer := parts[2]

	services, err := s.service.List(&client.ListOpts{})
	if err != nil {
		fmt.Printf("Failed to list rancher services: %s\n", err)
		return
	}

	environments, err := s.account.List(&client.ListOpts{})
	if err != nil {
		fmt.Printf("Failed to get environments: %s\n", err)
		return
	}
	envs := make(map[string]string)
	for environments != nil {
		for _, env := range environments.Data {
			envs[env.Id] = env.Name
		}
		environments, err = environments.Next()
		if err != nil {
			fmt.Printf("Failed: %s\n", err)
			return
		}
	}

	var enabledLabel = s.Config.EnableLabel
	for services != nil {
		for _, svc := range services.Data {
			if s.Config.Debug {
				log.Printf("Checking service: %s", svc.Name)
			}
			if svc.LaunchConfig != nil {
				if enable, ok := svc.LaunchConfig.Labels[enabledLabel]; ok && enable != "false" {
					if s.Config.Debug {
						log.Printf("Attempting to update service %s\n", svc.Name)
					}
					parts := strings.Split(svc.LaunchConfig.ImageUuid, ":")
					foundImage := parts[1]
					foundVer := parts[2]
					if utils.EnvironmentEnabled(envs[svc.AccountId], s.Config.EnvironmentNames) {
						if s.Config.Debug {
							log.Printf("Service %s Comparision: found-image %s, found-version %s, wanted-image %s, wanted-version %s\n", svc.Name, foundImage, foundVer, wantedImage, wantedVer)
						}
						if foundImage == wantedImage && ((foundVer < wantedVer) || (wantedVer == "latest")) {
							fmt.Println("Trying to upgrade...")
							err := s.doUpgrade(command, svc)
							if err != nil {
								fmt.Println(err.Error())
							} else {
								if command.Confirm {
									fmt.Println("Trying to confirm...")
									err := s.confirmUpgrade(command, svc)
									url := fmt.Sprintf("%s/env/%s/apps/stacks/%s", s.Config.CattleURL, svc.AccountId, svc.EnvironmentId)
									if err != nil {
										fmt.Printf("Unable to upgrade service %s: %s\n", svc.Name, err.Error())
										message := fmt.Sprintf("Unable to confirm upgrade to `%s`.\nCheck status at <%[2]s|%[1]s>", svc.Name, url)
										s.slackMessage("danger", message)
									} else {
										fmt.Printf("Upgraded %s to %s\n", svc.Name, command.Image)
										message := fmt.Sprintf("`%[1]s` has been successfully upgraded to `%[2]s` "+
											"in %[4]s\n View in Rancher here: <%[3]s|%[1]s>", svc.Name, wantedVer, url, envs[svc.AccountId])
										s.slackMessage("good", message)

									}
								}
							}
							continue
						} else if s.Config.Debug {
							log.Printf("Updating enabled from environment [%s], service [%s], but published version [%s] was not newer than current version [%s]\n", envs[svc.AccountId], svc.Name, wantedVer, foundVer)
						}
					} else if s.Config.Debug {
						log.Printf("Updating not enabled for environment %s\n", envs[svc.AccountId])
					}
				}
			}
		}
		services, _ = services.Next()
	}
}

func (s *ServiceUpdater) doUpgrade(command UpdateCommand, service client.Service) error {
	service.LaunchConfig.ImageUuid = command.Image
	upgrade := &client.ServiceUpgrade{}
	upgrade.InServiceStrategy = &client.InServiceUpgradeStrategy{
		LaunchConfig:           service.LaunchConfig,
		SecondaryLaunchConfigs: service.SecondaryLaunchConfigs,
		StartFirst:             command.StartFirst,
	}
	upgrade.ToServiceStrategy = &client.ToServiceUpgradeStrategy{}
	_, err := s.service.ActionUpgrade(&service, upgrade)
	return err
}

func (s *ServiceUpdater) confirmUpgrade(command UpdateCommand, service client.Service) error {
	srv, err := utils.Retry(func() (interface{}, error) {
		s, e := s.service.ById(service.Id)
		if e != nil {
			return nil, e
		}
		if s.State != "upgraded" {
			return nil, fmt.Errorf("Service not upgraded: %s\n", s.State)
		}
		return s, nil
	}, time.Duration(command.Timeout)*time.Second, 3*time.Second)
	if err != nil {
		return err
	}

	srv, err = s.service.ActionFinishupgrade(srv.(*client.Service))
	if err != nil {
		return err
	}
	fmt.Printf("Finished upgrade on %s\n", srv.(*client.Service).Name)
	return err
}

func (s *ServiceUpdater) slackMessage(status string, message string) {
	if s.Config.SlackWebhookURL != "" {
		attachment := slack.Attachment{Color: &status, Text: &message}
		mrkdwn := "text"
		attachment.AddMrkdwn(&mrkdwn)
		payload := slack.Payload{
			Username:    s.Config.SlackBotName,
			Attachments: []slack.Attachment{attachment},
		}
		printable, _ := json.Marshal(payload)
		fmt.Println(string(printable))
		err := slack.Send(s.Config.SlackWebhookURL, "", payload)
		if len(err) > 0 {
			fmt.Printf("error sending Slack message: %s\n", err)
		}
	}
}
<|endoftext|>


<commit_before>package clitest

import (
	"os"
	"reflect"
	"sync"
	"testing"
	"time"

	"src.elv.sh/pkg/cli"
	"src.elv.sh/pkg/cli/term"
	"src.elv.sh/pkg/testutil"
)

const (
	// Maximum number of buffer updates FakeTTY expect to see.
	fakeTTYBufferUpdates = 4096
	// Maximum number of events FakeTTY produces.
	fakeTTYEvents = 4096
	// Maximum number of signals FakeTTY produces.
	fakeTTYSignals = 4096
)

// An implementation of the cli.TTY interface that is useful in tests.
type fakeTTY struct {
	setup func() (func(), error)
	// Channel that StartRead returns. Can be used to inject additional events.
	eventCh chan term.Event
	// Whether eventCh has been closed.
	eventChClosed bool
	// Mutex for synchronizing writing and closing eventCh.
	eventChMutex sync.Mutex
	// Channel for publishing updates of the main buffer and notes buffer.
	bufCh, notesBufCh chan *term.Buffer
	// Records history of the main buffer and notes buffer.
	bufs, notesBufs []*term.Buffer
	// Mutexes for guarding bufs and notesBufs.
	bufMutex sync.RWMutex
	// Channel that NotifySignals returns. Can be used to inject signals.
	sigCh chan os.Signal
	// Argument that SetRawInput got.
	raw int
	// Number of times the TTY screen has been cleared, incremented in
	// ClearScreen.
	cleared int

	sizeMutex sync.RWMutex
	// Predefined sizes.
	height, width int
}

// Initial size of fake TTY.
const (
	FakeTTYHeight = 20
	FakeTTYWidth  = 50
)

// NewFakeTTY creates a new FakeTTY and a handle for controlling it. The initial
// size of the terminal is FakeTTYHeight and FakeTTYWidth.
func NewFakeTTY() (cli.TTY, TTYCtrl) {
	tty := &fakeTTY{
		eventCh:    make(chan term.Event, fakeTTYEvents),
		sigCh:      make(chan os.Signal, fakeTTYSignals),
		bufCh:      make(chan *term.Buffer, fakeTTYBufferUpdates),
		notesBufCh: make(chan *term.Buffer, fakeTTYBufferUpdates),
		height:     FakeTTYHeight, width: FakeTTYWidth,
	}
	return tty, TTYCtrl{tty}
}

// Delegates to the setup function specified using the SetSetup method of
// TTYCtrl, or return a nop function and a nil error.
func (t *fakeTTY) Setup() (func(), error) {
	if t.setup == nil {
		return func() {}, nil
	}
	return t.setup()
}

// Returns the size specified by using the SetSize method of TTYCtrl.
func (t *fakeTTY) Size() (h, w int) {
	t.sizeMutex.RLock()
	defer t.sizeMutex.RUnlock()
	return t.height, t.width
}

// Returns next event from t.eventCh.
func (t *fakeTTY) ReadEvent() (term.Event, error) {
	return <-t.eventCh, nil
}

// Records the argument.
func (t *fakeTTY) SetRawInput(n int) {
	t.raw = n
}

// Closes eventCh.
func (t *fakeTTY) CloseReader() {
	t.eventChMutex.Lock()
	defer t.eventChMutex.Unlock()
	close(t.eventCh)
	t.eventChClosed = true
}

// Returns the last recorded buffer.
func (t *fakeTTY) Buffer() *term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	return t.bufs[len(t.bufs)-1]
}

// Records a nil buffer.
func (t *fakeTTY) ResetBuffer() {
	t.bufMutex.Lock()
	defer t.bufMutex.Unlock()
	t.recordBuf(nil)
}

// UpdateBuffer records a new pair of buffers, i.e. sending them to their
// respective channels and appending them to their respective slices.
func (t *fakeTTY) UpdateBuffer(bufNotes, buf *term.Buffer, _ bool) error {
	t.bufMutex.Lock()
	defer t.bufMutex.Unlock()
	t.recordNotesBuf(bufNotes)
	t.recordBuf(buf)
	return nil
}

func (t *fakeTTY) HideCursor() {
}

func (t *fakeTTY) ShowCursor() {
}

func (t *fakeTTY) ClearScreen() {
	t.cleared++
}

func (t *fakeTTY) NotifySignals() <-chan os.Signal { return t.sigCh }

func (t *fakeTTY) StopSignals() { close(t.sigCh) }

func (t *fakeTTY) recordBuf(buf *term.Buffer) {
	t.bufs = append(t.bufs, buf)
	t.bufCh <- buf
}

func (t *fakeTTY) recordNotesBuf(buf *term.Buffer) {
	t.notesBufs = append(t.notesBufs, buf)
	t.notesBufCh <- buf
}

// TTYCtrl is an interface for controlling a fake terminal.
type TTYCtrl struct{ *fakeTTY }

// GetTTYCtrl takes a TTY and returns a TTYCtrl and true, if the TTY is a fake
// terminal. Otherwise it returns an invalid TTYCtrl and false.
func GetTTYCtrl(t cli.TTY) (TTYCtrl, bool) {
	fake, ok := t.(*fakeTTY)
	return TTYCtrl{fake}, ok
}

// SetSetup sets the return values of the Setup method of the fake terminal.
func (t TTYCtrl) SetSetup(restore func(), err error) {
	t.setup = func() (func(), error) {
		return restore, err
	}
}

// SetSize sets the size of the fake terminal.
func (t TTYCtrl) SetSize(h, w int) {
	t.sizeMutex.Lock()
	defer t.sizeMutex.Unlock()
	t.height, t.width = h, w
}

// Inject injects events to the fake terminal.
func (t TTYCtrl) Inject(events ...term.Event) {
	for _, event := range events {
		t.inject(event)
	}
}

func (t TTYCtrl) inject(event term.Event) {
	t.eventChMutex.Lock()
	defer t.eventChMutex.Unlock()
	if !t.eventChClosed {
		t.eventCh <- event
	}
}

// EventCh returns the underlying channel for delivering events.
func (t TTYCtrl) EventCh() chan term.Event {
	return t.eventCh
}

// InjectSignal injects signals.
func (t TTYCtrl) InjectSignal(sigs ...os.Signal) {
	for _, sig := range sigs {
		t.sigCh <- sig
	}
}

// RawInput returns the argument in the last call to the SetRawInput method of
// the TTY.
func (t TTYCtrl) RawInput() int {
	return t.raw
}

// ScreenCleared returns the number of times ClearScreen has been called on the
// TTY.
func (t TTYCtrl) ScreenCleared() int {
	return t.cleared
}

// TestBuffer verifies that a buffer will appear within the timeout of 4
// seconds, and fails the test if it doesn't
func (t TTYCtrl) TestBuffer(tt *testing.T, b *term.Buffer) {
	tt.Helper()
	ok := testBuffer(tt, b, t.bufCh)
	if !ok {
		t.bufMutex.RLock()
		defer t.bufMutex.RUnlock()
		lastBuf := t.LastBuffer()
		tt.Logf("Last buffer: %s", lastBuf.TTYString())
		if lastBuf == nil {
			bufs := t.BufferHistory()
			for i := len(bufs) - 1; i >= 0; i-- {
				if bufs[i] != nil {
					tt.Logf("Last non-nil buffer: %s", bufs[i].TTYString())
					return
				}
			}
		}
	}
}

// TestNotesBuffer verifies that a notes buffer will appear within the timeout of 4
// seconds, and fails the test if it doesn't
func (t TTYCtrl) TestNotesBuffer(tt *testing.T, b *term.Buffer) {
	tt.Helper()
	ok := testBuffer(tt, b, t.notesBufCh)
	if !ok {
		t.bufMutex.RLock()
		defer t.bufMutex.RUnlock()
		bufs := t.NotesBufferHistory()
		tt.Logf("There has been %d notes buffers. None-nil ones are:", len(bufs))
		for i, buf := range bufs {
			if buf != nil {
				tt.Logf("#%d:\n%s", i, buf.TTYString())
			}
		}
	}
}

// BufferHistory returns a slice of all buffers that have appeared.
func (t TTYCtrl) BufferHistory() []*term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	return t.bufs
}

// LastBuffer returns the last buffer that has appeared.
func (t TTYCtrl) LastBuffer() *term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	if len(t.bufs) == 0 {
		return nil
	}
	return t.bufs[len(t.bufs)-1]
}

// NotesBufferHistory returns a slice of all notes buffers that have appeared.
func (t TTYCtrl) NotesBufferHistory() []*term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	return t.notesBufs
}

func (t TTYCtrl) LastNotesBuffer() *term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	if len(t.notesBufs) == 0 {
		return nil
	}
	return t.notesBufs[len(t.notesBufs)-1]
}

// Tests that an expected buffer will appear within the timeout.
func testBuffer(t *testing.T, want *term.Buffer, ch <-chan *term.Buffer) bool {
	t.Helper()

	timeout := time.After(testutil.Scaled(100 * time.Millisecond))
	for {
		select {
		case buf := <-ch:
			if reflect.DeepEqual(buf, want) {
				return true
			}
		case <-timeout:
			t.Errorf("Wanted buffer not shown")
			t.Logf("Want: %s", want.TTYString())
			return false
		}
	}
}
<commit_msg>clitest: Make failures in TestBuffer and TestNotesBuffer fatal.<commit_after>package clitest

import (
	"os"
	"reflect"
	"sync"
	"testing"
	"time"

	"src.elv.sh/pkg/cli"
	"src.elv.sh/pkg/cli/term"
	"src.elv.sh/pkg/testutil"
)

const (
	// Maximum number of buffer updates FakeTTY expect to see.
	fakeTTYBufferUpdates = 4096
	// Maximum number of events FakeTTY produces.
	fakeTTYEvents = 4096
	// Maximum number of signals FakeTTY produces.
	fakeTTYSignals = 4096
)

// An implementation of the cli.TTY interface that is useful in tests.
type fakeTTY struct {
	setup func() (func(), error)
	// Channel that StartRead returns. Can be used to inject additional events.
	eventCh chan term.Event
	// Whether eventCh has been closed.
	eventChClosed bool
	// Mutex for synchronizing writing and closing eventCh.
	eventChMutex sync.Mutex
	// Channel for publishing updates of the main buffer and notes buffer.
	bufCh, notesBufCh chan *term.Buffer
	// Records history of the main buffer and notes buffer.
	bufs, notesBufs []*term.Buffer
	// Mutexes for guarding bufs and notesBufs.
	bufMutex sync.RWMutex
	// Channel that NotifySignals returns. Can be used to inject signals.
	sigCh chan os.Signal
	// Argument that SetRawInput got.
	raw int
	// Number of times the TTY screen has been cleared, incremented in
	// ClearScreen.
	cleared int

	sizeMutex sync.RWMutex
	// Predefined sizes.
	height, width int
}

// Initial size of fake TTY.
const (
	FakeTTYHeight = 20
	FakeTTYWidth  = 50
)

// NewFakeTTY creates a new FakeTTY and a handle for controlling it. The initial
// size of the terminal is FakeTTYHeight and FakeTTYWidth.
func NewFakeTTY() (cli.TTY, TTYCtrl) {
	tty := &fakeTTY{
		eventCh:    make(chan term.Event, fakeTTYEvents),
		sigCh:      make(chan os.Signal, fakeTTYSignals),
		bufCh:      make(chan *term.Buffer, fakeTTYBufferUpdates),
		notesBufCh: make(chan *term.Buffer, fakeTTYBufferUpdates),
		height:     FakeTTYHeight, width: FakeTTYWidth,
	}
	return tty, TTYCtrl{tty}
}

// Delegates to the setup function specified using the SetSetup method of
// TTYCtrl, or return a nop function and a nil error.
func (t *fakeTTY) Setup() (func(), error) {
	if t.setup == nil {
		return func() {}, nil
	}
	return t.setup()
}

// Returns the size specified by using the SetSize method of TTYCtrl.
func (t *fakeTTY) Size() (h, w int) {
	t.sizeMutex.RLock()
	defer t.sizeMutex.RUnlock()
	return t.height, t.width
}

// Returns next event from t.eventCh.
func (t *fakeTTY) ReadEvent() (term.Event, error) {
	return <-t.eventCh, nil
}

// Records the argument.
func (t *fakeTTY) SetRawInput(n int) {
	t.raw = n
}

// Closes eventCh.
func (t *fakeTTY) CloseReader() {
	t.eventChMutex.Lock()
	defer t.eventChMutex.Unlock()
	close(t.eventCh)
	t.eventChClosed = true
}

// Returns the last recorded buffer.
func (t *fakeTTY) Buffer() *term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	return t.bufs[len(t.bufs)-1]
}

// Records a nil buffer.
func (t *fakeTTY) ResetBuffer() {
	t.bufMutex.Lock()
	defer t.bufMutex.Unlock()
	t.recordBuf(nil)
}

// UpdateBuffer records a new pair of buffers, i.e. sending them to their
// respective channels and appending them to their respective slices.
func (t *fakeTTY) UpdateBuffer(bufNotes, buf *term.Buffer, _ bool) error {
	t.bufMutex.Lock()
	defer t.bufMutex.Unlock()
	t.recordNotesBuf(bufNotes)
	t.recordBuf(buf)
	return nil
}

func (t *fakeTTY) HideCursor() {
}

func (t *fakeTTY) ShowCursor() {
}

func (t *fakeTTY) ClearScreen() {
	t.cleared++
}

func (t *fakeTTY) NotifySignals() <-chan os.Signal { return t.sigCh }

func (t *fakeTTY) StopSignals() { close(t.sigCh) }

func (t *fakeTTY) recordBuf(buf *term.Buffer) {
	t.bufs = append(t.bufs, buf)
	t.bufCh <- buf
}

func (t *fakeTTY) recordNotesBuf(buf *term.Buffer) {
	t.notesBufs = append(t.notesBufs, buf)
	t.notesBufCh <- buf
}

// TTYCtrl is an interface for controlling a fake terminal.
type TTYCtrl struct{ *fakeTTY }

// GetTTYCtrl takes a TTY and returns a TTYCtrl and true, if the TTY is a fake
// terminal. Otherwise it returns an invalid TTYCtrl and false.
func GetTTYCtrl(t cli.TTY) (TTYCtrl, bool) {
	fake, ok := t.(*fakeTTY)
	return TTYCtrl{fake}, ok
}

// SetSetup sets the return values of the Setup method of the fake terminal.
func (t TTYCtrl) SetSetup(restore func(), err error) {
	t.setup = func() (func(), error) {
		return restore, err
	}
}

// SetSize sets the size of the fake terminal.
func (t TTYCtrl) SetSize(h, w int) {
	t.sizeMutex.Lock()
	defer t.sizeMutex.Unlock()
	t.height, t.width = h, w
}

// Inject injects events to the fake terminal.
func (t TTYCtrl) Inject(events ...term.Event) {
	for _, event := range events {
		t.inject(event)
	}
}

func (t TTYCtrl) inject(event term.Event) {
	t.eventChMutex.Lock()
	defer t.eventChMutex.Unlock()
	if !t.eventChClosed {
		t.eventCh <- event
	}
}

// EventCh returns the underlying channel for delivering events.
func (t TTYCtrl) EventCh() chan term.Event {
	return t.eventCh
}

// InjectSignal injects signals.
func (t TTYCtrl) InjectSignal(sigs ...os.Signal) {
	for _, sig := range sigs {
		t.sigCh <- sig
	}
}

// RawInput returns the argument in the last call to the SetRawInput method of
// the TTY.
func (t TTYCtrl) RawInput() int {
	return t.raw
}

// ScreenCleared returns the number of times ClearScreen has been called on the
// TTY.
func (t TTYCtrl) ScreenCleared() int {
	return t.cleared
}

// TestBuffer verifies that a buffer will appear within 100ms, and aborts the
// test if it doesn't.
func (t TTYCtrl) TestBuffer(tt *testing.T, b *term.Buffer) {
	tt.Helper()
	ok := testBuffer(b, t.bufCh)
	if !ok {
		tt.Logf("wanted buffer not shown:\n%s", b.TTYString())

		t.bufMutex.RLock()
		defer t.bufMutex.RUnlock()
		lastBuf := t.LastBuffer()
		tt.Logf("Last buffer: %s", lastBuf.TTYString())
		if lastBuf == nil {
			bufs := t.BufferHistory()
			for i := len(bufs) - 1; i >= 0; i-- {
				if bufs[i] != nil {
					tt.Logf("Last non-nil buffer: %s", bufs[i].TTYString())
					return
				}
			}
		}
		tt.FailNow()
	}
}

// TestNotesBuffer verifies that a notes buffer will appear within 100ms, and
// aborts the test if it doesn't.
func (t TTYCtrl) TestNotesBuffer(tt *testing.T, b *term.Buffer) {
	tt.Helper()
	ok := testBuffer(b, t.notesBufCh)
	if !ok {
		tt.Logf("wanted notes buffer not shown:\n%s", b.TTYString())

		t.bufMutex.RLock()
		defer t.bufMutex.RUnlock()
		bufs := t.NotesBufferHistory()
		tt.Logf("There has been %d notes buffers. None-nil ones are:", len(bufs))
		for i, buf := range bufs {
			if buf != nil {
				tt.Logf("#%d:\n%s", i, buf.TTYString())
			}
		}
		tt.FailNow()
	}
}

// BufferHistory returns a slice of all buffers that have appeared.
func (t TTYCtrl) BufferHistory() []*term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	return t.bufs
}

// LastBuffer returns the last buffer that has appeared.
func (t TTYCtrl) LastBuffer() *term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	if len(t.bufs) == 0 {
		return nil
	}
	return t.bufs[len(t.bufs)-1]
}

// NotesBufferHistory returns a slice of all notes buffers that have appeared.
func (t TTYCtrl) NotesBufferHistory() []*term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	return t.notesBufs
}

func (t TTYCtrl) LastNotesBuffer() *term.Buffer {
	t.bufMutex.RLock()
	defer t.bufMutex.RUnlock()
	if len(t.notesBufs) == 0 {
		return nil
	}
	return t.notesBufs[len(t.notesBufs)-1]
}

// Tests that an buffer appears on the channel within 100ms.
func testBuffer(want *term.Buffer, ch <-chan *term.Buffer) bool {
	timeout := time.After(testutil.Scaled(100 * time.Millisecond))
	for {
		select {
		case buf := <-ch:
			if reflect.DeepEqual(buf, want) {
				return true
			}
		case <-timeout:
			return false
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
  "net/http"
  "os"
  "os/exec"
  "log"
  "fmt"
  "strings"
)

/*
 * POST /build/:host/:user/:name
 */
func JekyllBuild(rw http.ResponseWriter, r *http.Request) {
  // webhook must be POST
  if r.Method != "POST" {
    rw.WriteHeader(http.StatusNotFound)
    return
  }

  url := strings.Split(r.URL.Path, "/")
  host, user, name := url[2], url[3], url[4]
  tmp := "/tmp"

  dir  := tmp + "/src/" + name
  dest := tmp + "/build/" + name
  repo := "git@" + host + ":" + user + "/" + name + ".git"

  cmd := []string{
    "git clone %[2]s %[1]s &&",
    "cd %[1]s;",
    // "git checkout master",
    "[ -f Gemfile ] && bundle install;",
    "jekyll build -s %[1]s -d %[3]s;",
    "rm -Rf %[1]s;"}

  log.Println("-----> Cloning " + repo)
  log.Println("-----> Building Jekyll site ...")
  out, err := exec.Command("sh", "-c", fmt.Sprintf(strings.Join(cmd, " "), dir, repo, dest)).Output()
  log.Println(string(out))
  if err != nil {
    log.Println("ERROR: %s", err)
    rw.WriteHeader(http.StatusInternalServerError)
    return
  }
  fmt.Println("-----> Jekyll site built successfully.")

  status, message := JekyllPublish(dest)
  fmt.Println(message)

  rw.WriteHeader(status)
}

func JekyllPublish(dir string) (int, string) {

  // Find S3 Bucket location from the BUCKET file
  bucket, err := exec.Command("sh", "-c", fmt.Sprintf("cd %s && cat BUCKET", dir)).Output()
  if err != nil {
    return 500, "No bucket specified"
  }

  // Sync files to S3
  fmt.Printf("-----> Publishing to Amazon S3 Bucket %s...\n", bucket)
  out, err := exec.Command(
    "s3cmd",
    "sync",
    dir + "/",
    "s3://" + string(bucket),
    "--delete-removed",
    "--acl-public",
    "--add-header=Cache-Control:max-age=60",
  ).CombinedOutput()
  fmt.Printf("%s", out)
  if err != nil {
    fmt.Printf("%s", err)
    return 500, "Problem syncing"
  }

  // TODO Optional - Use rsync to remote web server
  // rsync -avz --delete _site/ location:/var/www/html/jekyll

  // remove build
  os.RemoveAll(dir)

  return 200, "Success"
}

func main() {
  http.HandleFunc("/build/", JekyllBuild)
  port := os.Getenv("PORT")
  if port == "" {
      port = "8080"
  }
  http.ListenAndServe(":"+port, nil)
}

<commit_msg>use log for logging<commit_after>package main

import (
  "net/http"
  "os"
  "os/exec"
  "log"
  "fmt"
  "strings"
)

/*
 * POST /build/:host/:user/:name
 */
func JekyllBuild(rw http.ResponseWriter, r *http.Request) {
  // webhook must be POST
  if r.Method != "POST" {
    rw.WriteHeader(http.StatusNotFound)
    return
  }

  url := strings.Split(r.URL.Path, "/")
  host, user, name := url[2], url[3], url[4]
  tmp := "/tmp"

  dir  := tmp + "/src/" + name
  dest := tmp + "/build/" + name
  repo := "git@" + host + ":" + user + "/" + name + ".git"

  cmd := []string{
    "git clone %[2]s %[1]s &&",
    "cd %[1]s;",
    // "git checkout master",
    "[ -f Gemfile ] && bundle install;",
    "jekyll build -s %[1]s -d %[3]s;",
    "rm -Rf %[1]s;"}

  log.Println("-----> Cloning " + repo)
  log.Println("-----> Building Jekyll site ...")
  out, err := exec.Command("sh", "-c", fmt.Sprintf(strings.Join(cmd, " "), dir, repo, dest)).Output()
  log.Println(string(out))
  if err != nil {
    log.Println("ERROR: %s", err)
    rw.WriteHeader(http.StatusInternalServerError)
    return
  }
  log.Println("-----> Jekyll site built successfully.")

  status, message := JekyllPublish(dest)
  log.Println(message)

  rw.WriteHeader(status)
}

func JekyllPublish(dir string) (int, string) {

  // Find S3 Bucket location from the BUCKET file
  bucket, err := exec.Command("sh", "-c", fmt.Sprintf("cd %s && cat BUCKET", dir)).Output()
  if err != nil {
    return 500, "No bucket specified"
  }

  // Sync files to S3
  log.Println("-----> Publishing to Amazon S3 Bucket %s...\n", bucket)
  out, err := exec.Command(
    "s3cmd",
    "sync",
    dir + "/",
    "s3://" + string(bucket),
    "--delete-removed",
    "--acl-public",
    "--add-header=Cache-Control:max-age=60",
  ).CombinedOutput()
  log.Println("%s", out)
  if err != nil {
    log.Println("%s", err)
    return 500, "Problem syncing"
  }

  // TODO Optional - Use rsync to remote web server
  // rsync -avz --delete _site/ location:/var/www/html/jekyll

  // remove build
  os.RemoveAll(dir)

  return 200, "Success"
}

func main() {
  http.HandleFunc("/build/", JekyllBuild)
  port := os.Getenv("PORT")
  if port == "" {
      port = "8080"
  }
  http.ListenAndServe(":"+port, nil)
}

<|endoftext|>


<commit_before>// Package ocb2 implements the version 2 of the OCB authenticated-encryption algorithm.
// OCB2 is specified in http://www.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt.
//
// Note that this implementation is limited to block ciphers with a block size of 128 bits.
//
// It should also be noted that OCB's author, Phil Rogaway <rogaway@cs.ucdavis.edu>, holds
// several US patents on the algorithm.  This should be considered before using this code
// in your own projects.  See OCB's FAQ for more info:
// http://www.cs.ucdavis.edu/~rogaway/ocb/ocb-faq.htm#patent:phil
//
// The Mumble Project has a license to use OCB mode in its BSD licensed code on a royalty
// free basis.
package ocb2

import "crypto/cipher"

const (
	// BlockSize defines the block size that this particular implementation
	// of OCB2 is made to work on.
	BlockSize = 16
	// TagSize specifies the length in bytes of a full OCB2 tag.
	// As per the specification, applications may truncate their
	// tags to a given length, but advocates that typical applications
	// should use a tag length of at least 8 bytes (64 bits).
	TagSize = BlockSize
	// NonceSize specifies the length in bytes of an OCB2 nonce.
	NonceSize = BlockSize
)

// zeros fills block with zero bytes.
func zeros(block []byte) {
	for i := range block {
		block[i] = 0
	}
}

// xor outputs the bitwise exclusive-or of a and b to dst.
func xor(dst []byte, a []byte, b []byte) {
	for i := 0; i < BlockSize; i++ {
		dst[i] = a[i] ^ b[i]
	}
}

// times2 performs the times2 operation, defined as:
//
// times2(S)
//     S << 1 if S[1] = 0, and (S << 1) xor const(bitlength(S)) if S[1] = 1.
//
// where const(n) is defined as
//
// const(n)
//     The lexicographically first n-bit string C among all
//     strings that have a minimal possible number of "1"
//     bits and which name a polynomial x^n + C[1] *
//     x^{n-1} + ... + C[n-1] * x^1 + C[n] * x^0 that is
//     irreducible over the field with two elements.  In
//     particular, const(128) = num2str(135, 128).  For
//     other values of n, refer to a standard table of
//     irreducible polynomials [G. Seroussi,
//     "Table of low-weight binary irreducible polynomials",
//     HP Labs Technical Report HPL-98-135, 1998.].
//
// and num2str(x, n) is defined as
//
// num2str(x, n)
//     The n-bit binary representation of the integer x.
//     More formally, the n-bit string S where x = S[1] *
//     2^{n-1} + S[2] * 2^{n-2} + ... + S[n] * 2^{0}.  Only
//     used when 0 <= x < 2^n.
//
// For our 128-bit block size implementation, this means that
// the xor with const(bitlength(S)) if S[1] = 1 is implemented
// by simply xor'ing the last byte with the number 135 when
// S[1] = 1.
func times2(block []byte) {
	carry := (block[0] >> 7) & 0x1
	for i := 0; i < BlockSize-1; i++ {
		block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
	}
	block[BlockSize-1] = (block[BlockSize-1] << 1) ^ (carry * 135)
}

// times3 performs the times3 operation, defined as:
//
// times3(S)
//     times2(S) xor S
func times3(block []byte) {
	carry := (block[0] >> 7) & 0x1
	for i := 0; i < BlockSize-1; i++ {
		block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
	}
	block[BlockSize-1] ^= ((block[BlockSize-1] << 1) ^ (carry * 135))
}

// Encrypt encrypts the plaintext src and outputs the corresponding ciphertext into dst.
// Besides outputting a ciphertext into dst, Encrypt also outputs an authentication tag
// of ocb2.TagSize bytes into tag, which should be used to verify the authenticity of the
// message on the receiving side.
//
// To ensure both authenticity and secrecy of messages, each invocation to this function must
// be given an unique nonce of ocb2.NonceSize bytes.  The nonce need not be secret (it can be
// a counter), but it needs to be unique.
//
// The block cipher used in function must work on a block size equal to ocb2.BlockSize.
// The tag slice used in this function must have a length equal to ocb2.TagSize.
// The nonce slice used in this function must have a length equal to ocb2.NonceSize.
// If any of the above are violated, Encrypt will panic.
func Encrypt(cipher cipher.Block, dst []byte, src []byte, nonce []byte, tag []byte) {
	if cipher.BlockSize() != BlockSize {
		panic("ocb2: cipher blocksize is not equal to ocb2.BlockSize")
	}
	if len(nonce) != NonceSize {
		panic("ocb2: nonce length is not equal to ocb2.NonceSize")
	}
	if len(tag) != TagSize {
		panic("ocb2: tag length is not equal to ocb2.TagSize")
	}

	var delta [BlockSize]byte
	var checksum [BlockSize]byte
	var tmp [BlockSize]byte
	var pad [BlockSize]byte
	off := 0

	cipher.Encrypt(delta[0:], nonce[0:])
	zeros(checksum[0:])

	remain := len(src)
	for remain > BlockSize {
		times2(delta[0:])
		xor(tmp[0:], delta[0:], src[off:off+BlockSize])
		cipher.Encrypt(tmp[0:], tmp[0:])
		xor(dst[off:off+BlockSize], delta[0:], tmp[0:])
		xor(checksum[0:], checksum[0:], src[off:off+BlockSize])
		remain -= BlockSize
		off += BlockSize
	}

	times2(delta[0:])
	zeros(tmp[0:])
	num := remain * 8
	tmp[BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
	tmp[BlockSize-1] = uint8(num & 0xff)
	xor(tmp[0:], tmp[0:], delta[0:])
	cipher.Encrypt(pad[0:], tmp[0:])
	copied := copy(tmp[0:], src[off:])
	if copied != remain {
		panic("ocb2: copy failed")
	}
	if copy(tmp[copied:], pad[copied:]) != (BlockSize - remain) {
		panic("ocb2: copy failed")
	}
	xor(checksum[0:], checksum[0:], tmp[0:])
	xor(tmp[0:], pad[0:], tmp[0:])
	if copy(dst[off:], tmp[0:]) != remain {
		panic("ocb2: copy failed")
	}

	times3(delta[0:])
	xor(tmp[0:], delta[0:], checksum[0:])
	cipher.Encrypt(tag[0:], tmp[0:])
}

// Decrypt takes a ciphertext and a nonce as its input and outputs a decrypted plaintext
// and corresponding authentication tag.
//
// Before using the decrpyted plaintext, the application
// should verify that the computed authentication tag matches the tag that was produced when
// encrypting the message (taking into consideration that OCB tags are allowed to be truncated
// to a length less than ocb.TagSize).
//
// The block cipher used in function must work on a block size equal to ocb2.BlockSize.
// The tag slice used in this function must have a length equal to ocb2.TagSize.
// The nonce slice used in this function must have a length equal to ocb2.NonceSize.
// If any of the above are violated, Encrypt will panic.
func Decrypt(cipher cipher.Block, plain []byte, encrypted []byte, nonce []byte, tag []byte) {
	if cipher.BlockSize() != BlockSize {
		panic("ocb2: cipher blocksize is not equal to ocb2.BlockSize")
	}
	if len(nonce) != NonceSize {
		panic("ocb2: nonce length is not equal to ocb2.NonceSize")
	}
	if len(tag) != TagSize {
		panic("ocb2: tag length is not equal to ocb2.TagSize")
	}

	var checksum [BlockSize]byte
	var delta [BlockSize]byte
	var tmp [BlockSize]byte
	var pad [BlockSize]byte
	off := 0

	cipher.Encrypt(delta[0:], nonce[0:])
	zeros(checksum[0:])

	remain := len(encrypted)
	for remain > BlockSize {
		times2(delta[0:])
		xor(tmp[0:], delta[0:], encrypted[off:off+BlockSize])
		cipher.Decrypt(tmp[0:], tmp[0:])
		xor(plain[off:off+BlockSize], delta[0:], tmp[0:])
		xor(checksum[0:], checksum[0:], plain[off:off+BlockSize])
		off += BlockSize
		remain -= BlockSize
	}

	times2(delta[0:])
	zeros(tmp[0:])
	num := remain * 8
	tmp[BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
	tmp[BlockSize-1] = uint8(num & 0xff)
	xor(tmp[0:], tmp[0:], delta[0:])
	cipher.Encrypt(pad[0:], tmp[0:])
	zeros(tmp[0:])
	copied := copy(tmp[0:remain], encrypted[off:off+remain])
	if copied != remain {
		panic("ocb2: copy failed")
	}
	xor(tmp[0:], tmp[0:], pad[0:])
	xor(checksum[0:], checksum[0:], tmp[0:])
	copied = copy(plain[off:off+remain], tmp[0:remain])
	if copied != remain {
		panic("ocb2: copy failed")
	}

	times3(delta[0:])
	xor(tmp[0:], delta[0:], checksum[0:])
	cipher.Encrypt(tag[0:], tmp[0:])
}<commit_msg>pkg/cryptstate/ocb2: re-arrange var blocks inside Encrypt and Decrypt.<commit_after>// Package ocb2 implements the version 2 of the OCB authenticated-encryption algorithm.
// OCB2 is specified in http://www.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt.
//
// Note that this implementation is limited to block ciphers with a block size of 128 bits.
//
// It should also be noted that OCB's author, Phil Rogaway <rogaway@cs.ucdavis.edu>, holds
// several US patents on the algorithm.  This should be considered before using this code
// in your own projects.  See OCB's FAQ for more info:
// http://www.cs.ucdavis.edu/~rogaway/ocb/ocb-faq.htm#patent:phil
//
// The Mumble Project has a license to use OCB mode in its BSD licensed code on a royalty
// free basis.
package ocb2

import "crypto/cipher"

const (
	// BlockSize defines the block size that this particular implementation
	// of OCB2 is made to work on.
	BlockSize = 16
	// TagSize specifies the length in bytes of a full OCB2 tag.
	// As per the specification, applications may truncate their
	// tags to a given length, but advocates that typical applications
	// should use a tag length of at least 8 bytes (64 bits).
	TagSize = BlockSize
	// NonceSize specifies the length in bytes of an OCB2 nonce.
	NonceSize = BlockSize
)

// zeros fills block with zero bytes.
func zeros(block []byte) {
	for i := range block {
		block[i] = 0
	}
}

// xor outputs the bitwise exclusive-or of a and b to dst.
func xor(dst []byte, a []byte, b []byte) {
	for i := 0; i < BlockSize; i++ {
		dst[i] = a[i] ^ b[i]
	}
}

// times2 performs the times2 operation, defined as:
//
// times2(S)
//     S << 1 if S[1] = 0, and (S << 1) xor const(bitlength(S)) if S[1] = 1.
//
// where const(n) is defined as
//
// const(n)
//     The lexicographically first n-bit string C among all
//     strings that have a minimal possible number of "1"
//     bits and which name a polynomial x^n + C[1] *
//     x^{n-1} + ... + C[n-1] * x^1 + C[n] * x^0 that is
//     irreducible over the field with two elements.  In
//     particular, const(128) = num2str(135, 128).  For
//     other values of n, refer to a standard table of
//     irreducible polynomials [G. Seroussi,
//     "Table of low-weight binary irreducible polynomials",
//     HP Labs Technical Report HPL-98-135, 1998.].
//
// and num2str(x, n) is defined as
//
// num2str(x, n)
//     The n-bit binary representation of the integer x.
//     More formally, the n-bit string S where x = S[1] *
//     2^{n-1} + S[2] * 2^{n-2} + ... + S[n] * 2^{0}.  Only
//     used when 0 <= x < 2^n.
//
// For our 128-bit block size implementation, this means that
// the xor with const(bitlength(S)) if S[1] = 1 is implemented
// by simply xor'ing the last byte with the number 135 when
// S[1] = 1.
func times2(block []byte) {
	carry := (block[0] >> 7) & 0x1
	for i := 0; i < BlockSize-1; i++ {
		block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
	}
	block[BlockSize-1] = (block[BlockSize-1] << 1) ^ (carry * 135)
}

// times3 performs the times3 operation, defined as:
//
// times3(S)
//     times2(S) xor S
func times3(block []byte) {
	carry := (block[0] >> 7) & 0x1
	for i := 0; i < BlockSize-1; i++ {
		block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
	}
	block[BlockSize-1] ^= ((block[BlockSize-1] << 1) ^ (carry * 135))
}

// Encrypt encrypts the plaintext src and outputs the corresponding ciphertext into dst.
// Besides outputting a ciphertext into dst, Encrypt also outputs an authentication tag
// of ocb2.TagSize bytes into tag, which should be used to verify the authenticity of the
// message on the receiving side.
//
// To ensure both authenticity and secrecy of messages, each invocation to this function must
// be given an unique nonce of ocb2.NonceSize bytes.  The nonce need not be secret (it can be
// a counter), but it needs to be unique.
//
// The block cipher used in function must work on a block size equal to ocb2.BlockSize.
// The tag slice used in this function must have a length equal to ocb2.TagSize.
// The nonce slice used in this function must have a length equal to ocb2.NonceSize.
// If any of the above are violated, Encrypt will panic.
func Encrypt(cipher cipher.Block, dst []byte, src []byte, nonce []byte, tag []byte) {
	if cipher.BlockSize() != BlockSize {
		panic("ocb2: cipher blocksize is not equal to ocb2.BlockSize")
	}
	if len(nonce) != NonceSize {
		panic("ocb2: nonce length is not equal to ocb2.NonceSize")
	}
	if len(tag) != TagSize {
		panic("ocb2: tag length is not equal to ocb2.TagSize")
	}

	var (
		checksum [BlockSize]byte
		delta [BlockSize]byte
		tmp [BlockSize]byte
		pad [BlockSize]byte
		off int
	)

	cipher.Encrypt(delta[0:], nonce[0:])
	zeros(checksum[0:])

	remain := len(src)
	for remain > BlockSize {
		times2(delta[0:])
		xor(tmp[0:], delta[0:], src[off:off+BlockSize])
		cipher.Encrypt(tmp[0:], tmp[0:])
		xor(dst[off:off+BlockSize], delta[0:], tmp[0:])
		xor(checksum[0:], checksum[0:], src[off:off+BlockSize])
		remain -= BlockSize
		off += BlockSize
	}

	times2(delta[0:])
	zeros(tmp[0:])
	num := remain * 8
	tmp[BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
	tmp[BlockSize-1] = uint8(num & 0xff)
	xor(tmp[0:], tmp[0:], delta[0:])
	cipher.Encrypt(pad[0:], tmp[0:])
	copied := copy(tmp[0:], src[off:])
	if copied != remain {
		panic("ocb2: copy failed")
	}
	if copy(tmp[copied:], pad[copied:]) != (BlockSize - remain) {
		panic("ocb2: copy failed")
	}
	xor(checksum[0:], checksum[0:], tmp[0:])
	xor(tmp[0:], pad[0:], tmp[0:])
	if copy(dst[off:], tmp[0:]) != remain {
		panic("ocb2: copy failed")
	}

	times3(delta[0:])
	xor(tmp[0:], delta[0:], checksum[0:])
	cipher.Encrypt(tag[0:], tmp[0:])
}

// Decrypt takes a ciphertext and a nonce as its input and outputs a decrypted plaintext
// and corresponding authentication tag.
//
// Before using the decrpyted plaintext, the application
// should verify that the computed authentication tag matches the tag that was produced when
// encrypting the message (taking into consideration that OCB tags are allowed to be truncated
// to a length less than ocb.TagSize).
//
// The block cipher used in function must work on a block size equal to ocb2.BlockSize.
// The tag slice used in this function must have a length equal to ocb2.TagSize.
// The nonce slice used in this function must have a length equal to ocb2.NonceSize.
// If any of the above are violated, Encrypt will panic.
func Decrypt(cipher cipher.Block, plain []byte, encrypted []byte, nonce []byte, tag []byte) {
	if cipher.BlockSize() != BlockSize {
		panic("ocb2: cipher blocksize is not equal to ocb2.BlockSize")
	}
	if len(nonce) != NonceSize {
		panic("ocb2: nonce length is not equal to ocb2.NonceSize")
	}
	if len(tag) != TagSize {
		panic("ocb2: tag length is not equal to ocb2.TagSize")
	}

	var (
		checksum [BlockSize]byte
		delta [BlockSize]byte
		tmp [BlockSize]byte
		pad [BlockSize]byte
		off int
	)

	cipher.Encrypt(delta[0:], nonce[0:])
	zeros(checksum[0:])

	remain := len(encrypted)
	for remain > BlockSize {
		times2(delta[0:])
		xor(tmp[0:], delta[0:], encrypted[off:off+BlockSize])
		cipher.Decrypt(tmp[0:], tmp[0:])
		xor(plain[off:off+BlockSize], delta[0:], tmp[0:])
		xor(checksum[0:], checksum[0:], plain[off:off+BlockSize])
		off += BlockSize
		remain -= BlockSize
	}

	times2(delta[0:])
	zeros(tmp[0:])
	num := remain * 8
	tmp[BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
	tmp[BlockSize-1] = uint8(num & 0xff)
	xor(tmp[0:], tmp[0:], delta[0:])
	cipher.Encrypt(pad[0:], tmp[0:])
	zeros(tmp[0:])
	copied := copy(tmp[0:remain], encrypted[off:off+remain])
	if copied != remain {
		panic("ocb2: copy failed")
	}
	xor(tmp[0:], tmp[0:], pad[0:])
	xor(checksum[0:], checksum[0:], tmp[0:])
	copied = copy(plain[off:off+remain], tmp[0:remain])
	if copied != remain {
		panic("ocb2: copy failed")
	}

	times3(delta[0:])
	xor(tmp[0:], delta[0:], checksum[0:])
	cipher.Encrypt(tag[0:], tmp[0:])
}<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"github.com/GeoNet/cfg"
	"github.com/russross/blackfriday"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

var templates map[string]*template.Template
var config = cfg.Load()
var htmlFlags int
var htmlExt int

func init() {
	if templates == nil {
		templates = make(map[string]*template.Template)
	}
	templates["index"] = template.Must(template.ParseFiles("tmpl/base.tmpl", "tmpl/index.tmpl"))
	templates["index1"] = template.Must(template.ParseFiles("tmpl/base.tmpl", "tmpl/index1.tmpl"))
	templates["edit"] = template.Must(template.ParseFiles("tmpl/base.tmpl", "tmpl/edit.tmpl"))
	templates["preview"] = template.Must(template.ParseFiles("tmpl/preview.tmpl"))

	//handle css files
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
	http.Handle("/_images/", http.StripPrefix("/_images/", http.FileServer(http.Dir("_images"))))

	// set up html options
	htmlExt = 0
	htmlExt |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
	htmlExt |= blackfriday.EXTENSION_TABLES
	htmlExt |= blackfriday.EXTENSION_FENCED_CODE
	htmlExt |= blackfriday.EXTENSION_AUTOLINK
	htmlExt |= blackfriday.EXTENSION_STRIKETHROUGH
	htmlExt |= blackfriday.EXTENSION_SPACE_HEADERS

	htmlFlags = blackfriday.HTML_USE_XHTML |
		blackfriday.HTML_USE_SMARTYPANTS |
		blackfriday.HTML_SMARTYPANTS_FRACTIONS |
		blackfriday.HTML_SMARTYPANTS_LATEX_DASHES |
		blackfriday.HTML_FOOTNOTE_RETURN_LINKS |
		blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES

}

func main() {
	http.HandleFunc("/", indexPage)

	http.HandleFunc("/preview", indexPagePreview)

	http.HandleFunc("/edit", editPage)

	http.HandleFunc("/save", savePage)

	log.Fatal(http.ListenAndServe(":8080", nil))
}

func indexPagePreview(w http.ResponseWriter, r *http.Request) {
	pageData, err := getIndexPageData()
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotAcceptable)
	}

	//save the page to html
	saveHtmlContent("index", "index1", pageData)
	//copy css, images
	err = CopyDir("css/", "_html/css/")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("Directory copied")
	}
	err = CopyDir("_images/", "_html/_images/")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("Directory copied")
	}

	renderTemplate(w, "index1", pageData)
}

func indexPage(w http.ResponseWriter, r *http.Request) {
	pageData, err := getIndexPageData()
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotAcceptable)
	}

	renderTemplate(w, "index", pageData)
}

func getIndexPageData() (*PageData, error) {
	//get md files
	files, err := ioutil.ReadDir(md_src_dir)
	if err != nil {
		return nil, err
	}
	allMarkdown := make([]MarkdownData, 0)
	for _, f := range files {
		if strings.HasSuffix(f.Name(), ".md") {
			name := strings.Replace(f.Name(), ".md", "", 1)
			//log.Println("files filename: %s, name%s", f.Name(), name)
			allMarkdown = append(allMarkdown,
				MarkdownData{
					FileName: name,
					Title:    getFileTitle(name),
				})
		}
	}

	return &PageData{Title: "GeoNet News",
		AllMarkdown: allMarkdown}, nil
}

func editPage(w http.ResponseWriter, r *http.Request) {
	v := r.URL.Query()
	mdName := v.Get("mdname")
	mdData := MarkdownData{}
	pageData := PageData{Title: "GeoNet News"}
	if mdName != "" {
		mdData.FileName = mdName
		mdData.Title = getFileTitle(mdName)
		b, err := readMarkDown(md_src_dir + mdName + ".md")
		if err == nil {
			mdData.MdContent = string(b)
		}
		pageData.MarkDown = mdData
	}
	renderTemplate(w, "edit", pageData)
}

func savePage(w http.ResponseWriter, r *http.Request) {
	err := r.ParseMultipartForm(100000)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotAcceptable)
	}

	title := r.Form["postTitle"][0]

	log.Println("title ", title)

	content := r.Form["postContent"][0]
	//log.Println("content ", content);
	preview := r.Form["preview"][0]
	//log.Println("preview ", preview)
	if preview == "1" { //preview
		//log.Println("preview ", preview)
		previewPage(w, r, title, content)
	} else if preview == "2" { //load images
		//get a ref to the parsed multipart form
		m := r.MultipartForm
		//get the *fileheaders
		files := m.File["imagefiles"]
		var imgContents string
		for i, _ := range files {
			//for each fileheader, get a handle to the actual file
			file, err := files[i].Open()
			defer file.Close()
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			//create destination file making sure the path is writeable.
			//log.Println("images file  ", files[i].Filename)
			dst, err := os.Create(img_dir + files[i].Filename)
			defer dst.Close()
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			//copy the uploaded file to the destination file
			if _, err := io.Copy(dst, file); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			//make the images contents
			imgTitle := files[i].Filename
			last := strings.LastIndex(imgTitle, ".")

			if last > 0 && last < len(imgTitle) {
				imgTitle = imgTitle[0:last]
			}

			log.Println("imgTitle", imgTitle)
			imgContents += "![" + imgTitle + "](" + img_dir + files[i].Filename + ")\n"
		}
		//append uploaded images to end of md content
		content += imgContents
		mdData := MarkdownData{}
		pageData := PageData{Title: "GeoNet News"}
		mdData.Title = title
		mdData.MdContent = content
		pageData.MarkDown = mdData
		//show edit page again
		renderTemplate(w, "edit", pageData)

	} else { //submit
		//save md content
		saveMdContent(title, content)
		pageData := getHtmlPageData(title, content)
		//save html preview
		saveHtmlContent(title, "preview", pageData)
		indexPage(w, r)
	}
}

func previewPage(w http.ResponseWriter, r *http.Request, title string, content string) {
	pageData := getHtmlPageData(title, content)
	//save html preview
	saveHtmlContent(title, "preview", pageData)
	//render
	renderTemplate(w, "edit", pageData)
}

func getHtmlPageData(title string, md string) *PageData {
	mdData := MarkdownData{}
	pageData := PageData{Title: "GeoNet News"}
	mdData.Title = title
	mdData.MdContent = md

	htmlRenderer := blackfriday.HtmlRenderer(htmlFlags, "", "")
	mdData.HtmlContent = template.HTML(blackfriday.Markdown([]byte(md), htmlRenderer, htmlExt))
	pageData.MarkDown = mdData

	return &pageData
}

//render html page
func renderTemplate(w http.ResponseWriter, name string, data interface{}) {
	tmpl, ok := templates[name]
	if !ok {
		http.Error(http.ResponseWriter(w), fmt.Sprintf("The template %s does not exist.", name), http.StatusInternalServerError)
	}
	err := tmpl.ExecuteTemplate(w, "base", &data)
	if err != nil {
		http.Error(http.ResponseWriter(w), err.Error(), http.StatusInternalServerError)
	}
}

//save html content to local directory
func saveHtmlContent(title string, tmplName string, data interface{}) {
	fleName := getFileNameForTitle(title)
	f, err := os.Create(md_html_dir + fleName + ".html")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	w := bufio.NewWriter(f)
	tmpl, ok := templates[tmplName]
	if !ok {
		panic("The template preview does not exist.")
	}
	err = tmpl.ExecuteTemplate(w, "base", &data)
	if err != nil {
		panic(err)
	}
	w.Flush()
}

func CopyDir(source string, dest string) (err error) {

	// get properties of source dir
	sourceinfo, err := os.Stat(source)
	if err != nil {
		return err
	}

	// create dest dir
	err = os.MkdirAll(dest, sourceinfo.Mode())
	if err != nil {
		return err
	}

	directory, _ := os.Open(source)
	objects, err := directory.Readdir(-1)

	for _, obj := range objects {
		sourcefilepointer := source + "/" + obj.Name()
		destinationfilepointer := dest + "/" + obj.Name()

		// perform copy
		err = CopyFile(sourcefilepointer, destinationfilepointer)
		if err != nil {
			fmt.Println(err)
		}
	}
	return
}

func CopyFile(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

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

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}
	return
}

type MarkdownData struct {
	Title       string
	FileName    string
	MdContent   string
	HtmlContent template.HTML
}

type PageData struct {
	Title       string
	AllMarkdown []MarkdownData
	MarkDown    MarkdownData
}
<commit_msg>change port to os env to be able to run on heroku<commit_after>package main

import (
	"bufio"
	"fmt"
	"github.com/GeoNet/cfg"
	"github.com/russross/blackfriday"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
)

var templates map[string]*template.Template
var config = cfg.Load()
var htmlFlags int
var htmlExt int

func init() {
	if templates == nil {
		templates = make(map[string]*template.Template)
	}
	templates["index"] = template.Must(template.ParseFiles("tmpl/base.tmpl", "tmpl/index.tmpl"))
	templates["index1"] = template.Must(template.ParseFiles("tmpl/base.tmpl", "tmpl/index1.tmpl"))
	templates["edit"] = template.Must(template.ParseFiles("tmpl/base.tmpl", "tmpl/edit.tmpl"))
	templates["preview"] = template.Must(template.ParseFiles("tmpl/preview.tmpl"))

	//handle css files
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css"))))
	http.Handle("/_images/", http.StripPrefix("/_images/", http.FileServer(http.Dir("_images"))))

	// set up html options
	htmlExt = 0
	htmlExt |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
	htmlExt |= blackfriday.EXTENSION_TABLES
	htmlExt |= blackfriday.EXTENSION_FENCED_CODE
	htmlExt |= blackfriday.EXTENSION_AUTOLINK
	htmlExt |= blackfriday.EXTENSION_STRIKETHROUGH
	htmlExt |= blackfriday.EXTENSION_SPACE_HEADERS

	htmlFlags = blackfriday.HTML_USE_XHTML |
		blackfriday.HTML_USE_SMARTYPANTS |
		blackfriday.HTML_SMARTYPANTS_FRACTIONS |
		blackfriday.HTML_SMARTYPANTS_LATEX_DASHES |
		blackfriday.HTML_FOOTNOTE_RETURN_LINKS |
		blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES

}

func main() {
	http.HandleFunc("/", indexPage)

	http.HandleFunc("/preview", indexPagePreview)

	http.HandleFunc("/edit", editPage)

	http.HandleFunc("/save", savePage)

    port := os.Getenv("PORT")
    log.Println("port", port)

    if port == "" {
        log.Fatal("$PORT must be set")
    }

	log.Fatal(http.ListenAndServe(":" + port, nil))
}

func indexPagePreview(w http.ResponseWriter, r *http.Request) {
	pageData, err := getIndexPageData()
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotAcceptable)
	}

	//save the page to html
	saveHtmlContent("index", "index1", pageData)
	//copy css, images
	err = CopyDir("css/", "_html/css/")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("Directory copied")
	}
	err = CopyDir("_images/", "_html/_images/")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println("Directory copied")
	}

	renderTemplate(w, "index1", pageData)
}

func indexPage(w http.ResponseWriter, r *http.Request) {
	pageData, err := getIndexPageData()
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotAcceptable)
	}

	renderTemplate(w, "index", pageData)
}

func getIndexPageData() (*PageData, error) {
	//get md files
	files, err := ioutil.ReadDir(md_src_dir)
	if err != nil {
		return nil, err
	}
	allMarkdown := make([]MarkdownData, 0)
	for _, f := range files {
		if strings.HasSuffix(f.Name(), ".md") {
			name := strings.Replace(f.Name(), ".md", "", 1)
			//log.Println("files filename: %s, name%s", f.Name(), name)
			allMarkdown = append(allMarkdown,
				MarkdownData{
					FileName: name,
					Title:    getFileTitle(name),
				})
		}
	}

	return &PageData{Title: "GeoNet News",
		AllMarkdown: allMarkdown}, nil
}

func editPage(w http.ResponseWriter, r *http.Request) {
	v := r.URL.Query()
	mdName := v.Get("mdname")
	mdData := MarkdownData{}
	pageData := PageData{Title: "GeoNet News"}
	if mdName != "" {
		mdData.FileName = mdName
		mdData.Title = getFileTitle(mdName)
		b, err := readMarkDown(md_src_dir + mdName + ".md")
		if err == nil {
			mdData.MdContent = string(b)
		}
		pageData.MarkDown = mdData
	}
	renderTemplate(w, "edit", pageData)
}

func savePage(w http.ResponseWriter, r *http.Request) {
	err := r.ParseMultipartForm(100000)
	if err != nil {
		http.Error(w, err.Error(), http.StatusNotAcceptable)
	}

	title := r.Form["postTitle"][0]

	log.Println("title ", title)

	content := r.Form["postContent"][0]
	//log.Println("content ", content);
	preview := r.Form["preview"][0]
	//log.Println("preview ", preview)
	if preview == "1" { //preview
		//log.Println("preview ", preview)
		previewPage(w, r, title, content)
	} else if preview == "2" { //load images
		//get a ref to the parsed multipart form
		m := r.MultipartForm
		//get the *fileheaders
		files := m.File["imagefiles"]
		var imgContents string
		for i, _ := range files {
			//for each fileheader, get a handle to the actual file
			file, err := files[i].Open()
			defer file.Close()
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			//create destination file making sure the path is writeable.
			//log.Println("images file  ", files[i].Filename)
			dst, err := os.Create(img_dir + files[i].Filename)
			defer dst.Close()
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			//copy the uploaded file to the destination file
			if _, err := io.Copy(dst, file); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			//make the images contents
			imgTitle := files[i].Filename
			last := strings.LastIndex(imgTitle, ".")

			if last > 0 && last < len(imgTitle) {
				imgTitle = imgTitle[0:last]
			}

			log.Println("imgTitle", imgTitle)
			imgContents += "![" + imgTitle + "](" + img_dir + files[i].Filename + ")\n"
		}
		//append uploaded images to end of md content
		content += imgContents
		mdData := MarkdownData{}
		pageData := PageData{Title: "GeoNet News"}
		mdData.Title = title
		mdData.MdContent = content
		pageData.MarkDown = mdData
		//show edit page again
		renderTemplate(w, "edit", pageData)

	} else { //submit
		//save md content
		saveMdContent(title, content)
		pageData := getHtmlPageData(title, content)
		//save html preview
		saveHtmlContent(title, "preview", pageData)
		indexPage(w, r)
	}
}

func previewPage(w http.ResponseWriter, r *http.Request, title string, content string) {
	pageData := getHtmlPageData(title, content)
	//save html preview
	saveHtmlContent(title, "preview", pageData)
	//render
	renderTemplate(w, "edit", pageData)
}

func getHtmlPageData(title string, md string) *PageData {
	mdData := MarkdownData{}
	pageData := PageData{Title: "GeoNet News"}
	mdData.Title = title
	mdData.MdContent = md

	htmlRenderer := blackfriday.HtmlRenderer(htmlFlags, "", "")
	mdData.HtmlContent = template.HTML(blackfriday.Markdown([]byte(md), htmlRenderer, htmlExt))
	pageData.MarkDown = mdData

	return &pageData
}

//render html page
func renderTemplate(w http.ResponseWriter, name string, data interface{}) {
	tmpl, ok := templates[name]
	if !ok {
		http.Error(http.ResponseWriter(w), fmt.Sprintf("The template %s does not exist.", name), http.StatusInternalServerError)
	}
	err := tmpl.ExecuteTemplate(w, "base", &data)
	if err != nil {
		http.Error(http.ResponseWriter(w), err.Error(), http.StatusInternalServerError)
	}
}

//save html content to local directory
func saveHtmlContent(title string, tmplName string, data interface{}) {
	fleName := getFileNameForTitle(title)
	f, err := os.Create(md_html_dir + fleName + ".html")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	w := bufio.NewWriter(f)
	tmpl, ok := templates[tmplName]
	if !ok {
		panic("The template preview does not exist.")
	}
	err = tmpl.ExecuteTemplate(w, "base", &data)
	if err != nil {
		panic(err)
	}
	w.Flush()
}

func CopyDir(source string, dest string) (err error) {

	// get properties of source dir
	sourceinfo, err := os.Stat(source)
	if err != nil {
		return err
	}

	// create dest dir
	err = os.MkdirAll(dest, sourceinfo.Mode())
	if err != nil {
		return err
	}

	directory, _ := os.Open(source)
	objects, err := directory.Readdir(-1)

	for _, obj := range objects {
		sourcefilepointer := source + "/" + obj.Name()
		destinationfilepointer := dest + "/" + obj.Name()

		// perform copy
		err = CopyFile(sourcefilepointer, destinationfilepointer)
		if err != nil {
			fmt.Println(err)
		}
	}
	return
}

func CopyFile(source string, dest string) (err error) {
	sourcefile, err := os.Open(source)
	if err != nil {
		return err
	}

	defer sourcefile.Close()

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

	defer destfile.Close()

	_, err = io.Copy(destfile, sourcefile)
	if err == nil {
		sourceinfo, err := os.Stat(source)
		if err != nil {
			err = os.Chmod(dest, sourceinfo.Mode())
		}

	}
	return
}

type MarkdownData struct {
	Title       string
	FileName    string
	MdContent   string
	HtmlContent template.HTML
}

type PageData struct {
	Title       string
	AllMarkdown []MarkdownData
	MarkDown    MarkdownData
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"github.com/hanwen/go-fuse/fuse"

	"github.com/rfjakob/gocryptfs/internal/configfile"
	"github.com/rfjakob/gocryptfs/internal/contentenc"
	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/readpassword"
	"github.com/rfjakob/gocryptfs/internal/speed"
	"github.com/rfjakob/gocryptfs/internal/stupidgcm"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// GitVersion is the gocryptfs version according to git, set by build.bash
var GitVersion = "[GitVersion not set - please compile using ./build.bash]"

// GitVersionFuse is the go-fuse library version, set by build.bash
var GitVersionFuse = "[GitVersionFuse not set - please compile using ./build.bash]"

// BuildDate is a date string like "2017-09-06", set by build.bash
var BuildDate = "0000-00-00"

// raceDetector is set to true by race.go if we are compiled with "go build -race"
var raceDetector bool

// loadConfig loads the config file "args.config", prompting the user for the password
func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.ConfFile, err error) {
	// Check if the file can be opened at all before prompting for a password
	fd, err := os.Open(args.config)
	if err != nil {
		tlog.Fatal.Printf("Cannot open config file: %v", err)
		return nil, nil, exitcodes.NewErr(err.Error(), exitcodes.OpenConf)
	}
	fd.Close()
	// The user has passed the master key (probably because he forgot the
	// password).
	if args.masterkey != "" {
		masterkey = parseMasterKey(args.masterkey, false)
		_, confFile, err = configfile.LoadConfFile(args.config, nil)
	} else {
		pw := readpassword.Once(args.extpass, "")
		tlog.Info.Println("Decrypting master key")
		masterkey, confFile, err = configfile.LoadConfFile(args.config, pw)
		for i := range pw {
			pw[i] = 0
		}
	}
	if err != nil {
		tlog.Fatal.Println(err)
		return nil, nil, err
	}
	return masterkey, confFile, nil
}

// changePassword - change the password of config file "filename"
// Does not return (calls os.Exit both on success and on error).
func changePassword(args *argContainer) {
	var confFile *configfile.ConfFile
	var err error
	{
		var masterkey []byte
		masterkey, confFile, err = loadConfig(args)
		if err != nil {
			exitcodes.Exit(err)
		}
		tlog.Info.Println("Please enter your new password.")
		newPw := readpassword.Twice(args.extpass)
		readpassword.CheckTrailingGarbage()
		confFile.EncryptKey(masterkey, newPw, confFile.ScryptObject.LogN())
		for i := range newPw {
			newPw[i] = 0
		}
		for i := range masterkey {
			masterkey[i] = 0
		}
		// masterkey and newPw run out of scope here
	}
	// Are we resetting the password without knowing the old one using
	// "-masterkey"?
	if args.masterkey != "" {
		bak := args.config + ".bak"
		err = os.Link(args.config, bak)
		if err != nil {
			tlog.Fatal.Printf("Could not create backup file: %v", err)
			os.Exit(exitcodes.Init)
		}
		tlog.Info.Printf(tlog.ColorGrey+
			"A copy of the old config file has been created at %q.\n"+
			"Delete it after you have verified that you can access your files with the new password."+
			tlog.ColorReset, bak)
	}
	err = confFile.WriteFile()
	if err != nil {
		tlog.Fatal.Println(err)
		os.Exit(exitcodes.WriteConf)
	}
	tlog.Info.Printf(tlog.ColorGreen + "Password changed." + tlog.ColorReset)
}

// printVersion prints a version string like this:
// gocryptfs v0.12-36-ge021b9d-dirty; go-fuse a4c968c; 2016-07-03 go1.6.2
func printVersion() {
	buildFlags := ""
	if stupidgcm.BuiltWithoutOpenssl {
		buildFlags = " without_openssl"
	}
	built := fmt.Sprintf("%s %s", BuildDate, runtime.Version())
	if raceDetector {
		built += " -race"
	}
	fmt.Printf("%s %s%s; go-fuse %s; %s\n",
		tlog.ProgramName, GitVersion, buildFlags, GitVersionFuse, built)
}

func main() {
	mxp := runtime.GOMAXPROCS(0)
	if mxp < 4 {
		// On a 2-core machine, setting maxprocs to 4 gives 10% better performance
		runtime.GOMAXPROCS(4)
	}
	var err error
	// Parse all command-line options (i.e. arguments starting with "-")
	// into "args". Path arguments are parsed below.
	args := parseCliOpts()
	// Fork a child into the background if "-fg" is not set AND we are mounting
	// a filesystem. The child will do all the work.
	if !args.fg && flagSet.NArg() == 2 {
		ret := forkChild()
		os.Exit(ret)
	}
	if args.debug {
		tlog.Debug.Enabled = true
	}
	// "-v"
	if args.version {
		tlog.Debug.Printf("openssl=%v\n", args.openssl)
		tlog.Debug.Printf("on-disk format %d\n", contentenc.CurrentVersion)
		printVersion()
		os.Exit(0)
	}
	// "-hh"
	if args.hh {
		helpLong()
		os.Exit(0)
	}
	// "-speed"
	if args.speed {
		speed.Run()
		os.Exit(0)
	}
	if args.wpanic {
		tlog.Warn.Wpanic = true
		tlog.Debug.Printf("Panicking on warnings")
	}
	// Every operation below requires CIPHERDIR. Exit if we don't have it.
	if flagSet.NArg() == 0 {
		if flagSet.NFlag() == 0 {
			// Naked call to "gocryptfs". Just print the help text.
			helpShort()
		} else {
			// The user has passed some flags, but CIPHERDIR is missing. State
			// what is wrong.
			tlog.Fatal.Printf("CIPHERDIR argument is missing")
		}
		os.Exit(exitcodes.Usage)
	}
	// Check that CIPHERDIR exists
	args.cipherdir, _ = filepath.Abs(flagSet.Arg(0))
	err = isDir(args.cipherdir)
	if err != nil {
		tlog.Fatal.Printf("Invalid cipherdir: %v", err)
		os.Exit(exitcodes.CipherDir)
	}
	// "-q"
	if args.quiet {
		tlog.Info.Enabled = false
	}
	// "-reverse" implies "-aessiv"
	if args.reverse {
		args.aessiv = true
	}
	// "-config"
	if args.config != "" {
		args.config, err = filepath.Abs(args.config)
		if err != nil {
			tlog.Fatal.Printf("Invalid \"-config\" setting: %v", err)
			os.Exit(exitcodes.Init)
		}
		tlog.Info.Printf("Using config file at custom location %s", args.config)
		args._configCustom = true
	} else if args.reverse {
		args.config = filepath.Join(args.cipherdir, configfile.ConfReverseName)
	} else {
		args.config = filepath.Join(args.cipherdir, configfile.ConfDefaultName)
	}
	// "-force_owner"
	if args.force_owner != "" {
		var uidNum, gidNum int64
		ownerPieces := strings.SplitN(args.force_owner, ":", 2)
		if len(ownerPieces) != 2 {
			tlog.Fatal.Printf("force_owner must be in form UID:GID")
			os.Exit(exitcodes.Usage)
		}
		uidNum, err = strconv.ParseInt(ownerPieces[0], 0, 32)
		if err != nil || uidNum < 0 {
			tlog.Fatal.Printf("force_owner: Unable to parse UID %v as positive integer", ownerPieces[0])
			os.Exit(exitcodes.Usage)
		}
		gidNum, err = strconv.ParseInt(ownerPieces[1], 0, 32)
		if err != nil || gidNum < 0 {
			tlog.Fatal.Printf("force_owner: Unable to parse GID %v as positive integer", ownerPieces[1])
			os.Exit(exitcodes.Usage)
		}
		args._forceOwner = &fuse.Owner{Uid: uint32(uidNum), Gid: uint32(gidNum)}
	}
	// "-cpuprofile"
	if args.cpuprofile != "" {
		onExitFunc := setupCpuprofile(args.cpuprofile)
		defer onExitFunc()
	}
	// "-memprofile"
	if args.memprofile != "" {
		onExitFunc := setupMemprofile(args.memprofile)
		defer onExitFunc()
	}
	// "-trace"
	if args.trace != "" {
		onExitFunc := setupTrace(args.trace)
		defer onExitFunc()
	}
	if args.cpuprofile != "" || args.memprofile != "" || args.trace != "" {
		tlog.Info.Printf("Note: You must unmount gracefully, otherwise the profile file(s) will stay empty!\n")
	}
	// "-openssl"
	if !args.openssl {
		tlog.Debug.Printf("OpenSSL disabled, using Go GCM")
	} else {
		tlog.Debug.Printf("OpenSSL enabled")
	}
	// Operation flags
	nOps := countOpFlags(&args)
	if nOps == 0 {
		// Default operation: mount.
		if flagSet.NArg() != 2 {
			prettyArgs := prettyArgs()
			tlog.Info.Printf("Wrong number of arguments (have %d, want 2). You passed: %s",
				flagSet.NArg(), prettyArgs)
			tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT [-o COMMA-SEPARATED-OPTIONS]", tlog.ProgramName)
			os.Exit(exitcodes.Usage)
		}
		doMount(&args)
		// Don't call os.Exit to give deferred functions a chance to run
		return
	}
	if nOps > 1 {
		tlog.Fatal.Printf("At most one of -info, -init, -passwd, -fsck is allowed")
		os.Exit(exitcodes.Usage)
	}
	if flagSet.NArg() != 1 {
		tlog.Fatal.Printf("The options -info, -init, -passwd, -fsck take exactly one argument, %d given",
			flagSet.NArg())
		os.Exit(exitcodes.Usage)
	}
	// "-info"
	if args.info {
		info(args.config)
		os.Exit(0)
	}
	// "-init"
	if args.init {
		initDir(&args)
		os.Exit(0)
	}
	// "-passwd"
	if args.passwd {
		changePassword(&args)
		os.Exit(0)
	}
	// "-fsck"
	if args.fsck {
		fsck(&args)
		os.Exit(0)
	}
}
<commit_msg>Set a default PATH if PATH is empty or unset<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"

	"github.com/hanwen/go-fuse/fuse"

	"github.com/rfjakob/gocryptfs/internal/configfile"
	"github.com/rfjakob/gocryptfs/internal/contentenc"
	"github.com/rfjakob/gocryptfs/internal/exitcodes"
	"github.com/rfjakob/gocryptfs/internal/readpassword"
	"github.com/rfjakob/gocryptfs/internal/speed"
	"github.com/rfjakob/gocryptfs/internal/stupidgcm"
	"github.com/rfjakob/gocryptfs/internal/tlog"
)

// GitVersion is the gocryptfs version according to git, set by build.bash
var GitVersion = "[GitVersion not set - please compile using ./build.bash]"

// GitVersionFuse is the go-fuse library version, set by build.bash
var GitVersionFuse = "[GitVersionFuse not set - please compile using ./build.bash]"

// BuildDate is a date string like "2017-09-06", set by build.bash
var BuildDate = "0000-00-00"

// raceDetector is set to true by race.go if we are compiled with "go build -race"
var raceDetector bool

// loadConfig loads the config file "args.config", prompting the user for the password
func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.ConfFile, err error) {
	// Check if the file can be opened at all before prompting for a password
	fd, err := os.Open(args.config)
	if err != nil {
		tlog.Fatal.Printf("Cannot open config file: %v", err)
		return nil, nil, exitcodes.NewErr(err.Error(), exitcodes.OpenConf)
	}
	fd.Close()
	// The user has passed the master key (probably because he forgot the
	// password).
	if args.masterkey != "" {
		masterkey = parseMasterKey(args.masterkey, false)
		_, confFile, err = configfile.LoadConfFile(args.config, nil)
	} else {
		pw := readpassword.Once(args.extpass, "")
		tlog.Info.Println("Decrypting master key")
		masterkey, confFile, err = configfile.LoadConfFile(args.config, pw)
		for i := range pw {
			pw[i] = 0
		}
	}
	if err != nil {
		tlog.Fatal.Println(err)
		return nil, nil, err
	}
	return masterkey, confFile, nil
}

// changePassword - change the password of config file "filename"
// Does not return (calls os.Exit both on success and on error).
func changePassword(args *argContainer) {
	var confFile *configfile.ConfFile
	var err error
	{
		var masterkey []byte
		masterkey, confFile, err = loadConfig(args)
		if err != nil {
			exitcodes.Exit(err)
		}
		tlog.Info.Println("Please enter your new password.")
		newPw := readpassword.Twice(args.extpass)
		readpassword.CheckTrailingGarbage()
		confFile.EncryptKey(masterkey, newPw, confFile.ScryptObject.LogN())
		for i := range newPw {
			newPw[i] = 0
		}
		for i := range masterkey {
			masterkey[i] = 0
		}
		// masterkey and newPw run out of scope here
	}
	// Are we resetting the password without knowing the old one using
	// "-masterkey"?
	if args.masterkey != "" {
		bak := args.config + ".bak"
		err = os.Link(args.config, bak)
		if err != nil {
			tlog.Fatal.Printf("Could not create backup file: %v", err)
			os.Exit(exitcodes.Init)
		}
		tlog.Info.Printf(tlog.ColorGrey+
			"A copy of the old config file has been created at %q.\n"+
			"Delete it after you have verified that you can access your files with the new password."+
			tlog.ColorReset, bak)
	}
	err = confFile.WriteFile()
	if err != nil {
		tlog.Fatal.Println(err)
		os.Exit(exitcodes.WriteConf)
	}
	tlog.Info.Printf(tlog.ColorGreen + "Password changed." + tlog.ColorReset)
}

// printVersion prints a version string like this:
// gocryptfs v0.12-36-ge021b9d-dirty; go-fuse a4c968c; 2016-07-03 go1.6.2
func printVersion() {
	buildFlags := ""
	if stupidgcm.BuiltWithoutOpenssl {
		buildFlags = " without_openssl"
	}
	built := fmt.Sprintf("%s %s", BuildDate, runtime.Version())
	if raceDetector {
		built += " -race"
	}
	fmt.Printf("%s %s%s; go-fuse %s; %s\n",
		tlog.ProgramName, GitVersion, buildFlags, GitVersionFuse, built)
}

func main() {
	mxp := runtime.GOMAXPROCS(0)
	if mxp < 4 {
		// On a 2-core machine, setting maxprocs to 4 gives 10% better performance
		runtime.GOMAXPROCS(4)
	}
	// mount(1) unsets PATH. Since exec.Command does not handle this case, we set
	// PATH to a default value if it's empty or unset.
	if os.Getenv("PATH") == "" {
		os.Setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin")
	}
	var err error
	// Parse all command-line options (i.e. arguments starting with "-")
	// into "args". Path arguments are parsed below.
	args := parseCliOpts()
	// Fork a child into the background if "-fg" is not set AND we are mounting
	// a filesystem. The child will do all the work.
	if !args.fg && flagSet.NArg() == 2 {
		ret := forkChild()
		os.Exit(ret)
	}
	if args.debug {
		tlog.Debug.Enabled = true
	}
	// "-v"
	if args.version {
		tlog.Debug.Printf("openssl=%v\n", args.openssl)
		tlog.Debug.Printf("on-disk format %d\n", contentenc.CurrentVersion)
		printVersion()
		os.Exit(0)
	}
	// "-hh"
	if args.hh {
		helpLong()
		os.Exit(0)
	}
	// "-speed"
	if args.speed {
		speed.Run()
		os.Exit(0)
	}
	if args.wpanic {
		tlog.Warn.Wpanic = true
		tlog.Debug.Printf("Panicking on warnings")
	}
	// Every operation below requires CIPHERDIR. Exit if we don't have it.
	if flagSet.NArg() == 0 {
		if flagSet.NFlag() == 0 {
			// Naked call to "gocryptfs". Just print the help text.
			helpShort()
		} else {
			// The user has passed some flags, but CIPHERDIR is missing. State
			// what is wrong.
			tlog.Fatal.Printf("CIPHERDIR argument is missing")
		}
		os.Exit(exitcodes.Usage)
	}
	// Check that CIPHERDIR exists
	args.cipherdir, _ = filepath.Abs(flagSet.Arg(0))
	err = isDir(args.cipherdir)
	if err != nil {
		tlog.Fatal.Printf("Invalid cipherdir: %v", err)
		os.Exit(exitcodes.CipherDir)
	}
	// "-q"
	if args.quiet {
		tlog.Info.Enabled = false
	}
	// "-reverse" implies "-aessiv"
	if args.reverse {
		args.aessiv = true
	}
	// "-config"
	if args.config != "" {
		args.config, err = filepath.Abs(args.config)
		if err != nil {
			tlog.Fatal.Printf("Invalid \"-config\" setting: %v", err)
			os.Exit(exitcodes.Init)
		}
		tlog.Info.Printf("Using config file at custom location %s", args.config)
		args._configCustom = true
	} else if args.reverse {
		args.config = filepath.Join(args.cipherdir, configfile.ConfReverseName)
	} else {
		args.config = filepath.Join(args.cipherdir, configfile.ConfDefaultName)
	}
	// "-force_owner"
	if args.force_owner != "" {
		var uidNum, gidNum int64
		ownerPieces := strings.SplitN(args.force_owner, ":", 2)
		if len(ownerPieces) != 2 {
			tlog.Fatal.Printf("force_owner must be in form UID:GID")
			os.Exit(exitcodes.Usage)
		}
		uidNum, err = strconv.ParseInt(ownerPieces[0], 0, 32)
		if err != nil || uidNum < 0 {
			tlog.Fatal.Printf("force_owner: Unable to parse UID %v as positive integer", ownerPieces[0])
			os.Exit(exitcodes.Usage)
		}
		gidNum, err = strconv.ParseInt(ownerPieces[1], 0, 32)
		if err != nil || gidNum < 0 {
			tlog.Fatal.Printf("force_owner: Unable to parse GID %v as positive integer", ownerPieces[1])
			os.Exit(exitcodes.Usage)
		}
		args._forceOwner = &fuse.Owner{Uid: uint32(uidNum), Gid: uint32(gidNum)}
	}
	// "-cpuprofile"
	if args.cpuprofile != "" {
		onExitFunc := setupCpuprofile(args.cpuprofile)
		defer onExitFunc()
	}
	// "-memprofile"
	if args.memprofile != "" {
		onExitFunc := setupMemprofile(args.memprofile)
		defer onExitFunc()
	}
	// "-trace"
	if args.trace != "" {
		onExitFunc := setupTrace(args.trace)
		defer onExitFunc()
	}
	if args.cpuprofile != "" || args.memprofile != "" || args.trace != "" {
		tlog.Info.Printf("Note: You must unmount gracefully, otherwise the profile file(s) will stay empty!\n")
	}
	// "-openssl"
	if !args.openssl {
		tlog.Debug.Printf("OpenSSL disabled, using Go GCM")
	} else {
		tlog.Debug.Printf("OpenSSL enabled")
	}
	// Operation flags
	nOps := countOpFlags(&args)
	if nOps == 0 {
		// Default operation: mount.
		if flagSet.NArg() != 2 {
			prettyArgs := prettyArgs()
			tlog.Info.Printf("Wrong number of arguments (have %d, want 2). You passed: %s",
				flagSet.NArg(), prettyArgs)
			tlog.Fatal.Printf("Usage: %s [OPTIONS] CIPHERDIR MOUNTPOINT [-o COMMA-SEPARATED-OPTIONS]", tlog.ProgramName)
			os.Exit(exitcodes.Usage)
		}
		doMount(&args)
		// Don't call os.Exit to give deferred functions a chance to run
		return
	}
	if nOps > 1 {
		tlog.Fatal.Printf("At most one of -info, -init, -passwd, -fsck is allowed")
		os.Exit(exitcodes.Usage)
	}
	if flagSet.NArg() != 1 {
		tlog.Fatal.Printf("The options -info, -init, -passwd, -fsck take exactly one argument, %d given",
			flagSet.NArg())
		os.Exit(exitcodes.Usage)
	}
	// "-info"
	if args.info {
		info(args.config)
		os.Exit(0)
	}
	// "-init"
	if args.init {
		initDir(&args)
		os.Exit(0)
	}
	// "-passwd"
	if args.passwd {
		changePassword(&args)
		os.Exit(0)
	}
	// "-fsck"
	if args.fsck {
		fsck(&args)
		os.Exit(0)
	}
}
<|endoftext|>