<commit_before>// Copyright (c) 2014, Google, Inc.  All rights reserved.
//
// 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.
//
// File: simpleserver.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"log"
	"net"

	"github.com/jlmucb/cloudproxy/go/apps/simpleexample/common"
	taosupport "github.com/jlmucb/cloudproxy/go/support_libraries/tao_support"
	"github.com/jlmucb/cloudproxy/go/tao"
	"github.com/jlmucb/cloudproxy/go/util"
)

var caAddr = flag.String("caAddr", "localhost:8124", "The address to listen on")
var simpleCfg = flag.String("domain_config",
	"./tao.config",
	"path to simple tao configuration")
var simpleServerPath = flag.String("path",
	"./SimpleServer",
	"path to Server files")
var serverHost = flag.String("host", "localhost", "address for client/server")
var serverPort = flag.String("port", "8123", "port for client/server")
var useSimpleDomainService = flag.Bool("use_simpledomainservice", true,
	"whether to use simple domain service")
var serverAddr string

// Handle service request, req and return response over channel (ms).
// This handles the one valid service request: "SecretRequest"
// and terminates the channel after the first successful request
// which is not generally what would happen in most channels.
// Note that in the future, we might want to use grpc rather than custom
// service request/response buffers but we don't want to introduce complexity
// into this example.  The single request response buffer is defined in
// taosupport/taosupport.proto.
func HandleServiceRequest(ms *util.MessageStream, serverProgramData *taosupport.TaoProgramData,
	clientProgramName string, req *simpleexample_messages.SimpleMessage) (bool, error) {

	//  The somewhat boring secret is the corresponding simpleclient's program name || 43
	secret := clientProgramName + "43"

	if *req.RequestType == "SecretRequest" {
		req.Data = append(req.Data, []byte(secret))
		simpleexample_messages.SendResponse(ms, req)
		log.Printf("HandleServiceRequest response buffer: ")
		simpleexample_messages.PrintMessage(req)
		return true, nil
	} else {
		log.Printf("HandleServiceRequest response is bad request\n")
		errmsg := "BadRequest"
		req.Err = &errmsg
		return false, nil
	}
}

func serviceThead(ms *util.MessageStream, clientProgramName string,
	serverProgramData *taosupport.TaoProgramData) {

	for {
		req, err := simpleexample_messages.GetRequest(ms)
		if err != nil {
			return
		}
		log.Printf("serviceThread, got message: ")
		simpleexample_messages.PrintMessage(req)

		terminate, _ := HandleServiceRequest(ms, serverProgramData,
			clientProgramName, req)
		if terminate {
			break
		}
	}
	log.Printf("simpleserver: client thread terminating\n")
}

// This is the server. It implements the server Tao Channel negotiation corresponding
// to the client's taosupport.OpenTaoChannel.  It's possible we should move this into
// taosupport/taosupport.go since it should not vary very much from implementation to
// implementation.
func server(serverAddr string, serverProgramData *taosupport.TaoProgramData) {

	var sock net.Listener

	// Set up the single root certificate for channel negotiation which is the
	// policy key cert.
	pool := x509.NewCertPool()
	policyCert, err := x509.ParseCertificate(serverProgramData.PolicyCert)
	if err != nil {
		log.Printf("simpleserver, can't parse policyCert: ", err, "\n")
		return
	}
	// Make the policy cert the unique root of the verification chain.
	pool.AddCert(policyCert)
	tlsc, err := tao.EncodeTLSCert(&serverProgramData.ProgramKey)
	if err != nil {
		log.Printf("simpleserver, encode error: ", err, "\n")
		return
	}
	conf := &tls.Config{
		RootCAs:            pool,
		Certificates:       []tls.Certificate{*tlsc},
		InsecureSkipVerify: false,
		ClientAuth:         tls.RequireAnyClientCert,
	}

	// Listen for clients.
	log.Printf("simpleserver: Listening\n")
	sock, err = tls.Listen("tcp", serverAddr, conf)
	if err != nil {
		log.Printf("simpleserver, listen error: ", err, "\n")
		return
	}

	// Service client connections.
	for {
		log.Printf("server: at accept\n")
		conn, err := sock.Accept()
		if err != nil {
			fmt.Printf("simpleserver: can't accept connection: %s\n", err.Error())
			log.Printf("server: can't accept connection: %s\n", err.Error())
			continue
		}
		var clientName string
		err = conn.(*tls.Conn).Handshake()
		if err != nil {
			log.Printf("server: TLS handshake failed\n")
			continue
		}
		peerCerts := conn.(*tls.Conn).ConnectionState().PeerCertificates
		if peerCerts == nil {
			log.Printf("server: can't get peer list\n")
			continue
		}
		peerCert := conn.(*tls.Conn).ConnectionState().PeerCertificates[0]
		if peerCert.Raw == nil {
			log.Printf("server: can't get peer cert\n")
			continue
		}
		if peerCert.Subject.OrganizationalUnit == nil {
			log.Printf("server: can't get peer name\n")
			continue
		}
		clientName = peerCert.Subject.OrganizationalUnit[0]
		log.Printf("server, peer client name: %s\n", clientName)
		ms := util.NewMessageStream(conn)

		// At this point the handshake is complete and we fork a service thread
		// to communicate with this simpleclient.  ms is the bi-directional
		// confidentiality and integrity protected channel corresponding to the
		// channel opened by OpenTaoChannel.
		go serviceThead(ms, clientName, serverProgramData)
	}
}

func main() {

	// main is very similar to the initial parts of main in simpleclient.
	// see the comments there.
	var serverProgramData taosupport.TaoProgramData
	defer serverProgramData.ClearTaoProgramData()

	flag.Parse()
	serverAddr = *serverHost + ":" + *serverPort

	// Load domain info for this domain
	err := taosupport.TaoParadigm(simpleCfg, simpleServerPath, "ECC-P-256.aes128.hmacaes256",
		*useSimpleDomainService, *caAddr, &serverProgramData)
	if err != nil {
		log.Fatalln("simpleserver: Can't establish Tao", err)
	}
	log.Printf("simpleserver name is %s\n", serverProgramData.TaoName)

	server(serverAddr, &serverProgramData)
	log.Printf("simpleserver: done\n")
}
<commit_msg>serviceThread rename.<commit_after>// Copyright (c) 2014, Google, Inc.  All rights reserved.
//
// 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.
//
// File: simpleserver.go

package main

import (
	"crypto/tls"
	"crypto/x509"
	"flag"
	"fmt"
	"log"
	"net"

	"github.com/jlmucb/cloudproxy/go/apps/simpleexample/common"
	taosupport "github.com/jlmucb/cloudproxy/go/support_libraries/tao_support"
	"github.com/jlmucb/cloudproxy/go/tao"
	"github.com/jlmucb/cloudproxy/go/util"
)

var caAddr = flag.String("caAddr", "localhost:8124", "The address to listen on")
var simpleCfg = flag.String("domain_config",
	"./tao.config",
	"path to simple tao configuration")
var simpleServerPath = flag.String("path",
	"./SimpleServer",
	"path to Server files")
var serverHost = flag.String("host", "localhost", "address for client/server")
var serverPort = flag.String("port", "8123", "port for client/server")
var useSimpleDomainService = flag.Bool("use_simpledomainservice", true,
	"whether to use simple domain service")
var serverAddr string

// Handle service request, req and return response over channel (ms).
// This handles the one valid service request: "SecretRequest"
// and terminates the channel after the first successful request
// which is not generally what would happen in most channels.
// Note that in the future, we might want to use grpc rather than custom
// service request/response buffers but we don't want to introduce complexity
// into this example.  The single request response buffer is defined in
// taosupport/taosupport.proto.
func HandleServiceRequest(ms *util.MessageStream, serverProgramData *taosupport.TaoProgramData,
	clientProgramName string, req *simpleexample_messages.SimpleMessage) (bool, error) {

	//  The somewhat boring secret is the corresponding simpleclient's program name || 43
	secret := clientProgramName + "43"

	if *req.RequestType == "SecretRequest" {
		req.Data = append(req.Data, []byte(secret))
		simpleexample_messages.SendResponse(ms, req)
		log.Printf("HandleServiceRequest response buffer: ")
		simpleexample_messages.PrintMessage(req)
		return true, nil
	} else {
		log.Printf("HandleServiceRequest response is bad request\n")
		errmsg := "BadRequest"
		req.Err = &errmsg
		return false, nil
	}
}

func serviceThread(ms *util.MessageStream, clientProgramName string,
	serverProgramData *taosupport.TaoProgramData) {

	for {
		req, err := simpleexample_messages.GetRequest(ms)
		if err != nil {
			return
		}
		log.Printf("serviceThread, got message: ")
		simpleexample_messages.PrintMessage(req)

		terminate, _ := HandleServiceRequest(ms, serverProgramData,
			clientProgramName, req)
		if terminate {
			break
		}
	}
	log.Printf("simpleserver: client thread terminating\n")
}

// This is the server. It implements the server Tao Channel negotiation corresponding
// to the client's taosupport.OpenTaoChannel.  It's possible we should move this into
// taosupport/taosupport.go since it should not vary very much from implementation to
// implementation.
func server(serverAddr string, serverProgramData *taosupport.TaoProgramData) {

	var sock net.Listener

	// Set up the single root certificate for channel negotiation which is the
	// policy key cert.
	pool := x509.NewCertPool()
	policyCert, err := x509.ParseCertificate(serverProgramData.PolicyCert)
	if err != nil {
		log.Printf("simpleserver, can't parse policyCert: ", err, "\n")
		return
	}
	// Make the policy cert the unique root of the verification chain.
	pool.AddCert(policyCert)
	tlsc, err := tao.EncodeTLSCert(&serverProgramData.ProgramKey)
	if err != nil {
		log.Printf("simpleserver, encode error: ", err, "\n")
		return
	}
	conf := &tls.Config{
		RootCAs:            pool,
		Certificates:       []tls.Certificate{*tlsc},
		InsecureSkipVerify: false,
		ClientAuth:         tls.RequireAnyClientCert,
	}

	// Listen for clients.
	log.Printf("simpleserver: Listening\n")
	sock, err = tls.Listen("tcp", serverAddr, conf)
	if err != nil {
		log.Printf("simpleserver, listen error: ", err, "\n")
		return
	}

	// Service client connections.
	for {
		log.Printf("server: at accept\n")
		conn, err := sock.Accept()
		if err != nil {
			fmt.Printf("simpleserver: can't accept connection: %s\n", err.Error())
			log.Printf("server: can't accept connection: %s\n", err.Error())
			continue
		}
		var clientName string
		err = conn.(*tls.Conn).Handshake()
		if err != nil {
			log.Printf("server: TLS handshake failed\n")
			continue
		}
		peerCerts := conn.(*tls.Conn).ConnectionState().PeerCertificates
		if peerCerts == nil {
			log.Printf("server: can't get peer list\n")
			continue
		}
		peerCert := conn.(*tls.Conn).ConnectionState().PeerCertificates[0]
		if peerCert.Raw == nil {
			log.Printf("server: can't get peer cert\n")
			continue
		}
		if peerCert.Subject.OrganizationalUnit == nil {
			log.Printf("server: can't get peer name\n")
			continue
		}
		clientName = peerCert.Subject.OrganizationalUnit[0]
		log.Printf("server, peer client name: %s\n", clientName)
		ms := util.NewMessageStream(conn)

		// At this point the handshake is complete and we fork a service thread
		// to communicate with this simpleclient.  ms is the bi-directional
		// confidentiality and integrity protected channel corresponding to the
		// channel opened by OpenTaoChannel.
		go serviceThread(ms, clientName, serverProgramData)
	}
}

func main() {

	// main is very similar to the initial parts of main in simpleclient.
	// see the comments there.
	var serverProgramData taosupport.TaoProgramData
	defer serverProgramData.ClearTaoProgramData()

	flag.Parse()
	serverAddr = *serverHost + ":" + *serverPort

	// Load domain info for this domain
	err := taosupport.TaoParadigm(simpleCfg, simpleServerPath, "ECC-P-256.aes128.hmacaes256",
		*useSimpleDomainService, *caAddr, &serverProgramData)
	if err != nil {
		log.Fatalln("simpleserver: Can't establish Tao", err)
	}
	log.Printf("simpleserver name is %s\n", serverProgramData.TaoName)

	server(serverAddr, &serverProgramData)
	log.Printf("simpleserver: done\n")
}
<|endoftext|>


<commit_before>package triton

import (
	"bufio"
	"errors"
	"fmt"
	"gopkg.in/fsnotify.v1"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// Server provides static web hosting and a very minimal Content-Management-System, if it
// could even be called that. It searches the current working directory of execution recursively
// for .tmpl files containing valid html/templates, .css files, and .js files. It watches the file
// system for changes and updates the static site accordingly without restarting.
//
// Template files provide two different, but simple, behaviors depending whether they are located
// in dot-directories.
//
// If a .tmpl file is within a dot directory, it is parsed and added to the master static Template
// for use by other templates.
//
// If a .tmpl file is not within a dot directory, it is parsed and added to the master static
// Template. Additionally, a URL entry to that file location and template filename (without the
// extension) is created for web clients to visit. The template executed to create that particular
// page will be the template filename (again, without extension). A quick example:
//
// If the Server was executed at <ROOT>, hosting at SITE.com, and a template file is located at
// <ROOT>/foo/bar/baz.tmpl, then a web client can go to SITE.com/foo/bar/baz.tmpl and the server
// will execute the template named "baz" and serve it.
//
// The special case is the file "#.tmpl", which will serve and match the directory it is contained
// in. So if <ROOT> hosting SITE.com has <ROOT>/#.tmpl, then the client can go to SITE.com (the
// home page!) and be served template "/".
//
// CSS and Javascript files behave as expected with the caveat that they must not be located in
// any dot-directories.
type Server struct {
	// WebHost is used to handle http requests. Do not call
	// the WebHost's ListenAndServe nor ListenAndServeTLS functions. Call
	// the triton Server's variants instead for proper setup to occur.
	WebHost *http.Server
	// ErrChan can be provided by the client, or is created after the
	// server starts listening for requests. The client must listen to
	// the channel for errors, and a closed channel indicates that the server is
	// no longer properly updating its content and must be restarted.
	ErrChan chan error
	// Map between relative URL paths and template names to execute.
	staticTemplates map[string]string
	// Cached static assets.
	staticAssets map[string][]byte
	// Cached static HTML content.
	templates *template.Template
}

// initializeContent maps all content to URI locations.
func (s *Server) initializeContent() error {
	s.staticTemplates = make(map[string]string)
	s.staticAssets = make(map[string][]byte)
	s.templates = nil
	pwd, err := os.Getwd()
	if err != nil {
		return err
	}
	cw := NewContentWalker(".tmpl", ".css", ".js")
	if err = filepath.Walk(pwd, cw.Walk); err != nil {
		return err
	}
	hiddenTmplFiles, okHidden := cw.HiddenFiles(".tmpl")
	tmplFiles, ok := cw.Files(".tmpl")
	if !ok {
		return errors.New(fmt.Sprintf("No content walker .tmpl files"))
	}
	if okHidden {
		s.templates, err = template.ParseFiles(hiddenTmplFiles...)
		if err != nil {
			return err
		}
		s.templates, err = s.templates.ParseFiles(tmplFiles...)
	} else {
		s.templates, err = template.ParseFiles(tmplFiles...)
	}
	if err != nil {
		return err
	}
	for _, file := range tmplFiles {
		rel, err := filepath.Rel(pwd, file)
		if err != nil {
			return err
		}
		baseWithExt := filepath.Base(file)
		base := baseWithExt
		relNoExt := rel
		if filepath.Ext(file) != "" {
			base = baseWithExt[:strings.LastIndex(baseWithExt, ".")]
			relNoExt = rel[:strings.LastIndex(rel, ".")]
			if base == "#" {
				base = "/" + relNoExt[:len(relNoExt)-1]
				relNoExt = relNoExt[:len(relNoExt)-1]
			}
		}
		s.staticTemplates[base] = "/" + relNoExt
	}
	cssFiles, _ := cw.Files(".css")
	for _, file := range cssFiles {
		err = s.readStaticAssetsFile(pwd, file)
		if err != nil {
			return err
		}
	}
	jsFiles, _ := cw.Files(".js")
	for _, file := range jsFiles {
		err = s.readStaticAssetsFile(pwd, file)
		if err != nil {
			return err
		}
	}
	return nil
}

// readStaticAssetsFile associates a specific asset file path to its content.
func (s *Server) readStaticAssetsFile(baseDir string, assetFile string) error {
	file, err := os.Open(assetFile)
	if err != nil {
		return err
	}
	defer file.Close()
	var asset []byte
	scan := bufio.NewScanner(file)
	for scan.Scan() {
		asset = append(asset, scan.Bytes()...)
	}
	relPath, err := filepath.Rel(baseDir, assetFile)
	if err != nil {
		return err
	}
	s.staticAssets["/"+relPath] = asset
	return nil
}

// applyHandlers takes the URI mapped static content and creates web handlers to service
// the content.
func (s *Server) applyHandlers() {
	basicMux := http.NewServeMux()
	for path, content := range s.staticAssets {
		basicMux.HandleFunc(path, func(myContent []byte) func(http.ResponseWriter, *http.Request) {
			return func(wr http.ResponseWriter, req *http.Request) {
				wr.Write(myContent)
			}
		}(content))
	}
	for tmplName, path := range s.staticTemplates {
		basicMux.HandleFunc(path, func(myTmplName string, myTemplate *template.Template) func(http.ResponseWriter, *http.Request) {
			return func(wr http.ResponseWriter, req *http.Request) {
				myTemplate.ExecuteTemplate(wr, myTmplName, nil)
			}
		}(tmplName, s.templates))
	}
	s.WebHost.Handler = basicMux
}

// async_fsnotifylistener is a separate goroutine handling any changes to the filesystem,
// updating the site without taking it down in the process.
func (s *Server) async_fsnotifylistener() {
	if s.ErrChan == nil {
		s.ErrChan = make(chan error)
	}
	pwd, err := os.Getwd()
	if err != nil {
		s.ErrChan <- err
		close(s.ErrChan)
		return
	}
	tempR := RecursiveWatcher(make([]string, 0))
	r := &tempR
	w, err := r.RecursivelyWatch(pwd)
	if err != nil {
		s.ErrChan <- err
		close(s.ErrChan)
		return
	}
	defer w.Close()
	for {
		select {
		case err = <-w.Errors:
			s.ErrChan <- err
		case event := <-w.Events:
			// The following could be quite smarter: for now,
			// be dumb and rebuild the eentire tree.
			switch event.Op {
			case fsnotify.Write:
				fallthrough
			case fsnotify.Rename:
				fallthrough
			case fsnotify.Create:
				fallthrough
			case fsnotify.Remove:
				s.initializeContent()
				s.applyHandlers()
				err = w.Close()
				if err != nil {
					s.ErrChan <- err
					close(s.ErrChan)
					return
				}
				w, err = r.RecursivelyWatch(pwd)
				if err != nil {
					s.ErrChan <- err
					close(s.ErrChan)
					return
				}
			}
		}
	}
}

// ListenAndServe initializes the Server before using the WebHost to serve http
// requests.
func (s *Server) ListenAndServe() error {
	err := s.initializeContent()
	if err != nil {
		return err
	}
	s.applyHandlers()
	go s.async_fsnotifylistener()
	return s.WebHost.ListenAndServe()
}

// ListenAndServeTLS initializes the Server before using the WebHost to serve
// http over TLS.
func (s *Server) ListenAndServeTLS(certFile string, keyFile string) error {
	err := s.initializeContent()
	if err != nil {
		return err
	}
	s.applyHandlers()
	go s.async_fsnotifylistener()
	return s.WebHost.ListenAndServeTLS(certFile, keyFile)
}
<commit_msg>Fix export issue<commit_after>package triton

import (
	"bufio"
	"errors"
	"fmt"
	"gopkg.in/fsnotify.v1"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// Server provides static web hosting and a very minimal Content-Management-System, if it
// could even be called that. It searches the current working directory of execution recursively
// for .tmpl files containing valid html/templates, .css files, and .js files. It watches the file
// system for changes and updates the static site accordingly without restarting.
//
// Template files provide two different, but simple, behaviors depending whether they are located
// in dot-directories.
//
// If a .tmpl file is within a dot directory, it is parsed and added to the master static Template
// for use by other templates.
//
// If a .tmpl file is not within a dot directory, it is parsed and added to the master static
// Template. Additionally, a URL entry to that file location and template filename (without the
// extension) is created for web clients to visit. The template executed to create that particular
// page will be the template filename (again, without extension). A quick example:
//
// If the Server was executed at <ROOT>, hosting at SITE.com, and a template file is located at
// <ROOT>/foo/bar/baz.tmpl, then a web client can go to SITE.com/foo/bar/baz.tmpl and the server
// will execute the template named "baz" and serve it.
//
// The special case is the file "#.tmpl", which will serve and match the directory it is contained
// in. So if <ROOT> hosting SITE.com has <ROOT>/#.tmpl, then the client can go to SITE.com (the
// home page!) and be served template "/".
//
// CSS and Javascript files behave as expected with the caveat that they must not be located in
// any dot-directories.
type Server struct {
	// WebHost is used to handle http requests. Do not call
	// the WebHost's ListenAndServe nor ListenAndServeTLS functions. Call
	// the triton Server's variants instead for proper setup to occur.
	WebHost *http.Server
	// ErrChan can be provided by the client, or is created after the
	// server starts listening for requests. The client must listen to
	// the channel for errors, and a closed channel indicates that the server is
	// no longer properly updating its content and must be restarted.
	ErrChan chan error
	// Map between relative URL paths and template names to execute.
	staticTemplates map[string]string
	// Cached static assets.
	staticAssets map[string][]byte
	// Cached static HTML content.
	templates *template.Template
}

// initializeContent maps all content to URI locations.
func (s *Server) initializeContent() error {
	s.staticTemplates = make(map[string]string)
	s.staticAssets = make(map[string][]byte)
	s.templates = nil
	pwd, err := os.Getwd()
	if err != nil {
		return err
	}
	cw := NewContentWalker(".tmpl", ".css", ".js")
	if err = filepath.Walk(pwd, cw.Walk); err != nil {
		return err
	}
	hiddenTmplFiles, okHidden := cw.HiddenFiles(".tmpl")
	tmplFiles, ok := cw.Files(".tmpl")
	if !ok {
		return errors.New(fmt.Sprintf("No content walker .tmpl files"))
	}
	if okHidden {
		s.templates, err = template.ParseFiles(hiddenTmplFiles...)
		if err != nil {
			return err
		}
		s.templates, err = s.templates.ParseFiles(tmplFiles...)
	} else {
		s.templates, err = template.ParseFiles(tmplFiles...)
	}
	if err != nil {
		return err
	}
	for _, file := range tmplFiles {
		rel, err := filepath.Rel(pwd, file)
		if err != nil {
			return err
		}
		baseWithExt := filepath.Base(file)
		base := baseWithExt
		relNoExt := rel
		if filepath.Ext(file) != "" {
			base = baseWithExt[:strings.LastIndex(baseWithExt, ".")]
			relNoExt = rel[:strings.LastIndex(rel, ".")]
			if base == "#" {
				base = "/" + relNoExt[:len(relNoExt)-1]
				relNoExt = relNoExt[:len(relNoExt)-1]
			}
		}
		s.staticTemplates[base] = "/" + relNoExt
	}
	cssFiles, _ := cw.Files(".css")
	for _, file := range cssFiles {
		err = s.readStaticAssetsFile(pwd, file)
		if err != nil {
			return err
		}
	}
	jsFiles, _ := cw.Files(".js")
	for _, file := range jsFiles {
		err = s.readStaticAssetsFile(pwd, file)
		if err != nil {
			return err
		}
	}
	return nil
}

// readStaticAssetsFile associates a specific asset file path to its content.
func (s *Server) readStaticAssetsFile(baseDir string, assetFile string) error {
	file, err := os.Open(assetFile)
	if err != nil {
		return err
	}
	defer file.Close()
	var asset []byte
	scan := bufio.NewScanner(file)
	for scan.Scan() {
		asset = append(asset, scan.Bytes()...)
	}
	relPath, err := filepath.Rel(baseDir, assetFile)
	if err != nil {
		return err
	}
	s.staticAssets["/"+relPath] = asset
	return nil
}

// applyHandlers takes the URI mapped static content and creates web handlers to service
// the content.
func (s *Server) applyHandlers() {
	basicMux := http.NewServeMux()
	for path, content := range s.staticAssets {
		basicMux.HandleFunc(path, func(myContent []byte) func(http.ResponseWriter, *http.Request) {
			return func(wr http.ResponseWriter, req *http.Request) {
				wr.Write(myContent)
			}
		}(content))
	}
	for tmplName, path := range s.staticTemplates {
		basicMux.HandleFunc(path, func(myTmplName string, myTemplate *template.Template) func(http.ResponseWriter, *http.Request) {
			return func(wr http.ResponseWriter, req *http.Request) {
				myTemplate.ExecuteTemplate(wr, myTmplName, nil)
			}
		}(tmplName, s.templates))
	}
	s.WebHost.Handler = basicMux
}

// async_fsnotifylistener is a separate goroutine handling any changes to the filesystem,
// updating the site without taking it down in the process.
func (s *Server) async_fsnotifylistener() {
	if s.ErrChan == nil {
		s.ErrChan = make(chan error)
	}
	pwd, err := os.Getwd()
	if err != nil {
		s.ErrChan <- err
		close(s.ErrChan)
		return
	}
	tempR := recursiveWatcher(make([]string, 0))
	r := &tempR
	w, err := r.RecursivelyWatch(pwd)
	if err != nil {
		s.ErrChan <- err
		close(s.ErrChan)
		return
	}
	defer w.Close()
	for {
		select {
		case err = <-w.Errors:
			s.ErrChan <- err
		case event := <-w.Events:
			// The following could be quite smarter: for now,
			// be dumb and rebuild the eentire tree.
			switch event.Op {
			case fsnotify.Write:
				fallthrough
			case fsnotify.Rename:
				fallthrough
			case fsnotify.Create:
				fallthrough
			case fsnotify.Remove:
				s.initializeContent()
				s.applyHandlers()
				err = w.Close()
				if err != nil {
					s.ErrChan <- err
					close(s.ErrChan)
					return
				}
				w, err = r.RecursivelyWatch(pwd)
				if err != nil {
					s.ErrChan <- err
					close(s.ErrChan)
					return
				}
			}
		}
	}
}

// ListenAndServe initializes the Server before using the WebHost to serve http
// requests.
func (s *Server) ListenAndServe() error {
	err := s.initializeContent()
	if err != nil {
		return err
	}
	s.applyHandlers()
	go s.async_fsnotifylistener()
	return s.WebHost.ListenAndServe()
}

// ListenAndServeTLS initializes the Server before using the WebHost to serve
// http over TLS.
func (s *Server) ListenAndServeTLS(certFile string, keyFile string) error {
	err := s.initializeContent()
	if err != nil {
		return err
	}
	s.applyHandlers()
	go s.async_fsnotifylistener()
	return s.WebHost.ListenAndServeTLS(certFile, keyFile)
}
<|endoftext|>


<commit_before>package main

import (
	"sync"
	"time"
)

type entry struct {
	tstamp     time.Time
	fetchCount int
}

type TtlSet struct {
	sync.RWMutex
	s    map[string]entry
	quit chan struct{}
}

func NewTtlSet() *TtlSet {
	t := &TtlSet{}
	t.s = make(map[string]entry)
	t.quit = make(chan struct{})
	go t.garbageCollector()
	return t
}

func (t *TtlSet) Has(key string) bool {
	t.RLock()
	defer t.RUnlock()
	_, ok := t.s[key]
	return ok
}

func (t *TtlSet) Put(key string, ttl time.Duration) {
	t.Lock()
	a := t.s[key]
	if a.fetchCount == 0 {
		a.tstamp = time.Now().Add(ttl)
	}
	a.fetchCount = a.fetchCount + 1
	t.s[key] = a
	t.Unlock()
}

func (t *TtlSet) UsageCount(key string) int {
	//zz := t.s[key]
	//return zz.fetchCount
	return t.s[key].fetchCount
}

func (t *TtlSet) Destroy() {
	t.Lock()
	close(t.quit)
	t.s = nil
	t.Unlock()
}

func (t *TtlSet) cleanup() {
	t.Lock()
	for k, v := range t.s {
		if time.Now().After(v.tstamp) {
			delete(t.s, k)
		}
	}
	t.Unlock()
}

func (t *TtlSet) garbageCollector() {
	ticker := time.Tick(5 * time.Second)
	for {
		select {
		case <-ticker:
			t.cleanup()
		case <-t.quit:
			return
		}
	}
}
<commit_msg>Removed commented code<commit_after>package main

import (
	"sync"
	"time"
)

type entry struct {
	tstamp     time.Time
	fetchCount int
}

type TtlSet struct {
	sync.RWMutex
	s    map[string]entry
	quit chan struct{}
}

func NewTtlSet() *TtlSet {
	t := &TtlSet{}
	t.s = make(map[string]entry)
	t.quit = make(chan struct{})
	go t.garbageCollector()
	return t
}

func (t *TtlSet) Has(key string) bool {
	t.RLock()
	defer t.RUnlock()
	_, ok := t.s[key]
	return ok
}

func (t *TtlSet) Put(key string, ttl time.Duration) {
	t.Lock()
	a := t.s[key]
	if a.fetchCount == 0 {
		a.tstamp = time.Now().Add(ttl)
	}
	a.fetchCount = a.fetchCount + 1
	t.s[key] = a
	t.Unlock()
}

func (t *TtlSet) UsageCount(key string) int {
	return t.s[key].fetchCount
}

func (t *TtlSet) Destroy() {
	t.Lock()
	close(t.quit)
	t.s = nil
	t.Unlock()
}

func (t *TtlSet) cleanup() {
	t.Lock()
	for k, v := range t.s {
		if time.Now().After(v.tstamp) {
			delete(t.s, k)
		}
	}
	t.Unlock()
}

func (t *TtlSet) garbageCollector() {
	ticker := time.Tick(5 * time.Second)
	for {
		select {
		case <-ticker:
			t.cleanup()
		case <-t.quit:
			return
		}
	}
}
<|endoftext|>


<commit_before>package symmetric

// constants for the symmetric cryptographic functions.

import (
	"crypto/aes"
	"fmt"
)

const BlockSize = aes.BlockSize
const KeySize = 32

var Cipher = aes.NewCipher

var (
	PaddingError = fmt.Errorf("invalid padding")
)
<commit_msg>add degraded error<commit_after>package symmetric

// constants for the symmetric cryptographic functions.

import (
	"crypto/aes"
	"fmt"
)

const BlockSize = aes.BlockSize
const KeySize = 32

var Cipher = aes.NewCipher

var (
	PaddingError = fmt.Errorf("invalid padding")
	DegradedError = fmt.Errorf("package is in degraded mode")
)
<|endoftext|>


<commit_before>package checkdisk

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
	gpud "github.com/shirou/gopsutil/disk"
)

var opts struct {
	Warning       *string `short:"w" long:"warning" value-name:"N, N%" description:"Exit with WARNING status if less than N units or N% of disk are free"`
	Critical      *string `short:"c" long:"critical" value-name:"N, N%" description:"Exit with CRITICAL status if less than N units or N% of disk are free"`
	InodeWarning  *string `short:"W" long:"iwarning" value-name:"N%" description:"Exit with WARNING status if less than PERCENT of inode space is free"`
	InodeCritical *string `short:"K" long:"icritical" value-name:"N%" description:"Exit with CRITICAL status if less than PERCENT of inode space is free"`
	Path          *string `short:"p" long:"path" value-name:"PATH" description:"Mount point or block device as emitted by the mount(8) command"`
	Exclude       *string `short:"x" long:"exclude-device" value-name:"EXCLUDE PATH" description:"Ignore device (only works if -p unspecified)"`
	All           bool    `short:"A" long:"all" description:"Explicitly select all paths."`
	ExcludeType   *string `short:"X" long:"exclude-type" value-name:"TYPE" description:"Ignore all filesystems of indicated type"`
	IncludeType   *string `short:"N" long:"include-type" value-name:"TYPE" description:"Check only filesystems of indicated type"`
	Units         *string `short:"u" long:"units" value-name:"STRING" description:"Choose bytes, kB, MB, GB, TB (default: MB)"`
}

const (
	b  = float64(1)
	kb = float64(1024) * b
	mb = float64(1024) * kb
	gb = float64(1024) * mb
	tb = float64(1024) * gb
)

type unit struct {
	Name string
	Size float64
}

func checkStatus(current checkers.Status, threshold string, units float64, disk *gpud.UsageStat, chkInode bool, status checkers.Status) (checkers.Status, error) {
	if strings.HasSuffix(threshold, "%") {
		v, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		freePct := float64(100) - disk.UsedPercent
		inodesFreePct := float64(100) - disk.InodesUsedPercent

		if chkInode && v > inodesFreePct {
			current = status
		}

		if !chkInode && (v > freePct || v > inodesFreePct) {
			current = status
		}
	} else {
		if chkInode {
			return checkers.UNKNOWN, errors.New("-W, -K value should be N%")
		}

		v, err := strconv.ParseFloat(threshold, 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		free := float64(disk.Free) / units
		if v > free {
			current = status
		}
	}

	return current, nil
}

func genMessage(disk *gpud.UsageStat, u unit) string {
	all := float64(disk.Total) / u.Size
	used := float64(disk.Used) / u.Size
	free := float64(disk.Free) / u.Size
	freePct := float64(100) - disk.UsedPercent
	inodesFreePct := float64(100) - disk.InodesUsedPercent

	return fmt.Sprintf("Path: %v, All: %.2f %v, Used: %.2f %v, Free: %.2f %v, Free percentage: %.2f (inodes: %.2f)", disk.Path, all, u.Name, used, u.Name, free, u.Name, freePct, inodesFreePct)
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "Disk"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	partitions, err := listPartitions(opts.IncludeType, opts.ExcludeType)
	if err != nil {
		return checkers.Unknown(fmt.Sprintf("Faild to fetch partitions: %s", err))
	}

	if !opts.All {
		if opts.Path != nil {
			if opts.Exclude != nil {
				return checkers.Unknown(fmt.Sprintf("Invalid arguments: %s", errors.New("-x does not work with -p")))
			}

			exist := false
			for _, partition := range partitions {
				if *opts.Path == partition.Mountpoint {
					partitions = []gpud.PartitionStat{partition}
					exist = true
					break
				}
			}

			if !exist {
				return checkers.Unknown(fmt.Sprintf("Faild to fetch mountpoint: %s", errors.New("Invalid argument flag '-p, --path'")))
			}
		}

		if opts.Path == nil && opts.Exclude != nil {
			var tmp []gpud.PartitionStat
			for _, partition := range partitions {
				if *opts.Exclude != partition.Mountpoint {
					tmp = append(tmp, partition)
				}
			}
			partitions = tmp
		}
	}

	var disks []*gpud.UsageStat

	for _, partition := range partitions {
		disk, err := gpud.Usage(partition.Mountpoint)
		if err != nil {
			return checkers.Unknown(fmt.Sprintf("Faild to fetch disk usage: %s", err))
		}

		disks = append(disks, disk)
	}

	u := unit{"MB", mb}
	if opts.Units != nil {
		us := strings.ToLower(*opts.Units)
		if us == "bytes" {
			u = unit{us, b}
		} else if us == "kb" {
			u = unit{us, mb}
		} else if us == "gb" {
			u = unit{us, gb}
		} else if us == "tb" {
			u = unit{us, tb}
		} else {
			return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", errors.New("Invalid argument flag '-u, --units'")))
		}
	}

	checkSt := checkers.OK
	if opts.InodeCritical != nil {
		for _, disk := range disks {
			checkSt, err = checkStatus(checkSt, *opts.InodeCritical, u.Size, disk, true, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.Critical != nil {
		for _, disk := range disks {
			checkSt, err = checkStatus(checkSt, *opts.Critical, u.Size, disk, false, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.InodeWarning != nil {
		for _, disk := range disks {
			checkSt, err = checkStatus(checkSt, *opts.InodeWarning, u.Size, disk, true, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	if checkSt == checkers.OK && opts.Warning != nil {
		for _, disk := range disks {
			checkSt, err = checkStatus(checkSt, *opts.Warning, u.Size, disk, false, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	var msgs []string
	for _, disk := range disks {
		msg := genMessage(disk, u)
		msgs = append(msgs, msg)
	}
	msgss := strings.Join(msgs, ";\n")

	return checkers.NewChecker(checkSt, msgss)
}

// ref: mountlist.c in gnulib
// https://github.com/coreutils/gnulib/blob/a742bdb3/lib/mountlist.c#L168
func listPartitions(includeType *string, excludeType *string) ([]gpud.PartitionStat, error) {
	allPartitions, err := gpud.Partitions(true)
	if err != nil {
		return nil, err
	}
	partitions := make([]gpud.PartitionStat, 0, len(allPartitions))
	for _, p := range allPartitions {
		if includeType != nil {
			if p.Fstype == *includeType {
				partitions = append(partitions, p)
				continue
			}
		}

		if excludeType != nil {
			if p.Fstype == *excludeType {
				continue
			}
		}

		switch p.Fstype {
		case "autofs",
			"proc",
			"subfs",
			"debugfs",
			"devpts",
			"fusectl",
			"mqueue",
			"rpc_pipefs",
			"sysfs",
			"devfs",
			"kernfs",
			"ignore":
			continue
		case "none":
			if !strings.Contains(p.Opts, "bind") {
				partitions = append(partitions, p)
			}
		default:
			partitions = append(partitions, p)
		}
	}
	return partitions, nil
}
<commit_msg>Change error message<commit_after>package checkdisk

import (
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/jessevdk/go-flags"
	"github.com/mackerelio/checkers"
	gpud "github.com/shirou/gopsutil/disk"
)

var opts struct {
	Warning       *string `short:"w" long:"warning" value-name:"N, N%" description:"Exit with WARNING status if less than N units or N% of disk are free"`
	Critical      *string `short:"c" long:"critical" value-name:"N, N%" description:"Exit with CRITICAL status if less than N units or N% of disk are free"`
	InodeWarning  *string `short:"W" long:"iwarning" value-name:"N%" description:"Exit with WARNING status if less than PERCENT of inode space is free"`
	InodeCritical *string `short:"K" long:"icritical" value-name:"N%" description:"Exit with CRITICAL status if less than PERCENT of inode space is free"`
	Path          *string `short:"p" long:"path" value-name:"PATH" description:"Mount point or block device as emitted by the mount(8) command"`
	Exclude       *string `short:"x" long:"exclude-device" value-name:"EXCLUDE PATH" description:"Ignore device (only works if -p unspecified)"`
	All           bool    `short:"A" long:"all" description:"Explicitly select all paths."`
	ExcludeType   *string `short:"X" long:"exclude-type" value-name:"TYPE" description:"Ignore all filesystems of indicated type"`
	IncludeType   *string `short:"N" long:"include-type" value-name:"TYPE" description:"Check only filesystems of indicated type"`
	Units         *string `short:"u" long:"units" value-name:"STRING" description:"Choose bytes, kB, MB, GB, TB (default: MB)"`
}

const (
	b  = float64(1)
	kb = float64(1024) * b
	mb = float64(1024) * kb
	gb = float64(1024) * mb
	tb = float64(1024) * gb
)

type unit struct {
	Name string
	Size float64
}

func checkStatus(current checkers.Status, threshold string, units float64, disk *gpud.UsageStat, chkInode bool, status checkers.Status) (checkers.Status, error) {
	if strings.HasSuffix(threshold, "%") {
		v, err := strconv.ParseFloat(strings.TrimRight(threshold, "%"), 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		freePct := float64(100) - disk.UsedPercent
		inodesFreePct := float64(100) - disk.InodesUsedPercent

		if chkInode && v > inodesFreePct {
			current = status
		}

		if !chkInode && (v > freePct || v > inodesFreePct) {
			current = status
		}
	} else {
		if chkInode {
			return checkers.UNKNOWN, errors.New("-W, -K value should be N%")
		}

		v, err := strconv.ParseFloat(threshold, 64)
		if err != nil {
			return checkers.UNKNOWN, err
		}

		free := float64(disk.Free) / units
		if v > free {
			current = status
		}
	}

	return current, nil
}

func genMessage(disk *gpud.UsageStat, u unit) string {
	all := float64(disk.Total) / u.Size
	used := float64(disk.Used) / u.Size
	free := float64(disk.Free) / u.Size
	freePct := float64(100) - disk.UsedPercent
	inodesFreePct := float64(100) - disk.InodesUsedPercent

	return fmt.Sprintf("Path: %v, All: %.2f %v, Used: %.2f %v, Free: %.2f %v, Free percentage: %.2f (inodes: %.2f)", disk.Path, all, u.Name, used, u.Name, free, u.Name, freePct, inodesFreePct)
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "Disk"
	ckr.Exit()
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	partitions, err := listPartitions(opts.IncludeType, opts.ExcludeType)
	if err != nil {
		return checkers.Unknown(fmt.Sprintf("Faild to fetch partitions: %s", err))
	}

	if !opts.All {
		if opts.Path != nil {
			if opts.Exclude != nil {
				return checkers.Unknown(fmt.Sprintf("Invalid arguments: %s", errors.New("-x does not work with -p")))
			}

			exist := false
			for _, partition := range partitions {
				if *opts.Path == partition.Mountpoint {
					partitions = []gpud.PartitionStat{partition}
					exist = true
					break
				}
			}

			if !exist {
				return checkers.Unknown(fmt.Sprintf("Faild to fetch mountpoint: %s", errors.New("No device found for the specified path")))
			}
		}

		if opts.Path == nil && opts.Exclude != nil {
			var tmp []gpud.PartitionStat
			for _, partition := range partitions {
				if *opts.Exclude != partition.Mountpoint {
					tmp = append(tmp, partition)
				}
			}
			partitions = tmp
		}
	}

	var disks []*gpud.UsageStat

	for _, partition := range partitions {
		disk, err := gpud.Usage(partition.Mountpoint)
		if err != nil {
			return checkers.Unknown(fmt.Sprintf("Faild to fetch disk usage: %s", err))
		}

		disks = append(disks, disk)
	}

	u := unit{"MB", mb}
	if opts.Units != nil {
		us := strings.ToLower(*opts.Units)
		if us == "bytes" {
			u = unit{us, b}
		} else if us == "kb" {
			u = unit{us, mb}
		} else if us == "gb" {
			u = unit{us, gb}
		} else if us == "tb" {
			u = unit{us, tb}
		} else {
			return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", errors.New("Invalid argument flag '-u, --units'")))
		}
	}

	checkSt := checkers.OK
	if opts.InodeCritical != nil {
		for _, disk := range disks {
			checkSt, err = checkStatus(checkSt, *opts.InodeCritical, u.Size, disk, true, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.Critical != nil {
		for _, disk := range disks {
			checkSt, err = checkStatus(checkSt, *opts.Critical, u.Size, disk, false, checkers.CRITICAL)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", err))
			}

			if checkSt == checkers.CRITICAL {
				break
			}
		}
	}

	if checkSt != checkers.CRITICAL && opts.InodeWarning != nil {
		for _, disk := range disks {
			checkSt, err = checkStatus(checkSt, *opts.InodeWarning, u.Size, disk, true, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	if checkSt == checkers.OK && opts.Warning != nil {
		for _, disk := range disks {
			checkSt, err = checkStatus(checkSt, *opts.Warning, u.Size, disk, false, checkers.WARNING)
			if err != nil {
				return checkers.Unknown(fmt.Sprintf("Faild to check disk status: %s", err))
			}

			if checkSt == checkers.WARNING {
				break
			}
		}
	}

	var msgs []string
	for _, disk := range disks {
		msg := genMessage(disk, u)
		msgs = append(msgs, msg)
	}
	msgss := strings.Join(msgs, ";\n")

	return checkers.NewChecker(checkSt, msgss)
}

// ref: mountlist.c in gnulib
// https://github.com/coreutils/gnulib/blob/a742bdb3/lib/mountlist.c#L168
func listPartitions(includeType *string, excludeType *string) ([]gpud.PartitionStat, error) {
	allPartitions, err := gpud.Partitions(true)
	if err != nil {
		return nil, err
	}
	partitions := make([]gpud.PartitionStat, 0, len(allPartitions))
	for _, p := range allPartitions {
		if includeType != nil {
			if p.Fstype == *includeType {
				partitions = append(partitions, p)
				continue
			}
		}

		if excludeType != nil {
			if p.Fstype == *excludeType {
				continue
			}
		}

		switch p.Fstype {
		case "autofs",
			"proc",
			"subfs",
			"debugfs",
			"devpts",
			"fusectl",
			"mqueue",
			"rpc_pipefs",
			"sysfs",
			"devfs",
			"kernfs",
			"ignore":
			continue
		case "none":
			if !strings.Contains(p.Opts, "bind") {
				partitions = append(partitions, p)
			}
		default:
			partitions = append(partitions, p)
		}
	}
	return partitions, nil
}
<|endoftext|>


<commit_before>package checksmtp

import (
	"os"

	"net/smtp"

	"fmt"

	"crypto/tls"

	"net"

	"time"

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

var opts struct {
	Host     string  `short:"H" long:"host" default:"localhost" description:"Hostname"`
	Port     string  `short:"p" long:"port" default:"25" description:"Port"`
	FQDN     string  `short:"F" long:"fqdn" description:"FQDN used for HELO"`
	SMTPS    bool    `short:"s" long:"smtps" description:"Use SMTP over TLS"`
	StartTLS bool    `short:"S" long:"starttls" description:"Use STARTTLS"`
	Auth     string  `short:"A" long:"authmech" description:"SMTP AUTH Authentication Mechanisms (only PLAIN supported)"`
	User     string  `short:"U" long:"authuser" description:"SMTP AUTH username"`
	Password string  `short:"P" long:"authpassword" description:"SMTP AUTH password"`
	Warning  float64 `short:"w" long:"warning" description:"Warning threshold (sec)"`
	Critical float64 `short:"c" long:"critical" description:"Critical threshold (sec)"`
	Timeout  int     `short:"t" long:"timeout" default:"10" description:"Timeout (sec)"`
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "SMTP"
	ckr.Exit()
}

func makeConn(host, port string, timeout int, isSMTPS bool, tlsConfig *tls.Config) (net.Conn, error) {
	d := net.Dialer{Timeout: time.Duration(timeout) * time.Second}
	if isSMTPS {
		return tls.DialWithDialer(&d, "tcp", fmt.Sprintf("%s:%s", host, port), tlsConfig)
	}
	return d.Dial("tcp", fmt.Sprintf("%s:%s", opts.Host, opts.Port))
}

func run(args []string) *checkers.Checker {
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	if opts.Warning == 0 && opts.Critical == 0 {
		return checkers.Unknown("require threshold option (warning or critical)")
	}

	if opts.Auth != "" && opts.Auth != "PLAIN" {
		return checkers.Unknown("invalid SMTP AUTH Authentication Mechanisms (only PLAIN supported)")
	}

	fqdn := opts.FQDN
	if fqdn == "" {
		localHostName, err := os.Hostname()
		if err != nil {
			return checkers.Unknown(err.Error())
		}
		fqdn = localHostName
	}

	tlsConfig := &tls.Config{
		InsecureSkipVerify: true,
		ServerName:         opts.Host,
	}

	conn, err := makeConn(opts.Host, opts.Port, opts.Timeout, opts.SMTPS, tlsConfig)
	if err != nil {
		return checkers.Critical(err.Error())
	}

	stTime := time.Now()

	c, err := smtp.NewClient(conn, opts.Host)
	if err != nil {
		return checkers.Critical(err.Error())
	}
	defer c.Quit()

	if err := c.Hello(fqdn); err != nil {
		return checkers.Critical(err.Error())
	}

	if opts.StartTLS {
		if err := c.StartTLS(tlsConfig); err != nil {
			return checkers.Critical(err.Error())
		}
	}

	if opts.Auth == "PLAIN" {
		auth := smtp.PlainAuth("", opts.User, opts.Password, opts.Host)
		if err := c.Auth(auth); err != nil {
			return checkers.Critical(err.Error())
		}
	}

	if err := c.Mail(""); err != nil {
		c.Reset()
		return checkers.Critical(err.Error())
	}
	c.Reset()

	elapsed := time.Since(stTime)

	msg := fmt.Sprintf("%.3f seconds response time", elapsed.Seconds())

	if opts.Critical != 0 && elapsed.Seconds() > opts.Critical {
		return checkers.Critical(msg)
	} else if opts.Warning != 0 && elapsed.Seconds() > opts.Warning {
		return checkers.Warning(msg)
	} else {
		return checkers.Ok(msg)
	}
}
<commit_msg>Initialize options when invoke `run()`<commit_after>package checksmtp

import (
	"os"

	"net/smtp"

	"fmt"

	"crypto/tls"

	"net"

	"time"

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

type options struct {
	Host     string  `short:"H" long:"host" default:"localhost" description:"Hostname"`
	Port     string  `short:"p" long:"port" default:"25" description:"Port"`
	FQDN     string  `short:"F" long:"fqdn" description:"FQDN used for HELO"`
	SMTPS    bool    `short:"s" long:"smtps" description:"Use SMTP over TLS"`
	StartTLS bool    `short:"S" long:"starttls" description:"Use STARTTLS"`
	Auth     string  `short:"A" long:"authmech" description:"SMTP AUTH Authentication Mechanisms (only PLAIN supported)"`
	User     string  `short:"U" long:"authuser" description:"SMTP AUTH username"`
	Password string  `short:"P" long:"authpassword" description:"SMTP AUTH password"`
	Warning  float64 `short:"w" long:"warning" description:"Warning threshold (sec)"`
	Critical float64 `short:"c" long:"critical" description:"Critical threshold (sec)"`
	Timeout  int     `short:"t" long:"timeout" default:"10" description:"Timeout (sec)"`
}

// Do the plugin
func Do() {
	ckr := run(os.Args[1:])
	ckr.Name = "SMTP"
	ckr.Exit()
}

func makeConn(host, port string, timeout int, isSMTPS bool, tlsConfig *tls.Config) (net.Conn, error) {
	d := net.Dialer{Timeout: time.Duration(timeout) * time.Second}
	if isSMTPS {
		return tls.DialWithDialer(&d, "tcp", fmt.Sprintf("%s:%s", host, port), tlsConfig)
	}
	return d.Dial("tcp", fmt.Sprintf("%s:%s", host, port))
}

func run(args []string) *checkers.Checker {
	var opts options
	_, err := flags.ParseArgs(&opts, args)
	if err != nil {
		os.Exit(1)
	}

	if opts.Warning == 0 && opts.Critical == 0 {
		return checkers.Unknown("require threshold option (warning or critical)")
	}

	if opts.Auth != "" && opts.Auth != "PLAIN" {
		return checkers.Unknown("invalid SMTP AUTH Authentication Mechanisms (only PLAIN supported)")
	}

	fqdn := opts.FQDN
	if fqdn == "" {
		localHostName, err := os.Hostname()
		if err != nil {
			return checkers.Unknown(err.Error())
		}
		fqdn = localHostName
	}

	tlsConfig := &tls.Config{
		InsecureSkipVerify: true,
		ServerName:         opts.Host,
	}

	conn, err := makeConn(opts.Host, opts.Port, opts.Timeout, opts.SMTPS, tlsConfig)
	if err != nil {
		return checkers.Critical(err.Error())
	}

	stTime := time.Now()

	c, err := smtp.NewClient(conn, opts.Host)
	if err != nil {
		return checkers.Critical(err.Error())
	}
	defer c.Quit()

	if err := c.Hello(fqdn); err != nil {
		return checkers.Critical(err.Error())
	}

	if opts.StartTLS {
		if err := c.StartTLS(tlsConfig); err != nil {
			return checkers.Critical(err.Error())
		}
	}

	if opts.Auth == "PLAIN" {
		auth := smtp.PlainAuth("", opts.User, opts.Password, opts.Host)
		if err := c.Auth(auth); err != nil {
			return checkers.Critical(err.Error())
		}
	}

	if err := c.Mail(""); err != nil {
		c.Reset()
		return checkers.Critical(err.Error())
	}
	c.Reset()

	elapsed := time.Since(stTime)

	msg := fmt.Sprintf("%.3f seconds response time", elapsed.Seconds())

	if opts.Critical != 0 && elapsed.Seconds() > opts.Critical {
		return checkers.Critical(msg)
	} else if opts.Warning != 0 && elapsed.Seconds() > opts.Warning {
		return checkers.Warning(msg)
	} else {
		return checkers.Ok(msg)
	}
}
<|endoftext|>


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

package opt

import (
	"math"

	"github.com/gonum/floats"
)

// Bisection is a LinesearchMethod that uses a bisection to find a point that
// satisfies the strong Wolfe conditions with the given gradient constant and
// function constant of zero. If GradConst is zero, it will be set to a reasonable
// value. Bisection will panic if GradConst is not between zero and one.
type Bisection struct {
	GradConst float64

	minStep  float64
	maxStep  float64
	currStep float64

	initF float64
	minF  float64
	maxF  float64

	initGrad float64
	minGrad  float64
	maxGrad  float64
}

func (b *Bisection) Init(initF, initG, initStepSize float64, f *FunctionStats) EvaluationType {
	if initG > 0 {
		panic("bisection: init G greater than 0")
	}

	if b.GradConst == 0 {
		b.GradConst = 0.9
	}
	if b.GradConst <= 0 || b.GradConst > 1 {
		panic("bisection: GradConst not between 0 and 1")
	}

	b.minStep = 0
	b.maxStep = math.Inf(1)
	b.currStep = initStepSize

	b.initF = initF
	b.minF = initF
	b.maxF = math.NaN()

	b.initGrad = initG
	b.minGrad = initG
	b.maxGrad = math.NaN()

	return FunctionAndGradient
}

func (b *Bisection) Finished(f, g float64) bool {
	if floats.EqualWithinRel(f, b.initF, 1e-14) {
		// The two numbers are so close that we should just check on the gradient
		// TODO: Should iterate be updated? Maybe find a function where it needs it.
		return math.Abs(g) < b.GradConst*math.Abs(b.initGrad)
	}

	return StrongWolfeConditionsMet(f, g, b.initF, b.initGrad, b.currStep, 0, b.GradConst)
}

func (b *Bisection) Iterate(f, g float64) (float64, EvaluationType) {
	// Deciding on the next step size
	if math.IsInf(b.maxStep, 1) {
		// Have not yet bounded the minimum
		switch {
		case g > 0:
			// Found a change in derivative sign, so this is the new maximum
			b.maxStep = b.currStep
			b.maxF = f
			b.maxGrad = g
			b.currStep = (b.minStep + b.maxStep) / 2
			return b.currStep, FunctionAndGradient
		case f <= b.minF:
			// Still haven't found an upper bound, but there is not an increase in
			// function value and the gradient is still negative, so go more in
			// that direction.
			b.minStep = b.currStep
			b.minF = f
			b.minGrad = g
			b.currStep *= 2

			return b.currStep, FunctionAndGradient
		default:
			// Increase in function value, but the gradient is still negative.
			// Means we must have skipped over a local minimum, so set this point
			// as the new maximum
			b.maxStep = b.currStep
			b.maxF = f
			b.maxGrad = g
			b.currStep = (b.minStep + b.maxStep) / 2

			return b.currStep, FunctionAndGradient
		}
	}
	// We have already bounded the minimum, so we're just working to find one
	// close enough to the minimum to meet the strong wolfe conditions
	if g < 0 {
		if f <= b.minF {
			b.minStep = b.currStep
			b.minF = f
			b.minGrad = g
		} else {
			// Negative gradient, but increase in function value, so must have
			// skipped over a local minimum. Set this as the new maximum location
			b.maxStep = b.currStep
			b.maxF = f
			b.maxGrad = g
		}
	} else {
		// Gradient is positive, so minimum must be between the max point and
		// the minimum point
		b.maxStep = b.currStep
		b.maxF = f
		b.maxGrad = g
	}
	b.currStep = (b.minStep + b.maxStep) / 2

	return b.currStep, FunctionAndGradient
}
<commit_msg>Modified Bisection function value check to only take place when the magnitudes of the gradient are small<commit_after>// Copyright ©2014 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package opt

import (
	"math"

	"github.com/gonum/floats"
)

// Bisection is a LinesearchMethod that uses a bisection to find a point that
// satisfies the strong Wolfe conditions with the given gradient constant and
// function constant of zero. If GradConst is zero, it will be set to a reasonable
// value. Bisection will panic if GradConst is not between zero and one.
type Bisection struct {
	GradConst float64

	minStep  float64
	maxStep  float64
	currStep float64

	initF float64
	minF  float64
	maxF  float64

	initGrad float64
	minGrad  float64
	maxGrad  float64
}

func (b *Bisection) Init(initF, initG, initStepSize float64, f *FunctionStats) EvaluationType {
	if initG > 0 {
		panic("bisection: init G greater than 0")
	}

	if b.GradConst == 0 {
		b.GradConst = 0.9
	}
	if b.GradConst <= 0 || b.GradConst > 1 {
		panic("bisection: GradConst not between 0 and 1")
	}

	b.minStep = 0
	b.maxStep = math.Inf(1)
	b.currStep = initStepSize

	b.initF = initF
	b.minF = initF
	b.maxF = math.NaN()

	b.initGrad = initG
	b.minGrad = initG
	b.maxGrad = math.NaN()

	return FunctionAndGradient
}

const (
	funcSmallEqual = 1e-14
	gradSmallEqual = 1e-10
)

func (b *Bisection) Finished(f, g float64) bool {
	if floats.EqualWithinRel(f, b.initF, funcSmallEqual) && math.Abs(g) < gradSmallEqual && math.Abs(b.initGrad) < gradSmallEqual {
		// The two numbers are so close that we should just check on the gradient
		// TODO: Should iterate be updated? Maybe find a function where it needs it.
		return math.Abs(g) < b.GradConst*math.Abs(b.initGrad)
	}

	return StrongWolfeConditionsMet(f, g, b.initF, b.initGrad, b.currStep, 0, b.GradConst)
}

func (b *Bisection) Iterate(f, g float64) (float64, EvaluationType) {
	// Deciding on the next step size
	if math.IsInf(b.maxStep, 1) {
		// Have not yet bounded the minimum
		switch {
		case g > 0:
			// Found a change in derivative sign, so this is the new maximum
			b.maxStep = b.currStep
			b.maxF = f
			b.maxGrad = g
			b.currStep = (b.minStep + b.maxStep) / 2
			return b.currStep, FunctionAndGradient
		case f <= b.minF:
			// Still haven't found an upper bound, but there is not an increase in
			// function value and the gradient is still negative, so go more in
			// that direction.
			b.minStep = b.currStep
			b.minF = f
			b.minGrad = g
			b.currStep *= 2

			return b.currStep, FunctionAndGradient
		default:
			// Increase in function value, but the gradient is still negative.
			// Means we must have skipped over a local minimum, so set this point
			// as the new maximum
			b.maxStep = b.currStep
			b.maxF = f
			b.maxGrad = g
			b.currStep = (b.minStep + b.maxStep) / 2

			return b.currStep, FunctionAndGradient
		}
	}
	// We have already bounded the minimum, so we're just working to find one
	// close enough to the minimum to meet the strong wolfe conditions
	if g < 0 {
		if f <= b.minF {
			b.minStep = b.currStep
			b.minF = f
			b.minGrad = g
		} else {
			// Negative gradient, but increase in function value, so must have
			// skipped over a local minimum. Set this as the new maximum location
			b.maxStep = b.currStep
			b.maxF = f
			b.maxGrad = g
		}
	} else {
		// Gradient is positive, so minimum must be between the max point and
		// the minimum point
		b.maxStep = b.currStep
		b.maxF = f
		b.maxGrad = g
	}
	b.currStep = (b.minStep + b.maxStep) / 2

	return b.currStep, FunctionAndGradient
}
<|endoftext|>


<commit_before>// Package dbitstream is a simple wrapper around a io.Reader and io.Writer to provide bit-level access to the stream.
package dbitstream

import (
	"io"
)

// A Bit is a zero or a one
type Bit bool

const (
	// Zero is our exported type for '0' bits
	Zero Bit = false
	// One is our exported type for '1' bits
	One = true
)

// A BitReader reads bits from an io.Reader
type BitReader struct {
	r     io.Reader
	b     [1]byte
	count uint8
}

// A BitWriter writes bits to an io.Writer
type BitWriter struct {
	w     io.Writer
	b     [1]byte
	count uint8
}

// NewReader returns a BitReader that returns a single bit at a time from 'r'
func NewReader(r io.Reader) *BitReader {
	b := new(BitReader)
	b.r = r
	return b
}

// ReadBit returns the next bit from the stream, reading a new byte from the underlying reader if required.
func (b *BitReader) ReadBit() (Bit, error) {
	if b.count == 0 {
		if n, err := b.r.Read(b.b[:]); n != 1 || err != nil {
			return Zero, err
		}
		b.count = 8
	}
	b.count--
	d := (b.b[0] & 0x80)
	b.b[0] <<= 1
	return d != 0, nil
}

// NewWriter returns a BitWriter that buffers bits and write the resulting bytes to 'w'
func NewWriter(w io.Writer) *BitWriter {
	b := new(BitWriter)
	b.w = w
	b.count = 8
	return b
}

// WriteBit writes a single bit to the stream, writing a new byte to 'w' if required.
func (b *BitWriter) WriteBit(bit Bit) error {

	if bit {
		b.b[0] |= 1 << (b.count - 1)
	}

	b.count--

	if b.count == 0 {
		if n, err := b.w.Write(b.b[:]); n != 1 || err != nil {
			return err
		}
		b.b[0] = 0
		b.count = 8
	}

	return nil
}

// WriteByte writes a single byte to the stream, regardless of alignment
func (b *BitWriter) WriteByte(byt byte) error {

	// fill up b.b with b.count bits from byt
	b.b[0] |= byt >> (8 - b.count)

	if n, err := b.w.Write(b.b[:]); n != 1 || err != nil {
		return err
	}
	b.b[0] = byt << b.count

	return nil
}

// ReadByte writes a single byte to the stream, regardless of alignment
func (b *BitReader) ReadByte() (byte, error) {

	if b.count == 0 {
		if n, err := b.r.Read(b.b[:]); n != 1 || err != nil {
			return 0, err
		}
		return b.b[0], nil
	}

	byt := b.b[0]

	if n, err := b.r.Read(b.b[:]); n != 1 || err != nil {
		return 0, err
	}

	byt |= b.b[0] >> b.count

	b.b[0] <<= (8 - b.count)

	return byt, nil
}

// Flush empties the currently in-process byte by filling it with 'bit'.
func (b *BitWriter) Flush(bit Bit) {

	for b.count != 8 {
		b.WriteBit(bit)
	}

	return
}
<commit_msg>doc fix<commit_after>// Package dbitstream is a simple wrapper around a io.Reader and io.Writer to provide bit-level access to the stream.
package dbitstream

import (
	"io"
)

// A Bit is a zero or a one
type Bit bool

const (
	// Zero is our exported type for '0' bits
	Zero Bit = false
	// One is our exported type for '1' bits
	One = true
)

// A BitReader reads bits from an io.Reader
type BitReader struct {
	r     io.Reader
	b     [1]byte
	count uint8
}

// A BitWriter writes bits to an io.Writer
type BitWriter struct {
	w     io.Writer
	b     [1]byte
	count uint8
}

// NewReader returns a BitReader that returns a single bit at a time from 'r'
func NewReader(r io.Reader) *BitReader {
	b := new(BitReader)
	b.r = r
	return b
}

// ReadBit returns the next bit from the stream, reading a new byte from the underlying reader if required.
func (b *BitReader) ReadBit() (Bit, error) {
	if b.count == 0 {
		if n, err := b.r.Read(b.b[:]); n != 1 || err != nil {
			return Zero, err
		}
		b.count = 8
	}
	b.count--
	d := (b.b[0] & 0x80)
	b.b[0] <<= 1
	return d != 0, nil
}

// NewWriter returns a BitWriter that buffers bits and write the resulting bytes to 'w'
func NewWriter(w io.Writer) *BitWriter {
	b := new(BitWriter)
	b.w = w
	b.count = 8
	return b
}

// WriteBit writes a single bit to the stream, writing a new byte to 'w' if required.
func (b *BitWriter) WriteBit(bit Bit) error {

	if bit {
		b.b[0] |= 1 << (b.count - 1)
	}

	b.count--

	if b.count == 0 {
		if n, err := b.w.Write(b.b[:]); n != 1 || err != nil {
			return err
		}
		b.b[0] = 0
		b.count = 8
	}

	return nil
}

// WriteByte writes a single byte to the stream, regardless of alignment
func (b *BitWriter) WriteByte(byt byte) error {

	// fill up b.b with b.count bits from byt
	b.b[0] |= byt >> (8 - b.count)

	if n, err := b.w.Write(b.b[:]); n != 1 || err != nil {
		return err
	}
	b.b[0] = byt << b.count

	return nil
}

// ReadByte reads a single byte from the stream, regardless of alignment
func (b *BitReader) ReadByte() (byte, error) {

	if b.count == 0 {
		if n, err := b.r.Read(b.b[:]); n != 1 || err != nil {
			return 0, err
		}
		return b.b[0], nil
	}

	byt := b.b[0]

	if n, err := b.r.Read(b.b[:]); n != 1 || err != nil {
		return 0, err
	}

	byt |= b.b[0] >> b.count

	b.b[0] <<= (8 - b.count)

	return byt, nil
}

// Flush empties the currently in-process byte by filling it with 'bit'.
func (b *BitWriter) Flush(bit Bit) {

	for b.count != 8 {
		b.WriteBit(bit)
	}

	return
}
<|endoftext|>


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

// Package blog implements a web server for articles written in present format.
package blog

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"

	"code.google.com/p/go.tools/blog/atom"
	"code.google.com/p/go.tools/present"
)

var validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`)

// Config specifies Server configuration values.
type Config struct {
	ContentPath  string // Relative or absolute location of article files and related content.
	TemplatePath string // Relative or absolute location of template files.

	BaseURL  string // Absolute base URL (for permalinks; no trailing slash).
	BasePath string // Base URL path relative to server root (no trailing slash).
	GodocURL string // The base URL of godoc (for menu bar; no trailing slash).
	Hostname string // Server host name, used for rendering ATOM feeds.

	HomeArticles int    // Articles to display on the home page.
	FeedArticles int    // Articles to include in Atom and JSON feeds.
	FeedTitle    string // The title of the Atom XML feed

	PlayEnabled bool
}

// Doc represents an article adorned with presentation data.
type Doc struct {
	*present.Doc
	Permalink string        // Canonical URL for this document.
	Path      string        // Path relative to server root (including base).
	HTML      template.HTML // rendered article

	Related      []*Doc
	Newer, Older *Doc
}

// Server implements an http.Handler that serves blog articles.
type Server struct {
	cfg      Config
	docs     []*Doc
	tags     []string
	docPaths map[string]*Doc // key is path without BasePath.
	docTags  map[string][]*Doc
	template struct {
		home, index, article, doc *template.Template
	}
	atomFeed []byte // pre-rendered Atom feed
	jsonFeed []byte // pre-rendered JSON feed
	content  http.Handler
}

// NewServer constructs a new Server using the specified config.
func NewServer(cfg Config) (*Server, error) {
	present.PlayEnabled = cfg.PlayEnabled

	root := filepath.Join(cfg.TemplatePath, "root.tmpl")
	parse := func(name string) (*template.Template, error) {
		t := template.New("").Funcs(funcMap)
		return t.ParseFiles(root, filepath.Join(cfg.TemplatePath, name))
	}

	s := &Server{cfg: cfg}

	// Parse templates.
	var err error
	s.template.home, err = parse("home.tmpl")
	if err != nil {
		return nil, err
	}
	s.template.index, err = parse("index.tmpl")
	if err != nil {
		return nil, err
	}
	s.template.article, err = parse("article.tmpl")
	if err != nil {
		return nil, err
	}
	p := present.Template().Funcs(funcMap)
	s.template.doc, err = p.ParseFiles(filepath.Join(cfg.TemplatePath, "doc.tmpl"))
	if err != nil {
		return nil, err
	}

	// Load content.
	err = s.loadDocs(filepath.Clean(cfg.ContentPath))
	if err != nil {
		return nil, err
	}

	err = s.renderAtomFeed()
	if err != nil {
		return nil, err
	}

	err = s.renderJSONFeed()
	if err != nil {
		return nil, err
	}

	// Set up content file server.
	s.content = http.FileServer(http.Dir(cfg.ContentPath))

	return s, nil
}

var funcMap = template.FuncMap{
	"sectioned": sectioned,
	"authors":   authors,
}

// sectioned returns true if the provided Doc contains more than one section.
// This is used to control whether to display the table of contents and headings.
func sectioned(d *present.Doc) bool {
	return len(d.Sections) > 1
}

// authors returns a comma-separated list of author names.
func authors(authors []present.Author) string {
	var b bytes.Buffer
	last := len(authors) - 1
	for i, a := range authors {
		if i > 0 {
			if i == last {
				b.WriteString(" and ")
			} else {
				b.WriteString(", ")
			}
		}
		b.WriteString(authorName(a))
	}
	return b.String()
}

// authorName returns the first line of the Author text: the author's name.
func authorName(a present.Author) string {
	el := a.TextElem()
	if len(el) == 0 {
		return ""
	}
	text, ok := el[0].(present.Text)
	if !ok || len(text.Lines) == 0 {
		return ""
	}
	return text.Lines[0]
}

// loadDocs reads all content from the provided file system root, renders all
// the articles it finds, adds them to the Server's docs field, computes the
// denormalized docPaths, docTags, and tags fields, and populates the various
// helper fields (Next, Previous, Related) for each Doc.
func (s *Server) loadDocs(root string) error {
	// Read content into docs field.
	const ext = ".article"
	fn := func(p string, info os.FileInfo, err error) error {
		if filepath.Ext(p) != ext {
			return nil
		}
		f, err := os.Open(p)
		if err != nil {
			return err
		}
		defer f.Close()
		d, err := present.Parse(f, p, 0)
		if err != nil {
			return err
		}
		html := new(bytes.Buffer)
		err = d.Render(html, s.template.doc)
		if err != nil {
			return err
		}
		p = p[len(root) : len(p)-len(ext)] // trim root and extension
		p = filepath.ToSlash(p)
		s.docs = append(s.docs, &Doc{
			Doc:       d,
			Path:      s.cfg.BasePath + p,
			Permalink: s.cfg.BaseURL + p,
			HTML:      template.HTML(html.String()),
		})
		return nil
	}
	err := filepath.Walk(root, fn)
	if err != nil {
		return err
	}
	sort.Sort(docsByTime(s.docs))

	// Pull out doc paths and tags and put in reverse-associating maps.
	s.docPaths = make(map[string]*Doc)
	s.docTags = make(map[string][]*Doc)
	for _, d := range s.docs {
		s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d
		for _, t := range d.Tags {
			s.docTags[t] = append(s.docTags[t], d)
		}
	}

	// Pull out unique sorted list of tags.
	for t := range s.docTags {
		s.tags = append(s.tags, t)
	}
	sort.Strings(s.tags)

	// Set up presentation-related fields, Newer, Older, and Related.
	for _, doc := range s.docs {
		// Newer, Older: docs adjacent to doc
		for i := range s.docs {
			if s.docs[i] != doc {
				continue
			}
			if i > 0 {
				doc.Newer = s.docs[i-1]
			}
			if i+1 < len(s.docs) {
				doc.Older = s.docs[i+1]
			}
			break
		}

		// Related: all docs that share tags with doc.
		related := make(map[*Doc]bool)
		for _, t := range doc.Tags {
			for _, d := range s.docTags[t] {
				if d != doc {
					related[d] = true
				}
			}
		}
		for d := range related {
			doc.Related = append(doc.Related, d)
		}
		sort.Sort(docsByTime(doc.Related))
	}

	return nil
}

// renderAtomFeed generates an XML Atom feed and stores it in the Server's
// atomFeed field.
func (s *Server) renderAtomFeed() error {
	var updated time.Time
	if len(s.docs) > 1 {
		updated = s.docs[0].Time
	}
	feed := atom.Feed{
		Title:   s.cfg.FeedTitle,
		ID:      "tag:" + s.cfg.Hostname + ",2013:" + s.cfg.Hostname,
		Updated: atom.Time(updated),
		Link: []atom.Link{{
			Rel:  "self",
			Href: s.cfg.BaseURL + "/feed.atom",
		}},
	}
	for i, doc := range s.docs {
		if i >= s.cfg.FeedArticles {
			break
		}
		e := &atom.Entry{
			Title: doc.Title,
			ID:    feed.ID + doc.Path,
			Link: []atom.Link{{
				Rel:  "alternate",
				Href: doc.Permalink,
			}},
			Published: atom.Time(doc.Time),
			Updated:   atom.Time(doc.Time),
			Summary: &atom.Text{
				Type: "html",
				Body: summary(doc),
			},
			Content: &atom.Text{
				Type: "html",
				Body: string(doc.HTML),
			},
			Author: &atom.Person{
				Name: authors(doc.Authors),
			},
		}
		feed.Entry = append(feed.Entry, e)
	}
	data, err := xml.Marshal(&feed)
	if err != nil {
		return err
	}
	s.atomFeed = data
	return nil
}

type jsonItem struct {
	Title   string
	Link    string
	Time    time.Time
	Summary string
	Content string
	Author  string
}

// renderJSONFeed generates a JSON feed and stores it in the Server's jsonFeed
// field.
func (s *Server) renderJSONFeed() error {
	var feed []jsonItem
	for i, doc := range s.docs {
		if i >= s.cfg.FeedArticles {
			break
		}
		item := jsonItem{
			Title:   doc.Title,
			Link:    doc.Permalink,
			Time:    doc.Time,
			Summary: summary(doc),
			Content: string(doc.HTML),
			Author:  authors(doc.Authors),
		}
		feed = append(feed, item)
	}
	data, err := json.Marshal(feed)
	if err != nil {
		return err
	}
	s.jsonFeed = data
	return nil
}

// summary returns the first paragraph of text from the provided Doc.
func summary(d *Doc) string {
	if len(d.Sections) == 0 {
		return ""
	}
	for _, elem := range d.Sections[0].Elem {
		text, ok := elem.(present.Text)
		if !ok || text.Pre {
			// skip everything but non-text elements
			continue
		}
		var buf bytes.Buffer
		for _, s := range text.Lines {
			buf.WriteString(string(present.Style(s)))
			buf.WriteByte('\n')
		}
		return buf.String()
	}
	return ""
}

// rootData encapsulates data destined for the root template.
type rootData struct {
	Doc      *Doc
	BasePath string
	GodocURL string
	Data     interface{}
}

// ServeHTTP serves the front, index, and article pages
// as well as the ATOM and JSON feeds.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var (
		d = rootData{BasePath: s.cfg.BasePath, GodocURL: s.cfg.GodocURL}
		t *template.Template
	)
	switch p := strings.TrimPrefix(r.URL.Path, s.cfg.BasePath); p {
	case "/":
		d.Data = s.docs
		if len(s.docs) > s.cfg.HomeArticles {
			d.Data = s.docs[:s.cfg.HomeArticles]
		}
		t = s.template.home
	case "/index":
		d.Data = s.docs
		t = s.template.index
	case "/feed.atom", "/feeds/posts/default":
		w.Header().Set("Content-type", "application/atom+xml; charset=utf-8")
		w.Write(s.atomFeed)
		return
	case "/.json":
		if p := r.FormValue("jsonp"); validJSONPFunc.MatchString(p) {
			w.Header().Set("Content-type", "application/javascript; charset=utf-8")
			fmt.Fprintf(w, "%v(%s)", p, s.jsonFeed)
			return
		}
		w.Header().Set("Content-type", "application/json; charset=utf-8")
		w.Write(s.jsonFeed)
		return
	default:
		doc, ok := s.docPaths[p]
		if !ok {
			// Not a doc; try to just serve static content.
			s.content.ServeHTTP(w, r)
			return
		}
		d.Doc = doc
		t = s.template.article
	}
	err := t.ExecuteTemplate(w, "root", d)
	if err != nil {
		log.Println(err)
	}
}

// docsByTime implements sort.Interface, sorting Docs by their Time field.
type docsByTime []*Doc

func (s docsByTime) Len() int           { return len(s) }
func (s docsByTime) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) }
<commit_msg>blog: Fix atom feed's `updated' time when there's only one article.<commit_after>// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package blog implements a web server for articles written in present format.
package blog

import (
	"bytes"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strings"
	"time"

	"code.google.com/p/go.tools/blog/atom"
	"code.google.com/p/go.tools/present"
)

var validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`)

// Config specifies Server configuration values.
type Config struct {
	ContentPath  string // Relative or absolute location of article files and related content.
	TemplatePath string // Relative or absolute location of template files.

	BaseURL  string // Absolute base URL (for permalinks; no trailing slash).
	BasePath string // Base URL path relative to server root (no trailing slash).
	GodocURL string // The base URL of godoc (for menu bar; no trailing slash).
	Hostname string // Server host name, used for rendering ATOM feeds.

	HomeArticles int    // Articles to display on the home page.
	FeedArticles int    // Articles to include in Atom and JSON feeds.
	FeedTitle    string // The title of the Atom XML feed

	PlayEnabled bool
}

// Doc represents an article adorned with presentation data.
type Doc struct {
	*present.Doc
	Permalink string        // Canonical URL for this document.
	Path      string        // Path relative to server root (including base).
	HTML      template.HTML // rendered article

	Related      []*Doc
	Newer, Older *Doc
}

// Server implements an http.Handler that serves blog articles.
type Server struct {
	cfg      Config
	docs     []*Doc
	tags     []string
	docPaths map[string]*Doc // key is path without BasePath.
	docTags  map[string][]*Doc
	template struct {
		home, index, article, doc *template.Template
	}
	atomFeed []byte // pre-rendered Atom feed
	jsonFeed []byte // pre-rendered JSON feed
	content  http.Handler
}

// NewServer constructs a new Server using the specified config.
func NewServer(cfg Config) (*Server, error) {
	present.PlayEnabled = cfg.PlayEnabled

	root := filepath.Join(cfg.TemplatePath, "root.tmpl")
	parse := func(name string) (*template.Template, error) {
		t := template.New("").Funcs(funcMap)
		return t.ParseFiles(root, filepath.Join(cfg.TemplatePath, name))
	}

	s := &Server{cfg: cfg}

	// Parse templates.
	var err error
	s.template.home, err = parse("home.tmpl")
	if err != nil {
		return nil, err
	}
	s.template.index, err = parse("index.tmpl")
	if err != nil {
		return nil, err
	}
	s.template.article, err = parse("article.tmpl")
	if err != nil {
		return nil, err
	}
	p := present.Template().Funcs(funcMap)
	s.template.doc, err = p.ParseFiles(filepath.Join(cfg.TemplatePath, "doc.tmpl"))
	if err != nil {
		return nil, err
	}

	// Load content.
	err = s.loadDocs(filepath.Clean(cfg.ContentPath))
	if err != nil {
		return nil, err
	}

	err = s.renderAtomFeed()
	if err != nil {
		return nil, err
	}

	err = s.renderJSONFeed()
	if err != nil {
		return nil, err
	}

	// Set up content file server.
	s.content = http.FileServer(http.Dir(cfg.ContentPath))

	return s, nil
}

var funcMap = template.FuncMap{
	"sectioned": sectioned,
	"authors":   authors,
}

// sectioned returns true if the provided Doc contains more than one section.
// This is used to control whether to display the table of contents and headings.
func sectioned(d *present.Doc) bool {
	return len(d.Sections) > 1
}

// authors returns a comma-separated list of author names.
func authors(authors []present.Author) string {
	var b bytes.Buffer
	last := len(authors) - 1
	for i, a := range authors {
		if i > 0 {
			if i == last {
				b.WriteString(" and ")
			} else {
				b.WriteString(", ")
			}
		}
		b.WriteString(authorName(a))
	}
	return b.String()
}

// authorName returns the first line of the Author text: the author's name.
func authorName(a present.Author) string {
	el := a.TextElem()
	if len(el) == 0 {
		return ""
	}
	text, ok := el[0].(present.Text)
	if !ok || len(text.Lines) == 0 {
		return ""
	}
	return text.Lines[0]
}

// loadDocs reads all content from the provided file system root, renders all
// the articles it finds, adds them to the Server's docs field, computes the
// denormalized docPaths, docTags, and tags fields, and populates the various
// helper fields (Next, Previous, Related) for each Doc.
func (s *Server) loadDocs(root string) error {
	// Read content into docs field.
	const ext = ".article"
	fn := func(p string, info os.FileInfo, err error) error {
		if filepath.Ext(p) != ext {
			return nil
		}
		f, err := os.Open(p)
		if err != nil {
			return err
		}
		defer f.Close()
		d, err := present.Parse(f, p, 0)
		if err != nil {
			return err
		}
		html := new(bytes.Buffer)
		err = d.Render(html, s.template.doc)
		if err != nil {
			return err
		}
		p = p[len(root) : len(p)-len(ext)] // trim root and extension
		p = filepath.ToSlash(p)
		s.docs = append(s.docs, &Doc{
			Doc:       d,
			Path:      s.cfg.BasePath + p,
			Permalink: s.cfg.BaseURL + p,
			HTML:      template.HTML(html.String()),
		})
		return nil
	}
	err := filepath.Walk(root, fn)
	if err != nil {
		return err
	}
	sort.Sort(docsByTime(s.docs))

	// Pull out doc paths and tags and put in reverse-associating maps.
	s.docPaths = make(map[string]*Doc)
	s.docTags = make(map[string][]*Doc)
	for _, d := range s.docs {
		s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d
		for _, t := range d.Tags {
			s.docTags[t] = append(s.docTags[t], d)
		}
	}

	// Pull out unique sorted list of tags.
	for t := range s.docTags {
		s.tags = append(s.tags, t)
	}
	sort.Strings(s.tags)

	// Set up presentation-related fields, Newer, Older, and Related.
	for _, doc := range s.docs {
		// Newer, Older: docs adjacent to doc
		for i := range s.docs {
			if s.docs[i] != doc {
				continue
			}
			if i > 0 {
				doc.Newer = s.docs[i-1]
			}
			if i+1 < len(s.docs) {
				doc.Older = s.docs[i+1]
			}
			break
		}

		// Related: all docs that share tags with doc.
		related := make(map[*Doc]bool)
		for _, t := range doc.Tags {
			for _, d := range s.docTags[t] {
				if d != doc {
					related[d] = true
				}
			}
		}
		for d := range related {
			doc.Related = append(doc.Related, d)
		}
		sort.Sort(docsByTime(doc.Related))
	}

	return nil
}

// renderAtomFeed generates an XML Atom feed and stores it in the Server's
// atomFeed field.
func (s *Server) renderAtomFeed() error {
	var updated time.Time
	if len(s.docs) > 0 {
		updated = s.docs[0].Time
	}
	feed := atom.Feed{
		Title:   s.cfg.FeedTitle,
		ID:      "tag:" + s.cfg.Hostname + ",2013:" + s.cfg.Hostname,
		Updated: atom.Time(updated),
		Link: []atom.Link{{
			Rel:  "self",
			Href: s.cfg.BaseURL + "/feed.atom",
		}},
	}
	for i, doc := range s.docs {
		if i >= s.cfg.FeedArticles {
			break
		}
		e := &atom.Entry{
			Title: doc.Title,
			ID:    feed.ID + doc.Path,
			Link: []atom.Link{{
				Rel:  "alternate",
				Href: doc.Permalink,
			}},
			Published: atom.Time(doc.Time),
			Updated:   atom.Time(doc.Time),
			Summary: &atom.Text{
				Type: "html",
				Body: summary(doc),
			},
			Content: &atom.Text{
				Type: "html",
				Body: string(doc.HTML),
			},
			Author: &atom.Person{
				Name: authors(doc.Authors),
			},
		}
		feed.Entry = append(feed.Entry, e)
	}
	data, err := xml.Marshal(&feed)
	if err != nil {
		return err
	}
	s.atomFeed = data
	return nil
}

type jsonItem struct {
	Title   string
	Link    string
	Time    time.Time
	Summary string
	Content string
	Author  string
}

// renderJSONFeed generates a JSON feed and stores it in the Server's jsonFeed
// field.
func (s *Server) renderJSONFeed() error {
	var feed []jsonItem
	for i, doc := range s.docs {
		if i >= s.cfg.FeedArticles {
			break
		}
		item := jsonItem{
			Title:   doc.Title,
			Link:    doc.Permalink,
			Time:    doc.Time,
			Summary: summary(doc),
			Content: string(doc.HTML),
			Author:  authors(doc.Authors),
		}
		feed = append(feed, item)
	}
	data, err := json.Marshal(feed)
	if err != nil {
		return err
	}
	s.jsonFeed = data
	return nil
}

// summary returns the first paragraph of text from the provided Doc.
func summary(d *Doc) string {
	if len(d.Sections) == 0 {
		return ""
	}
	for _, elem := range d.Sections[0].Elem {
		text, ok := elem.(present.Text)
		if !ok || text.Pre {
			// skip everything but non-text elements
			continue
		}
		var buf bytes.Buffer
		for _, s := range text.Lines {
			buf.WriteString(string(present.Style(s)))
			buf.WriteByte('\n')
		}
		return buf.String()
	}
	return ""
}

// rootData encapsulates data destined for the root template.
type rootData struct {
	Doc      *Doc
	BasePath string
	GodocURL string
	Data     interface{}
}

// ServeHTTP serves the front, index, and article pages
// as well as the ATOM and JSON feeds.
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var (
		d = rootData{BasePath: s.cfg.BasePath, GodocURL: s.cfg.GodocURL}
		t *template.Template
	)
	switch p := strings.TrimPrefix(r.URL.Path, s.cfg.BasePath); p {
	case "/":
		d.Data = s.docs
		if len(s.docs) > s.cfg.HomeArticles {
			d.Data = s.docs[:s.cfg.HomeArticles]
		}
		t = s.template.home
	case "/index":
		d.Data = s.docs
		t = s.template.index
	case "/feed.atom", "/feeds/posts/default":
		w.Header().Set("Content-type", "application/atom+xml; charset=utf-8")
		w.Write(s.atomFeed)
		return
	case "/.json":
		if p := r.FormValue("jsonp"); validJSONPFunc.MatchString(p) {
			w.Header().Set("Content-type", "application/javascript; charset=utf-8")
			fmt.Fprintf(w, "%v(%s)", p, s.jsonFeed)
			return
		}
		w.Header().Set("Content-type", "application/json; charset=utf-8")
		w.Write(s.jsonFeed)
		return
	default:
		doc, ok := s.docPaths[p]
		if !ok {
			// Not a doc; try to just serve static content.
			s.content.ServeHTTP(w, r)
			return
		}
		d.Doc = doc
		t = s.template.article
	}
	err := t.ExecuteTemplate(w, "root", d)
	if err != nil {
		log.Println(err)
	}
}

// docsByTime implements sort.Interface, sorting Docs by their Time field.
type docsByTime []*Doc

func (s docsByTime) Len() int           { return len(s) }
func (s docsByTime) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) }
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/nightlyone/lockfile"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = "master"
		}
		Errf("updating plugins... ")
		if err := node.UpdatePackages(); err != nil {
			panic(err)
		}
		Errln("done")
		manifest := getUpdateManifest(channel)
		build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
		Errf("updating to %s (%s)... ", manifest.Version, manifest.Channel)
		update(build.URL, build.Sha1)
		Errln("done")
	},
}

var binPath = filepath.Join(AppDir, "heroku-cli")
var updateLockPath = filepath.Join(AppDir, "updating.lock")
var autoupdateFile = filepath.Join(AppDir, "autoupdate")

func init() {
	if runtime.GOOS == "windows" {
		binPath = binPath + ".exe"
	}
}

// UpdateIfNeeded checks for and performs an autoupdate if there is a new version out.
func UpdateIfNeeded() {
	lock := getUpdateLock()
	if !updateNeeded() {
		lock.Unlock()
		return
	}
	doneUpdatingPlugins := make(chan bool)
	go func() {
		node.UpdatePackages()
		doneUpdatingPlugins <- true
	}()
	manifest := getUpdateManifest(Channel)
	if manifest.Version == Version {
		select {
		case <-time.After(time.Second * 30):
			Errln("Timed out while updating")
		case <-doneUpdatingPlugins:
		}
		touchAutoupdateFile()
		lock.Unlock()
		return
	}
	if !updatable() {
		Errf("Out of date: You are running %s but %s is out.\n", Version, manifest.Version)
		<-doneUpdatingPlugins
		lock.Unlock()
		return
	}
	// Leave out updating text until heroku-cli is used in place of ruby cli
	// So it doesn't confuse users with 2 different version numbers
	//Errf("Updating to %s... ", manifest.Version)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	update(build.URL, build.Sha1)
	select {
	case <-time.After(time.Second * 30):
		Errln("Timed out while updating")
	case <-doneUpdatingPlugins:
	}
	//Errln("done")

	// these are deferred but won't be called because of os.Exit
	touchAutoupdateFile()
	lock.Unlock()
	execBin()
	os.Exit(0)
}

// Attempts to get the lockfile
// Blocks if unavailable
func getUpdateLock() lockfile.Lockfile {
	lock, err := lockfile.New(updateLockPath)
	if err != nil {
		Errln("Cannot initialize update lockfile.")
		panic(err)
	}
	start := time.Now()
	for {
		if lock.TryLock() == nil {
			return lock
		}
		time.Sleep(10 * time.Millisecond)
		if time.Since(start) > 30*time.Second {
			// In a timeout, assume the last updating process timed out
			os.Remove(updateLockPath)
		}
	}
}

func updateNeeded() bool {
	if Version == "dev" {
		return false
	}
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	return time.Since(f.ModTime()) > 1*time.Hour
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) manifest {
	res, err := http.Get("http://d1gvo455cekpjp.cloudfront.net/" + channel + "/manifest.json")
	if err != nil {
		panic(err)
	}
	var m manifest
	json.NewDecoder(res.Body).Decode(&m)
	return m
}

func updatable() bool {
	path, err := filepath.Abs(os.Args[0])
	if err != nil {
		Errln(err)
	}
	return path == binPath
}

func update(url, sha1 string) {
	// on windows we can't remove an existing file or remove the running binary
	// so we download the file to binName.new
	// move the running binary to binName.old (deleting any existing file first)
	// rename the downloaded file to binName
	if err := downloadBin(binPath+".new", url); err != nil {
		panic(err)
	}
	if fileSha1(binPath+".new") != sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath + ".old")
	if err := os.Rename(binPath, binPath+".old"); err != nil {
		panic(err)
	}
	if err := os.Rename(binPath+".new", binPath); err != nil {
		panic(err)
	}
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	defer out.Close()
	client := &http.Client{}
	req, err := http.NewRequest("GET", url+".gz", nil)
	if err != nil {
		return err
	}
	req.Header.Add("Accept-Encoding", "gzip")
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	uncompressed, err := gzip.NewReader(resp.Body)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, uncompressed)
	return err
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

func execBin() {
	cmd := exec.Command(binPath, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()
}
<commit_msg>use ssl cloudfront url<commit_after>package main

import (
	"compress/gzip"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"github.com/nightlyone/lockfile"
)

var updateTopic = &Topic{
	Name:        "update",
	Description: "update heroku-cli",
}

var updateCmd = &Command{
	Topic:       "update",
	Description: "updates heroku-cli",
	Args:        []Arg{{Name: "channel", Optional: true}},
	Run: func(ctx *Context) {
		channel := ctx.Args.(map[string]string)["channel"]
		if channel == "" {
			channel = "master"
		}
		Errf("updating plugins... ")
		if err := node.UpdatePackages(); err != nil {
			panic(err)
		}
		Errln("done")
		manifest := getUpdateManifest(channel)
		build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
		Errf("updating to %s (%s)... ", manifest.Version, manifest.Channel)
		update(build.URL, build.Sha1)
		Errln("done")
	},
}

var binPath = filepath.Join(AppDir, "heroku-cli")
var updateLockPath = filepath.Join(AppDir, "updating.lock")
var autoupdateFile = filepath.Join(AppDir, "autoupdate")

func init() {
	if runtime.GOOS == "windows" {
		binPath = binPath + ".exe"
	}
}

// UpdateIfNeeded checks for and performs an autoupdate if there is a new version out.
func UpdateIfNeeded() {
	lock := getUpdateLock()
	if !updateNeeded() {
		lock.Unlock()
		return
	}
	doneUpdatingPlugins := make(chan bool)
	go func() {
		node.UpdatePackages()
		doneUpdatingPlugins <- true
	}()
	manifest := getUpdateManifest(Channel)
	if manifest.Version == Version {
		select {
		case <-time.After(time.Second * 30):
			Errln("Timed out while updating")
		case <-doneUpdatingPlugins:
		}
		touchAutoupdateFile()
		lock.Unlock()
		return
	}
	if !updatable() {
		Errf("Out of date: You are running %s but %s is out.\n", Version, manifest.Version)
		<-doneUpdatingPlugins
		lock.Unlock()
		return
	}
	// Leave out updating text until heroku-cli is used in place of ruby cli
	// So it doesn't confuse users with 2 different version numbers
	//Errf("Updating to %s... ", manifest.Version)
	build := manifest.Builds[runtime.GOOS][runtime.GOARCH]
	update(build.URL, build.Sha1)
	select {
	case <-time.After(time.Second * 30):
		Errln("Timed out while updating")
	case <-doneUpdatingPlugins:
	}
	//Errln("done")

	// these are deferred but won't be called because of os.Exit
	touchAutoupdateFile()
	lock.Unlock()
	execBin()
	os.Exit(0)
}

// Attempts to get the lockfile
// Blocks if unavailable
func getUpdateLock() lockfile.Lockfile {
	lock, err := lockfile.New(updateLockPath)
	if err != nil {
		Errln("Cannot initialize update lockfile.")
		panic(err)
	}
	start := time.Now()
	for {
		if lock.TryLock() == nil {
			return lock
		}
		time.Sleep(10 * time.Millisecond)
		if time.Since(start) > 30*time.Second {
			// In a timeout, assume the last updating process timed out
			os.Remove(updateLockPath)
		}
	}
}

func updateNeeded() bool {
	if Version == "dev" {
		return false
	}
	f, err := os.Stat(autoupdateFile)
	if err != nil {
		return true
	}
	return time.Since(f.ModTime()) > 1*time.Hour
}

func touchAutoupdateFile() {
	out, err := os.OpenFile(autoupdateFile, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		panic(err)
	}
	out.WriteString(time.Now().String())
}

type manifest struct {
	Channel, Version string
	Builds           map[string]map[string]struct {
		URL, Sha1 string
	}
}

func getUpdateManifest(channel string) manifest {
	res, err := http.Get("https://d1gvo455cekpjp.cloudfront.net/" + channel + "/manifest.json")
	if err != nil {
		panic(err)
	}
	var m manifest
	json.NewDecoder(res.Body).Decode(&m)
	return m
}

func updatable() bool {
	path, err := filepath.Abs(os.Args[0])
	if err != nil {
		Errln(err)
	}
	return path == binPath
}

func update(url, sha1 string) {
	// on windows we can't remove an existing file or remove the running binary
	// so we download the file to binName.new
	// move the running binary to binName.old (deleting any existing file first)
	// rename the downloaded file to binName
	if err := downloadBin(binPath+".new", url); err != nil {
		panic(err)
	}
	if fileSha1(binPath+".new") != sha1 {
		panic("SHA mismatch")
	}
	os.Remove(binPath + ".old")
	if err := os.Rename(binPath, binPath+".old"); err != nil {
		panic(err)
	}
	if err := os.Rename(binPath+".new", binPath); err != nil {
		panic(err)
	}
}

func downloadBin(path, url string) error {
	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
	if err != nil {
		return err
	}
	defer out.Close()
	client := &http.Client{}
	req, err := http.NewRequest("GET", url+".gz", nil)
	if err != nil {
		return err
	}
	req.Header.Add("Accept-Encoding", "gzip")
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	uncompressed, err := gzip.NewReader(resp.Body)
	if err != nil {
		return err
	}
	_, err = io.Copy(out, uncompressed)
	return err
}

func fileSha1(path string) string {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	return fmt.Sprintf("%x", sha1.Sum(data))
}

func execBin() {
	cmd := exec.Command(binPath, os.Args[1:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Run()
}
<|endoftext|>