<commit_before>package sarama

import (
	"fmt"
	"net"
	"os"
	"sync"
	"testing"
	"time"
)

const (
	TestBatchSize = 1000
)

var (
	kafkaIsAvailable, kafkaShouldBeAvailable bool
	kafkaAddr                                string
)

func init() {
	kafkaAddr = os.Getenv("KAFKA_ADDR")
	if kafkaAddr == "" {
		kafkaAddr = "localhost:6667"
	}

	c, err := net.Dial("tcp", kafkaAddr)
	if err == nil {
		kafkaIsAvailable = true
		c.Close()
	}

	kafkaShouldBeAvailable = os.Getenv("CI") != ""
}

func checkKafkaAvailability(t *testing.T) {
	if !kafkaIsAvailable {
		if kafkaShouldBeAvailable {
			t.Fatalf("Kafka broker is not available on %s. Set KAFKA_ADDR to connect to Kafka on a different location.", kafkaAddr)
		} else {
			t.Skipf("Kafka broker is not available on %s. Set KAFKA_ADDR to connect to Kafka on a different location.", kafkaAddr)
		}
	}
}

func TestFuncProducing(t *testing.T) {
	config := NewProducerConfig()
	testProducingMessages(t, config)
}

func TestFuncProducingGzip(t *testing.T) {
	config := NewProducerConfig()
	config.Compression = CompressionGZIP
	testProducingMessages(t, config)
}

func TestFuncProducingSnappy(t *testing.T) {
	config := NewProducerConfig()
	config.Compression = CompressionSnappy
	testProducingMessages(t, config)
}

func TestFuncProducingNoResponse(t *testing.T) {
	config := NewProducerConfig()
	config.RequiredAcks = NoResponse
	testProducingMessages(t, config)
}

func TestFuncProducingFlushing(t *testing.T) {
	config := NewProducerConfig()
	config.FlushMsgCount = TestBatchSize / 8
	config.FlushFrequency = 250 * time.Millisecond
	testProducingMessages(t, config)
}

func TestFuncMultiPartitionProduce(t *testing.T) {
	checkKafkaAvailability(t)
	client, err := NewClient("functional_test", []string{kafkaAddr}, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer safeClose(t, client)

	config := NewProducerConfig()
	config.FlushFrequency = 50 * time.Millisecond
	config.FlushMsgCount = 200
	config.ChannelBufferSize = 20
	config.AckSuccesses = true
	producer, err := NewProducer(client, config)
	if err != nil {
		t.Fatal(err)
	}

	var wg sync.WaitGroup
	wg.Add(TestBatchSize)

	for i := 1; i <= TestBatchSize; i++ {

		go func(i int, w *sync.WaitGroup) {
			defer w.Done()
			msg := &MessageToSend{Topic: "multi_partition", Key: nil, Value: StringEncoder(fmt.Sprintf("hur %d", i))}
			producer.Input() <- msg
			select {
			case ret := <-producer.Errors():
				t.Fatal(ret.Err)
			case <-producer.Successes():
			}
		}(i, &wg)
	}

	wg.Wait()
	if err := producer.Close(); err != nil {
		t.Error(err)
	}
}

func testProducingMessages(t *testing.T, config *ProducerConfig) {
	checkKafkaAvailability(t)

	client, err := NewClient("functional_test", []string{kafkaAddr}, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer safeClose(t, client)

	consumerConfig := NewConsumerConfig()
	consumerConfig.OffsetMethod = OffsetMethodNewest

	consumer, err := NewConsumer(client, "single_partition", 0, "functional_test", consumerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer safeClose(t, consumer)

	config.AckSuccesses = true
	producer, err := NewProducer(client, config)
	if err != nil {
		t.Fatal(err)
	}

	expectedResponses := TestBatchSize
	for i := 1; i <= TestBatchSize; {
		msg := &MessageToSend{Topic: "single_partition", Key: nil, Value: StringEncoder(fmt.Sprintf("testing %d", i))}
		select {
		case producer.Input() <- msg:
			i++
		case ret := <-producer.Errors():
			t.Fatal(ret.Err)
		case <-producer.Successes():
			expectedResponses--
		}
	}
	for expectedResponses > 0 {
		select {
		case ret := <-producer.Errors():
			t.Fatal(ret.Err)
		case <-producer.Successes():
			expectedResponses--
		}
	}
	err = producer.Close()
	if err != nil {
		t.Error(err)
	}

	events := consumer.Events()
	for i := 1; i <= TestBatchSize; i++ {
		select {
		case <-time.After(10 * time.Second):
			t.Fatal("Not received any more events in the last 10 seconds.")

		case event := <-events:
			if string(event.Value) != fmt.Sprintf("testing %d", i) {
				t.Fatalf("Unexpected message with index %d: %s", i, event.Value)
			}
		}

	}
}
<commit_msg>Add a test to make sure connection to a cluster that is completely down wil return an error<commit_after>package sarama

import (
	"fmt"
	"net"
	"os"
	"sync"
	"testing"
	"time"
)

const (
	TestBatchSize = 1000
)

var (
	kafkaIsAvailable, kafkaShouldBeAvailable bool
	kafkaAddr                                string
)

func init() {
	kafkaAddr = os.Getenv("KAFKA_ADDR")
	if kafkaAddr == "" {
		kafkaAddr = "localhost:6667"
	}

	c, err := net.Dial("tcp", kafkaAddr)
	if err == nil {
		kafkaIsAvailable = true
		c.Close()
	}

	kafkaShouldBeAvailable = os.Getenv("CI") != ""
}

func checkKafkaAvailability(t *testing.T) {
	if !kafkaIsAvailable {
		if kafkaShouldBeAvailable {
			t.Fatalf("Kafka broker is not available on %s. Set KAFKA_ADDR to connect to Kafka on a different location.", kafkaAddr)
		} else {
			t.Skipf("Kafka broker is not available on %s. Set KAFKA_ADDR to connect to Kafka on a different location.", kafkaAddr)
		}
	}
}

func TestFuncConnectionFailure(t *testing.T) {
	config := NewClientConfig()
	config.MetadataRetries = 1

	_, err := NewClient("test", []string{"localhost:9000"}, config)
	if err != OutOfBrokers {
		t.Fatal("Expected returned error to be OutOfBrokers, but was: ", err)
	}
}

func TestFuncProducing(t *testing.T) {
	config := NewProducerConfig()
	testProducingMessages(t, config)
}

func TestFuncProducingGzip(t *testing.T) {
	config := NewProducerConfig()
	config.Compression = CompressionGZIP
	testProducingMessages(t, config)
}

func TestFuncProducingSnappy(t *testing.T) {
	config := NewProducerConfig()
	config.Compression = CompressionSnappy
	testProducingMessages(t, config)
}

func TestFuncProducingNoResponse(t *testing.T) {
	config := NewProducerConfig()
	config.RequiredAcks = NoResponse
	testProducingMessages(t, config)
}

func TestFuncProducingFlushing(t *testing.T) {
	config := NewProducerConfig()
	config.FlushMsgCount = TestBatchSize / 8
	config.FlushFrequency = 250 * time.Millisecond
	testProducingMessages(t, config)
}

func TestFuncMultiPartitionProduce(t *testing.T) {
	checkKafkaAvailability(t)
	client, err := NewClient("functional_test", []string{kafkaAddr}, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer safeClose(t, client)

	config := NewProducerConfig()
	config.FlushFrequency = 50 * time.Millisecond
	config.FlushMsgCount = 200
	config.ChannelBufferSize = 20
	config.AckSuccesses = true
	producer, err := NewProducer(client, config)
	if err != nil {
		t.Fatal(err)
	}

	var wg sync.WaitGroup
	wg.Add(TestBatchSize)

	for i := 1; i <= TestBatchSize; i++ {

		go func(i int, w *sync.WaitGroup) {
			defer w.Done()
			msg := &MessageToSend{Topic: "multi_partition", Key: nil, Value: StringEncoder(fmt.Sprintf("hur %d", i))}
			producer.Input() <- msg
			select {
			case ret := <-producer.Errors():
				t.Fatal(ret.Err)
			case <-producer.Successes():
			}
		}(i, &wg)
	}

	wg.Wait()
	if err := producer.Close(); err != nil {
		t.Error(err)
	}
}

func testProducingMessages(t *testing.T, config *ProducerConfig) {
	checkKafkaAvailability(t)

	client, err := NewClient("functional_test", []string{kafkaAddr}, nil)
	if err != nil {
		t.Fatal(err)
	}
	defer safeClose(t, client)

	consumerConfig := NewConsumerConfig()
	consumerConfig.OffsetMethod = OffsetMethodNewest

	consumer, err := NewConsumer(client, "single_partition", 0, "functional_test", consumerConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer safeClose(t, consumer)

	config.AckSuccesses = true
	producer, err := NewProducer(client, config)
	if err != nil {
		t.Fatal(err)
	}

	expectedResponses := TestBatchSize
	for i := 1; i <= TestBatchSize; {
		msg := &MessageToSend{Topic: "single_partition", Key: nil, Value: StringEncoder(fmt.Sprintf("testing %d", i))}
		select {
		case producer.Input() <- msg:
			i++
		case ret := <-producer.Errors():
			t.Fatal(ret.Err)
		case <-producer.Successes():
			expectedResponses--
		}
	}
	for expectedResponses > 0 {
		select {
		case ret := <-producer.Errors():
			t.Fatal(ret.Err)
		case <-producer.Successes():
			expectedResponses--
		}
	}
	err = producer.Close()
	if err != nil {
		t.Error(err)
	}

	events := consumer.Events()
	for i := 1; i <= TestBatchSize; i++ {
		select {
		case <-time.After(10 * time.Second):
			t.Fatal("Not received any more events in the last 10 seconds.")

		case event := <-events:
			if string(event.Value) != fmt.Sprintf("testing %d", i) {
				t.Fatalf("Unexpected message with index %d: %s", i, event.Value)
			}
		}

	}
}
<|endoftext|>


<commit_before>// HTTP server that uses OAuth to create security.PrivateID objects.
package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"

	"veyron/services/identity/googleoauth"
	"veyron/services/identity/handlers"
	"veyron/services/identity/util"
	"veyron2/rt"
	"veyron2/security"
	"veyron2/vlog"
)

var (
	port          = flag.Int("port", 8125, "Port number on which the HTTP server listens on.")
	host          = flag.String("host", defaultHost(), "Hostname the HTTP server listens on. This can be the name of the host running the webserver, but if running behind a NAT or load balancer, this should be the host name that clients will connect to. For example, if set to 'x.com', Veyron identities will have the IssuerName set to 'x.com' and clients can expect to find the public key of the signer at 'x.com/pubkey/'.")
	tlsconfig     = flag.String("tlsconfig", "", "Comma-separated list of TLS certificate and private key files. If empty, will not use HTTPS.")
	minExpiryDays = flag.Int("min_expiry_days", 365, "Minimum expiry time (in days) of identities issued by this server")
	googleConfig  = flag.String("google_config", "", "Path to the JSON-encoded file containing the ClientID for web applications registered with the Google Developer Console. (Use the 'Download JSON' link on the Google APIs console).")

	generate = flag.String("generate", "", "If non-empty, instead of running an HTTP server, a new identity will be created with the provided name and saved to --identity (if specified) and dumped to STDOUT in base64-encoded-vom")
	identity = flag.String("identity", "", "Path to the file where the VOM-encoded security.PrivateID created with --generate will be written.")
)

func main() {
	// Setup flags and logging
	flag.Usage = usage
	r := rt.Init()
	defer r.Shutdown()

	if len(*generate) > 0 {
		generateAndSaveIdentity()
		return
	}

	// Setup handlers
	http.HandleFunc("/", handleMain)
	http.Handle("/pubkey/", handlers.Object{r.Identity().PublicID().PublicKey()}) // public key of this identity server
	http.Handle("/random/", handlers.Random{r})                                   // mint identities with a random name
	http.HandleFunc("/bless/", handlers.Bless)                                    // use a provided PrivateID to bless a provided PublicID
	// Google OAuth
	if enableGoogleOAuth() {
		f, err := os.Open(*googleConfig)
		if err != nil {
			vlog.Fatalf("Failed to open %q: %v", *googleConfig, err)
		}
		clientid, secret, err := googleoauth.ClientIDAndSecretFromJSON(f)
		if err != nil {
			vlog.Fatalf("Failed to decode %q: %v", *googleConfig, err)
		}
		f.Close()
		n := "/google/"
		http.Handle(n, googleoauth.NewHandler(googleoauth.HandlerArgs{
			UseTLS:        enableTLS(),
			Addr:          fmt.Sprintf("%s:%d", *host, *port),
			Prefix:        n,
			ClientID:      clientid,
			ClientSecret:  secret,
			MinExpiryDays: *minExpiryDays,
			Runtime:       r,
		}))
	}
	startHTTPServer(*port)
}

func enableTLS() bool         { return len(*tlsconfig) > 0 }
func enableGoogleOAuth() bool { return len(*googleConfig) > 0 }

func startHTTPServer(port int) {
	addr := fmt.Sprintf(":%d", port)
	if !enableTLS() {
		vlog.Infof("Starting HTTP server (without TLS) at http://%v", addr)
		if err := http.ListenAndServe(addr, nil); err != nil {
			vlog.Fatalf("http.ListenAndServe failed: %v", err)
		}
		return
	}
	paths := strings.Split(*tlsconfig, ",")
	if len(paths) != 2 {
		vlog.Fatalf("Could not parse --tlsconfig. Must have exactly two components, separated by a comma")
	}
	vlog.Infof("Starting HTTP server with TLS using certificate [%s] and private key [%s] at https://%s", paths[0], paths[1], addr)
	if err := http.ListenAndServeTLS(addr, paths[0], paths[1], nil); err != nil {
		vlog.Fatalf("http.ListenAndServeTLS failed: %v", err)
	}
}

func usage() {
	fmt.Fprintf(os.Stderr, `%s starts an HTTP server that mints veyron identities in response to GET requests.

To generate TLS certificates so the HTTP server can use SSL:
go run $GOROOT/src/pkg/crypto/tls/generate_cert.go --host <IP address>

To enable use of Google APIs to use Google OAuth for authorization, set --google_config,
which must point to the contents of a JSON file obtained after registering your application
with the Google Developer Console at:
https://code.google.com/apis/console
More details on Google OAuth at:
https://developers.google.com/accounts/docs/OAuth2Login

Flags:
`, os.Args[0])
	flag.PrintDefaults()
}

func defaultHost() string {
	host, err := os.Hostname()
	if err != nil {
		vlog.Fatalf("Failed to get hostname: %v", err)
	}
	return host
}

func handleMain(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(`
<!doctype html>

<meta charset="UTF-8">
<title>Veyron Identity Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">

Veyron Identity Generation

This HTTP server mints veyron identities. The public key of the identity of this server is available in
base64-encoded-vom-encoded format.

`))
	if enableGoogleOAuth() {
		w.Write([]byte(`<a class="btn btn-lg btn-primary" href="/google/auth">Google</a> `))
	}
	w.Write([]byte(`Random
Bless As

`))
}

func generateAndSaveIdentity() {
	id, err := rt.R().NewIdentity(*generate)
	if err != nil {
		vlog.Fatalf("Runtime.NewIdentity(%q) failed: %v", *generate, err)
	}
	if len(*identity) > 0 {
		if err = saveIdentity(*identity, id); err != nil {
			vlog.Fatalf("SaveIdentity %v: %v", *identity, err)
		}
	}
	b64, err := util.Base64VomEncode(id)
	if err != nil {
		vlog.Fatalf("Base64VomEncode(%q) failed: %v", id, err)
	}
	fmt.Println(b64)
}

func saveIdentity(filePath string, id security.PrivateID) error {
	f, err := os.OpenFile(filePath, os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := security.SaveIdentity(f, id); err != nil {
		return err
	}
	return nil
}
<commit_msg>Squashed commit of the following:<commit_after>// HTTP server that uses OAuth to create security.PrivateID objects.
package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"

	"veyron/services/identity/googleoauth"
	"veyron/services/identity/handlers"
	"veyron/services/identity/util"
	"veyron2/rt"
	"veyron2/security"
	"veyron2/vlog"
)

var (
	port          = flag.Int("port", 8125, "Port number on which the HTTP server listens on.")
	host          = flag.String("host", defaultHost(), "Hostname the HTTP server listens on. This can be the name of the host running the webserver, but if running behind a NAT or load balancer, this should be the host name that clients will connect to. For example, if set to 'x.com', Veyron identities will have the IssuerName set to 'x.com' and clients can expect to find the public key of the signer at 'x.com/pubkey/'.")
	tlsconfig     = flag.String("tlsconfig", "", "Comma-separated list of TLS certificate and private key files. If empty, will not use HTTPS.")
	minExpiryDays = flag.Int("min_expiry_days", 365, "Minimum expiry time (in days) of identities issued by this server")
	googleConfig  = flag.String("google_config", "", "Path to the JSON-encoded file containing the ClientID for web applications registered with the Google Developer Console. (Use the 'Download JSON' link on the Google APIs console).")

	generate = flag.String("generate", "", "If non-empty, instead of running an HTTP server, a new identity will be created with the provided name and saved to --identity (if specified) and dumped to STDOUT in base64-encoded-vom")
	identity = flag.String("identity", "", "Path to the file where the VOM-encoded security.PrivateID created with --generate will be written.")
)

func main() {
	// Setup flags and logging
	flag.Usage = usage
	r := rt.Init()
	defer r.Shutdown()

	if len(*generate) > 0 {
		generateAndSaveIdentity()
		return
	}

	// Setup handlers
	http.HandleFunc("/", handleMain)
	http.Handle("/pubkey/", handlers.Object{r.Identity().PublicID().PublicKey()}) // public key of this identity server
	if enableRandomHandler() {
		http.Handle("/random/", handlers.Random{r}) // mint identities with a random name
	}
	http.HandleFunc("/bless/", handlers.Bless) // use a provided PrivateID to bless a provided PublicID
	// Google OAuth
	if enableGoogleOAuth() {
		f, err := os.Open(*googleConfig)
		if err != nil {
			vlog.Fatalf("Failed to open %q: %v", *googleConfig, err)
		}
		clientid, secret, err := googleoauth.ClientIDAndSecretFromJSON(f)
		if err != nil {
			vlog.Fatalf("Failed to decode %q: %v", *googleConfig, err)
		}
		f.Close()
		n := "/google/"
		http.Handle(n, googleoauth.NewHandler(googleoauth.HandlerArgs{
			UseTLS:        enableTLS(),
			Addr:          fmt.Sprintf("%s:%d", *host, *port),
			Prefix:        n,
			ClientID:      clientid,
			ClientSecret:  secret,
			MinExpiryDays: *minExpiryDays,
			Runtime:       r,
		}))
	}
	startHTTPServer(*port)
}

func enableTLS() bool           { return len(*tlsconfig) > 0 }
func enableGoogleOAuth() bool   { return len(*googleConfig) > 0 }
func enableRandomHandler() bool { return !enableGoogleOAuth() }

func startHTTPServer(port int) {
	addr := fmt.Sprintf(":%d", port)
	if !enableTLS() {
		vlog.Infof("Starting HTTP server (without TLS) at http://%v", addr)
		if err := http.ListenAndServe(addr, nil); err != nil {
			vlog.Fatalf("http.ListenAndServe failed: %v", err)
		}
		return
	}
	paths := strings.Split(*tlsconfig, ",")
	if len(paths) != 2 {
		vlog.Fatalf("Could not parse --tlsconfig. Must have exactly two components, separated by a comma")
	}
	vlog.Infof("Starting HTTP server with TLS using certificate [%s] and private key [%s] at https://%s", paths[0], paths[1], addr)
	if err := http.ListenAndServeTLS(addr, paths[0], paths[1], nil); err != nil {
		vlog.Fatalf("http.ListenAndServeTLS failed: %v", err)
	}
}

func usage() {
	fmt.Fprintf(os.Stderr, `%s starts an HTTP server that mints veyron identities in response to GET requests.

To generate TLS certificates so the HTTP server can use SSL:
go run $GOROOT/src/pkg/crypto/tls/generate_cert.go --host <IP address>

To enable use of Google APIs to use Google OAuth for authorization, set --google_config,
which must point to the contents of a JSON file obtained after registering your application
with the Google Developer Console at:
https://code.google.com/apis/console
More details on Google OAuth at:
https://developers.google.com/accounts/docs/OAuth2Login

Flags:
`, os.Args[0])
	flag.PrintDefaults()
}

func defaultHost() string {
	host, err := os.Hostname()
	if err != nil {
		vlog.Fatalf("Failed to get hostname: %v", err)
	}
	return host
}

func handleMain(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(`
<!doctype html>

<meta charset="UTF-8">
<title>Veyron Identity Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">

Veyron Identity Generation

This HTTP server mints veyron identities. The public key of the identity of this server is available in
base64-encoded-vom-encoded format.

`))
	if enableGoogleOAuth() {
		w.Write([]byte(`<a class="btn btn-lg btn-primary" href="/google/auth">Google</a> `))
	}
	if enableRandomHandler() {
		w.Write([]byte(`<a class="btn btn-lg btn-primary" href="/random/">Random</a> `))
	}
	w.Write([]byte(`Bless As

`))
}

func generateAndSaveIdentity() {
	id, err := rt.R().NewIdentity(*generate)
	if err != nil {
		vlog.Fatalf("Runtime.NewIdentity(%q) failed: %v", *generate, err)
	}
	if len(*identity) > 0 {
		if err = saveIdentity(*identity, id); err != nil {
			vlog.Fatalf("SaveIdentity %v: %v", *identity, err)
		}
	}
	b64, err := util.Base64VomEncode(id)
	if err != nil {
		vlog.Fatalf("Base64VomEncode(%q) failed: %v", id, err)
	}
	fmt.Println(b64)
}

func saveIdentity(filePath string, id security.PrivateID) error {
	f, err := os.OpenFile(filePath, os.O_WRONLY, 0600)
	if err != nil {
		return err
	}
	defer f.Close()
	if err := security.SaveIdentity(f, id); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 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.

package fuseops

import (
	"time"

	"golang.org/x/net/context"

	"github.com/jacobsa/bazilfuse"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied bazilfuse request struct to an Op. finished will be
// called with the error supplied to o.Respond when the user invokes that
// method, before a response is sent to the kernel.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
func Convert(
	opCtx context.Context,
	r bazilfuse.Request,
	logForOp func(int, string, ...interface{}),
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch typed := r.(type) {
	case *bazilfuse.InitRequest:
		to := &InitOp{
			maxReadahead: typed.MaxReadahead,
		}

		io = to
		co = &to.commonOp

	case *bazilfuse.LookupRequest:
		to := &LookUpInodeOp{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.GetattrRequest:
		to := &GetInodeAttributesOp{
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.SetattrRequest:
		to := &SetInodeAttributesOp{
			Inode: InodeID(typed.Header.Node),
		}

		if typed.Valid&bazilfuse.SetattrSize != 0 {
			to.Size = &typed.Size
		}

		if typed.Valid&bazilfuse.SetattrMode != 0 {
			to.Mode = &typed.Mode
		}

		if typed.Valid&bazilfuse.SetattrAtime != 0 {
			to.Atime = &typed.Atime
		}

		if typed.Valid&bazilfuse.SetattrMtime != 0 {
			to.Mtime = &typed.Mtime
		}

		io = to
		co = &to.commonOp

	case *bazilfuse.ForgetRequest:
		to := &ForgetInodeOp{
			Inode: InodeID(typed.Header.Node),
			N:     typed.N,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.MkdirRequest:
		to := &MkDirOp{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.CreateRequest:
		to := &CreateFileOp{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
			Flags:  typed.Flags,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.RemoveRequest:
		if typed.Dir {
			to := &RmDirOp{
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &UnlinkOp{
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.OpenRequest:
		if typed.Dir {
			to := &OpenDirOp{
				Inode: InodeID(typed.Header.Node),
				Flags: typed.Flags,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &OpenFileOp{
				Inode: InodeID(typed.Header.Node),
				Flags: typed.Flags,
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.ReadRequest:
		if typed.Dir {
			to := &ReadDirOp{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReadFileOp{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.ReleaseRequest:
		if typed.Dir {
			to := &ReleaseDirHandleOp{
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReleaseFileHandleOp{
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.WriteRequest:
		to := &WriteFileOp{
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.FsyncRequest:
		// We don't currently support this for directories.
		if typed.Dir {
			to := &unknownOp{}
			io = to
			co = &to.commonOp
		} else {
			to := &SyncFileOp{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.FlushRequest:
		to := &FlushFileOp{
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(opCtx, io, r, logForOp, finished)

	o = io
	return
}

func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
	return bazilfuse.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *bazilfuse.LookupResponse) {
	out.Node = bazilfuse.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes)
	out.AttrValid = convertExpirationTime(in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<commit_msg>Added Convert support.<commit_after>// Copyright 2015 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.

package fuseops

import (
	"time"

	"golang.org/x/net/context"

	"github.com/jacobsa/bazilfuse"
)

// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied bazilfuse request struct to an Op. finished will be
// called with the error supplied to o.Respond when the user invokes that
// method, before a response is sent to the kernel.
//
// It is guaranteed that o != nil. If the op is unknown, a special unexported
// type will be used.
func Convert(
	opCtx context.Context,
	r bazilfuse.Request,
	logForOp func(int, string, ...interface{}),
	finished func(error)) (o Op) {
	var co *commonOp

	var io internalOp
	switch typed := r.(type) {
	case *bazilfuse.InitRequest:
		to := &InitOp{
			maxReadahead: typed.MaxReadahead,
		}

		io = to
		co = &to.commonOp

	case *bazilfuse.LookupRequest:
		to := &LookUpInodeOp{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.GetattrRequest:
		to := &GetInodeAttributesOp{
			Inode: InodeID(typed.Header.Node),
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.SetattrRequest:
		to := &SetInodeAttributesOp{
			Inode: InodeID(typed.Header.Node),
		}

		if typed.Valid&bazilfuse.SetattrSize != 0 {
			to.Size = &typed.Size
		}

		if typed.Valid&bazilfuse.SetattrMode != 0 {
			to.Mode = &typed.Mode
		}

		if typed.Valid&bazilfuse.SetattrAtime != 0 {
			to.Atime = &typed.Atime
		}

		if typed.Valid&bazilfuse.SetattrMtime != 0 {
			to.Mtime = &typed.Mtime
		}

		io = to
		co = &to.commonOp

	case *bazilfuse.ForgetRequest:
		to := &ForgetInodeOp{
			Inode: InodeID(typed.Header.Node),
			N:     typed.N,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.MkdirRequest:
		to := &MkDirOp{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.CreateRequest:
		to := &CreateFileOp{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.Name,
			Mode:   typed.Mode,
			Flags:  typed.Flags,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.SymlinkRequest:
		to := &CreateSymlinkOp{
			Parent: InodeID(typed.Header.Node),
			Name:   typed.NewName,
			Target: typed.Target,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.RemoveRequest:
		if typed.Dir {
			to := &RmDirOp{
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &UnlinkOp{
				Parent: InodeID(typed.Header.Node),
				Name:   typed.Name,
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.OpenRequest:
		if typed.Dir {
			to := &OpenDirOp{
				Inode: InodeID(typed.Header.Node),
				Flags: typed.Flags,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &OpenFileOp{
				Inode: InodeID(typed.Header.Node),
				Flags: typed.Flags,
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.ReadRequest:
		if typed.Dir {
			to := &ReadDirOp{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: DirOffset(typed.Offset),
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReadFileOp{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
				Offset: typed.Offset,
				Size:   typed.Size,
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.ReleaseRequest:
		if typed.Dir {
			to := &ReleaseDirHandleOp{
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		} else {
			to := &ReleaseFileHandleOp{
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.WriteRequest:
		to := &WriteFileOp{
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
			Data:   typed.Data,
			Offset: typed.Offset,
		}
		io = to
		co = &to.commonOp

	case *bazilfuse.FsyncRequest:
		// We don't currently support this for directories.
		if typed.Dir {
			to := &unknownOp{}
			io = to
			co = &to.commonOp
		} else {
			to := &SyncFileOp{
				Inode:  InodeID(typed.Header.Node),
				Handle: HandleID(typed.Handle),
			}
			io = to
			co = &to.commonOp
		}

	case *bazilfuse.FlushRequest:
		to := &FlushFileOp{
			Inode:  InodeID(typed.Header.Node),
			Handle: HandleID(typed.Handle),
		}
		io = to
		co = &to.commonOp

	default:
		to := &unknownOp{}
		io = to
		co = &to.commonOp
	}

	co.init(opCtx, io, r, logForOp, finished)

	o = io
	return
}

func convertAttributes(inode InodeID, attr InodeAttributes) bazilfuse.Attr {
	return bazilfuse.Attr{
		Inode:  uint64(inode),
		Size:   attr.Size,
		Mode:   attr.Mode,
		Nlink:  uint32(attr.Nlink),
		Atime:  attr.Atime,
		Mtime:  attr.Mtime,
		Ctime:  attr.Ctime,
		Crtime: attr.Crtime,
		Uid:    attr.Uid,
		Gid:    attr.Gid,
	}
}

// Convert an absolute cache expiration time to a relative time from now for
// consumption by fuse.
func convertExpirationTime(t time.Time) (d time.Duration) {
	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
	// counts of nanoseconds (cf. http://goo.gl/EJupJV). The bazil.org/fuse
	// package converts time.Duration values to this form in a straightforward
	// way (cf. http://goo.gl/FJhV8j).
	//
	// So negative durations are right out. There is no need to cap the positive
	// magnitude, because 2^64 seconds is well longer than the 2^63 ns range of
	// time.Duration.
	d = t.Sub(time.Now())
	if d < 0 {
		d = 0
	}

	return
}

func convertChildInodeEntry(
	in *ChildInodeEntry,
	out *bazilfuse.LookupResponse) {
	out.Node = bazilfuse.NodeID(in.Child)
	out.Generation = uint64(in.Generation)
	out.Attr = convertAttributes(in.Child, in.Attributes)
	out.AttrValid = convertExpirationTime(in.AttributesExpiration)
	out.EntryValid = convertExpirationTime(in.EntryExpiration)
}
<|endoftext|>


<commit_before>package subsonic

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/mdlayher/wavepipe/api"
	"github.com/mdlayher/wavepipe/data"
)

const (
	// XMLName is the top-level name of a Subsonic XML response
	XMLName = "subsonic-response"
	// XMLNS is the XML namespace of a Subsonic XML response
	XMLNS = "http://subsonic.org/restapi"
	// Version is the emulated Subsonic API version
	Version = "1.8.0"
)

var (
	// ErrBadCredentials returns a bad credentials response
	ErrBadCredentials = func() *Container {
		// Generate new container with failed status
		c := newContainer()
		c.Status = "failed"

		// Return error
		c.SubError = &Error{Code: 40, Message: "Wrong username or password."}
		return c
	}()
	// ErrMissingParameter returns a missing required parameter response
	ErrMissingParameter = func() *Container {
		// Generate new container with failed status
		c := newContainer()
		c.Status = "failed"

		// Return error
		c.SubError = &Error{Code: 10, Message: "Required parameter is missing."}
		return c
	}()
)

// Container is the top-level emulated Subsonic response
type Container struct {
	// Top-level container name
	XMLName xml.Name `xml:"subsonic-response"`

	// Attributes which are always present
	XMLNS   string `xml:"xmlns,attr"`
	Status  string `xml:"status,attr"`
	Version string `xml:"version,attr"`

	// Error, returned on failures
	SubError *Error

	// Nested data

	// getAlbum.view
	Album []Album `xml:"album"`

	// getAlbumList2.view
	AlbumList2 *AlbumList2Container
}

// Error returns the error code and message from Subsonic, and enables Subsonic
// errors to be returned in authentication
func (c Container) Error() string {
	return fmt.Sprintf("%d: %s", c.SubError.Code, c.SubError.Message)
}

// Error contains a Subsonic error, with status code and message
type Error struct {
	XMLName xml.Name `xml:"error,omitempty"`

	Code    int    `xml:"code,attr"`
	Message string `xml:"message,attr"`
}

// AlbumList2Container contains a list of emulated Subsonic albums, by tags
type AlbumList2Container struct {
	// Container name
	XMLName xml.Name `xml:"albumList2,omitempty"`

	// Albums
	Albums []Album `xml:"album"`
}

// Album represents an emulated Subsonic album
type Album struct {
	// Subsonic fields
	ID        int    `xml:"id,attr"`
	Name      string `xml:"name,attr"`
	Artist    string `xml:"artist,attr"`
	ArtistID  int    `xml:"artistId,attr"`
	CoverArt  string `xml:"coverArt,attr"`
	SongCount int    `xml:"songCount,attr"`
	Duration  int    `xml:"duration,attr"`
	Created   string `xml:"created,attr"`

	// Nested data

	// getAlbum.view
	Songs []Song `xml:"song"`
}

// newContainer creates a new, empty Container with the proper attributes
func newContainer() *Container {
	return &Container{
		XMLNS:   XMLNS,
		Status:  "ok",
		Version: Version,
	}
}

// GetPing is used in Subsonic to check server connectivity
func GetPing(res http.ResponseWriter) {
	// All Subsonic emulation replies are XML
	res.Header().Set("Content-Type", "text/xml")

	// Marshal empty container to XML
	out, err := xml.Marshal(newContainer())
	if err != nil {
		return
	}

	// Remove closing tag, replace with self-closing tag (needed by Android client)
	res.Write(bytes.Replace(out, []byte("></"+XMLName+">"), []byte("/>"), -1))
}

// GetAlbumList2 is used in Subsonic to return a list of albums organized with tags
func GetAlbumList2(req *http.Request, res http.ResponseWriter) {
	// All Subsonic emulation replies are XML
	res.Header().Set("Content-Type", "text/xml")

	// Create a new response container
	c := newContainer()

	// Fetch all albums
	// TODO: add a LimitAlbums method to fetch subsets
	albums, err := data.DB.AllAlbums()
	if err != nil {
		log.Println(err)
		return
	}

	// If offset is past albums count, stop sending albums
	qOffset := req.URL.Query().Get("offset")
	if qOffset != "" {
		// Parse offset
		offset, err := strconv.Atoi(qOffset)
		if err != nil {
			log.Println(err)
			return
		}

		// Check if offset is greater than count
		if offset > len(albums) {
			// Empty albums list
			c.AlbumList2 = new(AlbumList2Container)

			// Marshal container to XML
			out, err := xml.Marshal(c)
			if err != nil {
				log.Println(err)
				return
			}

			// Write response
			res.Write(out)
			return
		}
	}

	// Iterate all albums
	outAlbums := make([]Album, 0)
	for _, a := range albums {
		// Load songs for album
		songs, err := data.DB.SongsForAlbum(a.ID)
		if err != nil {
			log.Println(err)
			return
		}

		// Get cover art, duration, and creation time from songs
		coverArt := 0
		duration := 0
		created := int64(0)

		// Sum up duration
		for i, s := range songs {
			duration += s.Length

			// Set cover art and created time from first song
			if i == 0 {
				coverArt = s.ArtID
				created = s.LastModified
			}
		}

		// Append Subsonic-style album to list
		outAlbums = append(outAlbums, Album{
			ID:        a.ID,
			Name:      a.Title,
			Artist:    a.Artist,
			ArtistID:  a.ArtistID,
			CoverArt:  strconv.Itoa(coverArt),
			SongCount: len(songs),
			Duration:  duration,
			Created:   time.Unix(created, 0).Format("2006-01-02T15:04:05"),
		})
	}

	// Copy albums list into output
	c.AlbumList2 = &AlbumList2Container{Albums: outAlbums}

	// Marshal container to XML
	out, err := xml.Marshal(c)
	if err != nil {
		log.Println(err)
		return
	}

	// Write response
	res.Write(out)
}

// GetAlbum is used in Subsonic to return a single album
func GetAlbum(req *http.Request, res http.ResponseWriter) {
	// All Subsonic emulation replies are XML
	res.Header().Set("Content-Type", "text/xml")

	// Fetch ID parameter
	pID := req.URL.Query().Get("id")
	if pID == "" {
		log.Println("No ID")
		return
	}

	// Parse ID
	id, err := strconv.Atoi(pID)
	if err != nil {
		log.Println(err)
		return
	}

	// Load album by ID
	album := &data.Album{ID: id}
	if err := album.Load(); err != nil {
		log.Println(err)
		return
	}

	// Load songs for album
	songs, err := data.DB.SongsForAlbum(album.ID)
	if err != nil {
		log.Println(err)
		return
	}

	// Get cover art, duration, and creation time from songs
	coverArt := 0
	duration := 0
	created := int64(0)

	outSongs := make([]Song, 0)
	for i, s := range songs {
		// Sum up duration
		duration += s.Length

		// Set cover art and created time from first song
		if i == 0 {
			coverArt = s.ArtID
			created = s.LastModified
		}

		// Build a Subsonic song
		outSongs = append(outSongs, Song{
			ID:          s.ID,
			Parent:      0,
			Title:       s.Title,
			Album:       s.Album,
			Artist:      s.Artist,
			IsDir:       false,
			CoverArt:    strconv.Itoa(coverArt),
			Created:     time.Unix(s.LastModified, 0).Format("2006-01-02T15:04:05"),
			Duration:    s.Length,
			BitRate:     s.Bitrate,
			Track:       s.Track,
			DiscNumber:  1,
			Year:        s.Year,
			Genre:       s.Genre,
			Size:        s.FileSize,
			Suffix:      "mp3",
			ContentType: "audio/mpeg",
			IsVideo:     false,
			Path:        s.FileName,
			AlbumID:     s.AlbumID,
			ArtistID:    s.ArtistID,
			Type:        "music",
		})
	}

	// Build output album
	outAlbum := &Album{
		ID:        album.ID,
		Name:      album.Title,
		Artist:    album.Artist,
		ArtistID:  album.ArtistID,
		CoverArt:  strconv.Itoa(coverArt),
		SongCount: len(songs),
		Duration:  duration,
		Created:   time.Unix(created, 0).Format("2006-01-02T15:04:05"),
	}

	// Create a new response container
	c := newContainer()

	// Copy album container into output
	outAlbum.Songs = outSongs
	c.Album = []Album{*outAlbum}

	// Marshal container to XML
	out, err := xml.Marshal(c)
	if err != nil {
		log.Println(err)
		return
	}

	// Write response
	res.Write(out)
}

// Song represents an emulated Subsonic song
type Song struct {
	ID          int    `xml:"id,attr"`
	Parent      int    `xml:"parent,attr"`
	Title       string `xml:"title,attr"`
	Album       string `xml:"album,attr"`
	Artist      string `xml:"artist,attr"`
	IsDir       bool   `xml:"isDir,attr"`
	CoverArt    string `xml:"coverArt,attr"`
	Created     string `xml:"created,attr"`
	Duration    int    `xml:"duration,attr"`
	BitRate     int    `xml:"bitRate,attr"`
	Track       int    `xml:"track,attr"`
	DiscNumber  int    `xml:"discNumber,attr"`
	Year        int    `xml:"year,attr"`
	Genre       string `xml:"genre,attr"`
	Size        int64  `xml:"size,attr"`
	Suffix      string `xml:"suffix,attr"`
	ContentType string `xml:"contentType,attr"`
	IsVideo     bool   `xml:"isVideo,attr"`
	Path        string `xml:"path,attr"`
	AlbumID     int    `xml:"albumId,attr"`
	ArtistID    int    `xml:"artistId,attr"`
	Type        string `xml:"type,attr"`
}

// GetStream is used to return the media stream for a single file
func GetStream(req *http.Request, res http.ResponseWriter) {
	// All Subsonic emulation replies are XML
	res.Header().Set("Content-Type", "text/xml")

	// Fetch ID parameter
	pID := req.URL.Query().Get("id")
	if pID == "" {
		log.Println("No ID")
		return
	}

	// Parse ID
	id, err := strconv.Atoi(pID)
	if err != nil {
		log.Println(err)
		return
	}

	// Load song by ID
	song := &data.Song{ID: id}
	if err := song.Load(); err != nil {
		log.Println(err)
		return
	}

	// Open file stream
	stream, err := song.Stream()
	if err != nil {
		log.Println(err)
		return
	}

	// Generate a string used for logging this operation
	opStr := fmt.Sprintf("[#%05d] %s - %s [%s %dkbps]", song.ID, song.Artist, song.Title,
		data.CodecMap[song.FileTypeID], song.Bitrate)

	// Attempt to send file stream over HTTP
	log.Println("stream: starting:", opStr)

	// Pass stream using song's file size, auto-detect MIME type
	if err := api.HTTPStream(song, "", song.FileSize, stream, req, res); err != nil {
		// Check for client reset
		if strings.Contains(err.Error(), "connection reset by peer") || strings.Contains(err.Error(), "broken pipe") {
			return
		}

		log.Println("stream: error:", err)
		return
	}

	log.Println("stream: completed:", opStr)
	return
}
<commit_msg>Replace XML marshing with render<commit_after>package subsonic

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/mdlayher/wavepipe/api"
	"github.com/mdlayher/wavepipe/data"

	"github.com/martini-contrib/render"
)

const (
	// XMLName is the top-level name of a Subsonic XML response
	XMLName = "subsonic-response"
	// XMLNS is the XML namespace of a Subsonic XML response
	XMLNS = "http://subsonic.org/restapi"
	// Version is the emulated Subsonic API version
	Version = "1.8.0"
)

var (
	// ErrBadCredentials returns a bad credentials response
	ErrBadCredentials = func() *Container {
		// Generate new container with failed status
		c := newContainer()
		c.Status = "failed"

		// Return error
		c.SubError = &Error{Code: 40, Message: "Wrong username or password."}
		return c
	}()
	// ErrMissingParameter returns a missing required parameter response
	ErrMissingParameter = func() *Container {
		// Generate new container with failed status
		c := newContainer()
		c.Status = "failed"

		// Return error
		c.SubError = &Error{Code: 10, Message: "Required parameter is missing."}
		return c
	}()
)

// Container is the top-level emulated Subsonic response
type Container struct {
	// Top-level container name
	XMLName xml.Name `xml:"subsonic-response"`

	// Attributes which are always present
	XMLNS   string `xml:"xmlns,attr"`
	Status  string `xml:"status,attr"`
	Version string `xml:"version,attr"`

	// Error, returned on failures
	SubError *Error

	// Nested data

	// getAlbum.view
	Album []Album `xml:"album"`

	// getAlbumList2.view
	AlbumList2 *AlbumList2Container
}

// Error returns the error code and message from Subsonic, and enables Subsonic
// errors to be returned in authentication
func (c Container) Error() string {
	return fmt.Sprintf("%d: %s", c.SubError.Code, c.SubError.Message)
}

// Error contains a Subsonic error, with status code and message
type Error struct {
	XMLName xml.Name `xml:"error,omitempty"`

	Code    int    `xml:"code,attr"`
	Message string `xml:"message,attr"`
}

// AlbumList2Container contains a list of emulated Subsonic albums, by tags
type AlbumList2Container struct {
	// Container name
	XMLName xml.Name `xml:"albumList2,omitempty"`

	// Albums
	Albums []Album `xml:"album"`
}

// Album represents an emulated Subsonic album
type Album struct {
	// Subsonic fields
	ID        int    `xml:"id,attr"`
	Name      string `xml:"name,attr"`
	Artist    string `xml:"artist,attr"`
	ArtistID  int    `xml:"artistId,attr"`
	CoverArt  string `xml:"coverArt,attr"`
	SongCount int    `xml:"songCount,attr"`
	Duration  int    `xml:"duration,attr"`
	Created   string `xml:"created,attr"`

	// Nested data

	// getAlbum.view
	Songs []Song `xml:"song"`
}

// newContainer creates a new, empty Container with the proper attributes
func newContainer() *Container {
	return &Container{
		XMLNS:   XMLNS,
		Status:  "ok",
		Version: Version,
	}
}

// GetPing is used in Subsonic to check server connectivity
func GetPing(res http.ResponseWriter) {
	// All Subsonic emulation replies are XML
	res.Header().Set("Content-Type", "text/xml")

	// Marshal empty container to XML
	out, err := xml.Marshal(newContainer())
	if err != nil {
		return
	}

	// Remove closing tag, replace with self-closing tag (needed by Android client)
	res.Write(bytes.Replace(out, []byte("></"+XMLName+">"), []byte("/>"), -1))
}

// GetAlbumList2 is used in Subsonic to return a list of albums organized with tags
func GetAlbumList2(req *http.Request, res http.ResponseWriter, r render.Render) {
	// Create a new response container
	c := newContainer()

	// Fetch all albums
	// TODO: add a LimitAlbums method to fetch subsets
	albums, err := data.DB.AllAlbums()
	if err != nil {
		log.Println(err)
		return
	}

	// If offset is past albums count, stop sending albums
	qOffset := req.URL.Query().Get("offset")
	if qOffset != "" {
		// Parse offset
		offset, err := strconv.Atoi(qOffset)
		if err != nil {
			log.Println(err)
			return
		}

		// Check if offset is greater than count
		if offset > len(albums) {
			// Empty albums list
			c.AlbumList2 = new(AlbumList2Container)

			// Write empty response
			r.XML(200, c)
			return
		}
	}

	// Iterate all albums
	outAlbums := make([]Album, 0)
	for _, a := range albums {
		// Load songs for album
		songs, err := data.DB.SongsForAlbum(a.ID)
		if err != nil {
			log.Println(err)
			return
		}

		// Get cover art, duration, and creation time from songs
		coverArt := 0
		duration := 0
		created := int64(0)

		// Sum up duration
		for i, s := range songs {
			duration += s.Length

			// Set cover art and created time from first song
			if i == 0 {
				coverArt = s.ArtID
				created = s.LastModified
			}
		}

		// Append Subsonic-style album to list
		outAlbums = append(outAlbums, Album{
			ID:        a.ID,
			Name:      a.Title,
			Artist:    a.Artist,
			ArtistID:  a.ArtistID,
			CoverArt:  strconv.Itoa(coverArt),
			SongCount: len(songs),
			Duration:  duration,
			Created:   time.Unix(created, 0).Format("2006-01-02T15:04:05"),
		})
	}

	// Copy albums list into output
	c.AlbumList2 = &AlbumList2Container{Albums: outAlbums}

	// Write response
	r.XML(200, c)
}

// GetAlbum is used in Subsonic to return a single album
func GetAlbum(req *http.Request, res http.ResponseWriter, r render.Render) {
	// Fetch ID parameter
	pID := req.URL.Query().Get("id")
	if pID == "" {
		log.Println("No ID")
		return
	}

	// Parse ID
	id, err := strconv.Atoi(pID)
	if err != nil {
		log.Println(err)
		return
	}

	// Load album by ID
	album := &data.Album{ID: id}
	if err := album.Load(); err != nil {
		log.Println(err)
		return
	}

	// Load songs for album
	songs, err := data.DB.SongsForAlbum(album.ID)
	if err != nil {
		log.Println(err)
		return
	}

	// Get cover art, duration, and creation time from songs
	coverArt := 0
	duration := 0
	created := int64(0)

	outSongs := make([]Song, 0)
	for i, s := range songs {
		// Sum up duration
		duration += s.Length

		// Set cover art and created time from first song
		if i == 0 {
			coverArt = s.ArtID
			created = s.LastModified
		}

		// Build a Subsonic song
		outSongs = append(outSongs, Song{
			ID:          s.ID,
			Parent:      0,
			Title:       s.Title,
			Album:       s.Album,
			Artist:      s.Artist,
			IsDir:       false,
			CoverArt:    strconv.Itoa(coverArt),
			Created:     time.Unix(s.LastModified, 0).Format("2006-01-02T15:04:05"),
			Duration:    s.Length,
			BitRate:     s.Bitrate,
			Track:       s.Track,
			DiscNumber:  1,
			Year:        s.Year,
			Genre:       s.Genre,
			Size:        s.FileSize,
			Suffix:      "mp3",
			ContentType: "audio/mpeg",
			IsVideo:     false,
			Path:        s.FileName,
			AlbumID:     s.AlbumID,
			ArtistID:    s.ArtistID,
			Type:        "music",
		})
	}

	// Build output album
	outAlbum := &Album{
		ID:        album.ID,
		Name:      album.Title,
		Artist:    album.Artist,
		ArtistID:  album.ArtistID,
		CoverArt:  strconv.Itoa(coverArt),
		SongCount: len(songs),
		Duration:  duration,
		Created:   time.Unix(created, 0).Format("2006-01-02T15:04:05"),
	}

	// Create a new response container
	c := newContainer()

	// Copy album container into output
	outAlbum.Songs = outSongs
	c.Album = []Album{*outAlbum}

	// Write response
	r.XML(200, c)
}

// Song represents an emulated Subsonic song
type Song struct {
	ID          int    `xml:"id,attr"`
	Parent      int    `xml:"parent,attr"`
	Title       string `xml:"title,attr"`
	Album       string `xml:"album,attr"`
	Artist      string `xml:"artist,attr"`
	IsDir       bool   `xml:"isDir,attr"`
	CoverArt    string `xml:"coverArt,attr"`
	Created     string `xml:"created,attr"`
	Duration    int    `xml:"duration,attr"`
	BitRate     int    `xml:"bitRate,attr"`
	Track       int    `xml:"track,attr"`
	DiscNumber  int    `xml:"discNumber,attr"`
	Year        int    `xml:"year,attr"`
	Genre       string `xml:"genre,attr"`
	Size        int64  `xml:"size,attr"`
	Suffix      string `xml:"suffix,attr"`
	ContentType string `xml:"contentType,attr"`
	IsVideo     bool   `xml:"isVideo,attr"`
	Path        string `xml:"path,attr"`
	AlbumID     int    `xml:"albumId,attr"`
	ArtistID    int    `xml:"artistId,attr"`
	Type        string `xml:"type,attr"`
}

// GetStream is used to return the media stream for a single file
func GetStream(req *http.Request, res http.ResponseWriter) {
	// Fetch ID parameter
	pID := req.URL.Query().Get("id")
	if pID == "" {
		log.Println("No ID")
		return
	}

	// Parse ID
	id, err := strconv.Atoi(pID)
	if err != nil {
		log.Println(err)
		return
	}

	// Load song by ID
	song := &data.Song{ID: id}
	if err := song.Load(); err != nil {
		log.Println(err)
		return
	}

	// Open file stream
	stream, err := song.Stream()
	if err != nil {
		log.Println(err)
		return
	}

	// Generate a string used for logging this operation
	opStr := fmt.Sprintf("[#%05d] %s - %s [%s %dkbps]", song.ID, song.Artist, song.Title,
		data.CodecMap[song.FileTypeID], song.Bitrate)

	// Attempt to send file stream over HTTP
	log.Println("stream: starting:", opStr)

	// Pass stream using song's file size, auto-detect MIME type
	if err := api.HTTPStream(song, "", song.FileSize, stream, req, res); err != nil {
		// Check for client reset
		if strings.Contains(err.Error(), "connection reset by peer") || strings.Contains(err.Error(), "broken pipe") {
			return
		}

		log.Println("stream: error:", err)
		return
	}

	log.Println("stream: completed:", opStr)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/dimfeld/glog"
	"github.com/dimfeld/gocache"
	"github.com/dimfeld/goconfig"
	"github.com/dimfeld/httppath"
	"github.com/dimfeld/httptreemux"
	"html/template"
	"io"
	"net"
	"net/http"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"strconv"
	"sync"
	"syscall"
	"time"
)

var (
	config *Config
)

func catchSIGINT(f func(), quit bool) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			glog.Infoln("SIGINT received...")
			f()
			if quit {
				os.Exit(1)
			}
		}
	}()
}

type GlobalData struct {
	*sync.RWMutex

	// General cache
	cache    gocache.Cache
	memCache gocache.Cache

	archive   ArchiveSpecList
	templates *template.Template
}

type Config struct {
	// Number of posts to display on the main page.
	IndexPosts int
	// True if /tag/<tag> should sort posts in descending order.
	TagsPageNewestFirst bool
	// True if archive list at the bottom should start with the latest month.
	ArchiveListNewestFirst bool

	// Directory to search for posts.
	PostsDir string
	// Directory to search for static data.
	DataDir string
	// Directory to use for the disk cache.
	CacheDir string
	// File path to store tags.json.
	TagsPath string

	LogDir string

	Domain string
	Port   int

	RunAs string

	LargeMemCacheLimit       int
	SmallMemCacheLimit       int
	LargeMemCacheObjectLimit int
	SmallMemCacheObjectLimit int
}

type simpleBlogHandler func(*GlobalData, http.ResponseWriter, *http.Request, map[string]string)

func handlerWrapper(handler simpleBlogHandler, globalData *GlobalData) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, urlParams map[string]string) {
		glog.Infof("%s %s", r.Method, r.RequestURI)
		startTime := time.Now()
		handler(globalData, w, r, urlParams)
		endTime := time.Now()
		duration := endTime.Sub(startTime)
		glog.Infof("   Handled in %d us", duration/time.Microsecond)
	}
}

func fileWrapper(filename string, handler httptreemux.HandlerFunc) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, urlParams map[string]string) {
		urlParams["file"] = filename
		handler(w, r, urlParams)
	}
}

func filePrefixWrapper(prefix string, handler httptreemux.HandlerFunc) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, urlParams map[string]string) {
		urlParams["file"] = filepath.Join(prefix, httppath.Clean(urlParams["file"]))
		handler(w, r, urlParams)
	}
}

func isDirectory(dirPath string) bool {
	stat, err := os.Stat(dirPath)
	if err != nil || !stat.IsDir() {
		return false
	}
	return true
}

func runAs(username string) error {
	u, err := user.Lookup(username)
	if err != nil {
		return err
	}

	uid, err := strconv.Atoi(u.Uid)
	if err != nil {
		return fmt.Errorf("Invalid UID for user %s", username)
	}

	gid, err := strconv.Atoi(u.Gid)
	if err != nil {
		return fmt.Errorf("Invalid GID for user %s", username)
	}

	// Set group first, since we lose permissions for it after setuid.
	err = syscall.Setgid(gid)
	if err != nil {
		return fmt.Errorf("setgid failed: %s", err)
	}

	err = syscall.Setuid(uid)
	if err != nil {
		return fmt.Errorf("setuid failed: %s", err)
	}

	return nil
}

func setup() (router *httptreemux.TreeMux, listener net.Listener, cleanup func()) {
	flag.Parse()
	config = &Config{
		Port: 80,
		// Large memory cache uses 64 MiB at most, with the largest object being 8 MiB.
		LargeMemCacheLimit:       64 * 1024 * 1024,
		LargeMemCacheObjectLimit: 8 * 1024 * 1024,
		// Small memory cache uses 16 MiB at most, with the largest object being 16KiB.
		SmallMemCacheLimit:       16 * 1024 * 1024,
		SmallMemCacheObjectLimit: 16 * 1024,
	}
	confFile := os.Getenv("SIMPLEBLOG_CONF")
	if confFile == "" && flag.NArg() != 0 {
		confFile = flag.Arg(0)
	}

	if confFile == "" {
		confFile = os.Args[0] + ".conf"
	}

	var confReader io.Reader = os.Stdin
	var err error
	if confFile != "-" {
		// Load from stdin
		confReader, err = os.Open(confFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error loading config: %s\n", err)
			os.Exit(1)
		}
	}

	err = goconfig.Load(config, confReader, "SIMPLEBLOG")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading config: %s\n", err)
		os.Exit(1)
	}

	// Use config.LogDir if not given on the command line.
	dir := flag.CommandLine.Lookup("log_dir")
	if dir != nil && dir.Value.String() == "" {
		if config.LogDir == "" {
			config.LogDir = "."
		}
		flag.Set("log_dir", config.LogDir)
	}

	listener, err = net.Listen("tcp", ":"+strconv.Itoa(config.Port))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Could not listen on port %d: %s\n", config.Port, err)
		os.Exit(1)
	}

	// Downgrade privileges, if configured, so we're not running as root.
	if config.RunAs != "" {
		err = runAs(config.RunAs)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Could not switch to user %s: %s\n", config.RunAs, err)
			os.Exit(1)
		}
		glog.ReadUsername()
	}

	closer := func() {
		glog.Infoln("Shutting down...")
		glog.Flush()
	}

	glog.Infof("Starting with config\n%+v\n", config)

	if config.Port != 80 {
		config.Domain = fmt.Sprintf("%s:%d", config.Domain, config.Port)
	}

	diskCache, err := gocache.NewDiskCache(config.CacheDir)
	if err != nil {
		glog.Fatal("Could not create disk cache in ", config.CacheDir)
	}

	if !isDirectory(config.DataDir) {
		glog.Fatal("Could not find data directory ", config.DataDir)
	}

	if !isDirectory(config.PostsDir) {
		glog.Fatal("Could not find posts directory ", config.PostsDir)
	}

	if !isDirectory(filepath.Join(config.DataDir, "assets")) {
		glog.Fatal("Could not find assets directory ", filepath.Join(config.DataDir, "assets"))
	}

	if !isDirectory(filepath.Join(config.DataDir, "images")) {
		glog.Fatal("Could not find assets directory ", filepath.Join(config.DataDir, "images"))
	}

	largeObjectLimit := config.LargeMemCacheObjectLimit
	largeMemCache := gocache.NewMemoryCache(
		config.LargeMemCacheLimit, largeObjectLimit)

	smallObjectLimit := config.SmallMemCacheObjectLimit
	smallMemCache := gocache.NewMemoryCache(
		config.SmallMemCacheLimit, smallObjectLimit)

	// Create a split cache, putting all objects smaller than 16 KiB into the small cache.
	// This split cache prevents a few large objects from evicting all the smaller objects.
	memCache := gocache.NewSplitSize(
		gocache.SplitSizeChild{MaxSize: smallObjectLimit, Cache: smallMemCache},
		gocache.SplitSizeChild{MaxSize: largeObjectLimit, Cache: largeMemCache})

	multiLevelCache := gocache.MultiLevel{0: memCache, 1: diskCache}

	templates, err := createTemplates()
	if err != nil {
		glog.Fatal("Error parsing template: ", err.Error())
	}

	os.Remove(config.TagsPath)
	globalData := &GlobalData{
		RWMutex:   &sync.RWMutex{},
		cache:     multiLevelCache,
		memCache:  memCache,
		templates: templates,
	}

	archive, err := NewArchiveSpecList(config.PostsDir)
	if err != nil {
		glog.Fatal("Could not create archive list: ", err)
	}
	globalData.archive = archive

	go watchFiles(globalData)

	router = httptreemux.New()
	router.PanicHandler = httptreemux.ShowErrorsPanicHandler

	router.GET("/", handlerWrapper(indexHandler, globalData))
	router.GET("/:year/:month/", handlerWrapper(archiveHandler, globalData))
	router.GET("/:year/:month/:post", handlerWrapper(postHandler, globalData))

	router.GET("/images/*file", filePrefixWrapper("images",
		handlerWrapper(staticNoCompressHandler, globalData)))
	router.GET("/assets/*file", filePrefixWrapper("assets",
		handlerWrapper(staticCompressHandler, globalData)))

	router.GET("/tag/:tag", handlerWrapper(tagHandler, globalData))

	router.GET("/:page", handlerWrapper(pageHandler, globalData))
	router.GET("/favicon.ico", fileWrapper("assets/favicon.ico",
		handlerWrapper(staticCompressHandler, globalData)))
	router.GET("/feed", handlerWrapper(atomHandler, globalData))

	return router, listener, closer
}

func main() {
	router, listener, closer := setup()

	catchSIGINT(closer, true)
	defer closer()

	glog.Infoln(http.Serve(listener, router))
}
<commit_msg>Try to create log directory if it doesn't exist<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/dimfeld/glog"
	"github.com/dimfeld/gocache"
	"github.com/dimfeld/goconfig"
	"github.com/dimfeld/httppath"
	"github.com/dimfeld/httptreemux"
	"html/template"
	"io"
	"net"
	"net/http"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"strconv"
	"sync"
	"syscall"
	"time"
)

var (
	config *Config
)

func catchSIGINT(f func(), quit bool) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			glog.Infoln("SIGINT received...")
			f()
			if quit {
				os.Exit(1)
			}
		}
	}()
}

type GlobalData struct {
	*sync.RWMutex

	// General cache
	cache    gocache.Cache
	memCache gocache.Cache

	archive   ArchiveSpecList
	templates *template.Template
}

type Config struct {
	// Number of posts to display on the main page.
	IndexPosts int
	// True if /tag/<tag> should sort posts in descending order.
	TagsPageNewestFirst bool
	// True if archive list at the bottom should start with the latest month.
	ArchiveListNewestFirst bool

	// Directory to search for posts.
	PostsDir string
	// Directory to search for static data.
	DataDir string
	// Directory to use for the disk cache.
	CacheDir string
	// File path to store tags.json.
	TagsPath string

	LogDir string

	Domain string
	Port   int

	RunAs string

	LargeMemCacheLimit       int
	SmallMemCacheLimit       int
	LargeMemCacheObjectLimit int
	SmallMemCacheObjectLimit int
}

type simpleBlogHandler func(*GlobalData, http.ResponseWriter, *http.Request, map[string]string)

func handlerWrapper(handler simpleBlogHandler, globalData *GlobalData) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, urlParams map[string]string) {
		glog.Infof("%s %s", r.Method, r.RequestURI)
		startTime := time.Now()
		handler(globalData, w, r, urlParams)
		endTime := time.Now()
		duration := endTime.Sub(startTime)
		glog.Infof("   Handled in %d us", duration/time.Microsecond)
	}
}

func fileWrapper(filename string, handler httptreemux.HandlerFunc) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, urlParams map[string]string) {
		urlParams["file"] = filename
		handler(w, r, urlParams)
	}
}

func filePrefixWrapper(prefix string, handler httptreemux.HandlerFunc) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, urlParams map[string]string) {
		urlParams["file"] = filepath.Join(prefix, httppath.Clean(urlParams["file"]))
		handler(w, r, urlParams)
	}
}

func isDirectory(dirPath string) bool {
	stat, err := os.Stat(dirPath)
	if err != nil || !stat.IsDir() {
		return false
	}
	return true
}

func runAs(username string) error {
	u, err := user.Lookup(username)
	if err != nil {
		return err
	}

	uid, err := strconv.Atoi(u.Uid)
	if err != nil {
		return fmt.Errorf("Invalid UID for user %s", username)
	}

	gid, err := strconv.Atoi(u.Gid)
	if err != nil {
		return fmt.Errorf("Invalid GID for user %s", username)
	}

	// Set group first, since we lose permissions for it after setuid.
	err = syscall.Setgid(gid)
	if err != nil {
		return fmt.Errorf("setgid failed: %s", err)
	}

	err = syscall.Setuid(uid)
	if err != nil {
		return fmt.Errorf("setuid failed: %s", err)
	}

	return nil
}

func setup() (router *httptreemux.TreeMux, listener net.Listener, cleanup func()) {
	flag.Parse()
	config = &Config{
		Port: 80,
		// Large memory cache uses 64 MiB at most, with the largest object being 8 MiB.
		LargeMemCacheLimit:       64 * 1024 * 1024,
		LargeMemCacheObjectLimit: 8 * 1024 * 1024,
		// Small memory cache uses 16 MiB at most, with the largest object being 16KiB.
		SmallMemCacheLimit:       16 * 1024 * 1024,
		SmallMemCacheObjectLimit: 16 * 1024,
	}
	confFile := os.Getenv("SIMPLEBLOG_CONF")
	if confFile == "" && flag.NArg() != 0 {
		confFile = flag.Arg(0)
	}

	if confFile == "" {
		confFile = os.Args[0] + ".conf"
	}

	var confReader io.Reader = os.Stdin
	var err error
	if confFile != "-" {
		// Load from stdin
		confReader, err = os.Open(confFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error loading config: %s\n", err)
			os.Exit(1)
		}
	}

	err = goconfig.Load(config, confReader, "SIMPLEBLOG")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading config: %s\n", err)
		os.Exit(1)
	}

	listener, err = net.Listen("tcp", ":"+strconv.Itoa(config.Port))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Could not listen on port %d: %s\n", config.Port, err)
		os.Exit(1)
	}

	// Downgrade privileges, if configured, so we're not running as root.
	if config.RunAs != "" {
		err = runAs(config.RunAs)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Could not switch to user %s: %s\n", config.RunAs, err)
			os.Exit(1)
		}
		glog.ReadUsername()
	}

	// Use config.LogDir if not given on the command line.
	dir := flag.CommandLine.Lookup("log_dir")
	if dir != nil && dir.Value.String() == "" {
		if config.LogDir == "" {
			config.LogDir = "."
		}
		flag.Set("log_dir", config.LogDir)
		if !isDirectory(config.LogDir) {
			err = os.MkdirAll(config.LogDir, 0755)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to create log directory: %s\n", err)
				fmt.Fprintf(os.Stderr, "Logs will go to $TMPDIR\n", err)
			}
		}
	}

	closer := func() {
		glog.Infoln("Shutting down...")
		glog.Flush()
	}

	glog.Infof("Starting with config\n%+v\n", config)

	if config.Port != 80 {
		config.Domain = fmt.Sprintf("%s:%d", config.Domain, config.Port)
	}

	diskCache, err := gocache.NewDiskCache(config.CacheDir)
	if err != nil {
		glog.Fatal("Could not create disk cache in ", config.CacheDir)
	}

	if !isDirectory(config.DataDir) {
		glog.Fatal("Could not find data directory ", config.DataDir)
	}

	if !isDirectory(config.PostsDir) {
		glog.Fatal("Could not find posts directory ", config.PostsDir)
	}

	if !isDirectory(filepath.Join(config.DataDir, "assets")) {
		glog.Fatal("Could not find assets directory ", filepath.Join(config.DataDir, "assets"))
	}

	if !isDirectory(filepath.Join(config.DataDir, "images")) {
		glog.Fatal("Could not find assets directory ", filepath.Join(config.DataDir, "images"))
	}

	largeObjectLimit := config.LargeMemCacheObjectLimit
	largeMemCache := gocache.NewMemoryCache(
		config.LargeMemCacheLimit, largeObjectLimit)

	smallObjectLimit := config.SmallMemCacheObjectLimit
	smallMemCache := gocache.NewMemoryCache(
		config.SmallMemCacheLimit, smallObjectLimit)

	// Create a split cache, putting all objects smaller than 16 KiB into the small cache.
	// This split cache prevents a few large objects from evicting all the smaller objects.
	memCache := gocache.NewSplitSize(
		gocache.SplitSizeChild{MaxSize: smallObjectLimit, Cache: smallMemCache},
		gocache.SplitSizeChild{MaxSize: largeObjectLimit, Cache: largeMemCache})

	multiLevelCache := gocache.MultiLevel{0: memCache, 1: diskCache}

	templates, err := createTemplates()
	if err != nil {
		glog.Fatal("Error parsing template: ", err.Error())
	}

	os.Remove(config.TagsPath)
	globalData := &GlobalData{
		RWMutex:   &sync.RWMutex{},
		cache:     multiLevelCache,
		memCache:  memCache,
		templates: templates,
	}

	archive, err := NewArchiveSpecList(config.PostsDir)
	if err != nil {
		glog.Fatal("Could not create archive list: ", err)
	}
	globalData.archive = archive

	go watchFiles(globalData)

	router = httptreemux.New()
	router.PanicHandler = httptreemux.ShowErrorsPanicHandler

	router.GET("/", handlerWrapper(indexHandler, globalData))
	router.GET("/:year/:month/", handlerWrapper(archiveHandler, globalData))
	router.GET("/:year/:month/:post", handlerWrapper(postHandler, globalData))

	router.GET("/images/*file", filePrefixWrapper("images",
		handlerWrapper(staticNoCompressHandler, globalData)))
	router.GET("/assets/*file", filePrefixWrapper("assets",
		handlerWrapper(staticCompressHandler, globalData)))

	router.GET("/tag/:tag", handlerWrapper(tagHandler, globalData))

	router.GET("/:page", handlerWrapper(pageHandler, globalData))
	router.GET("/favicon.ico", fileWrapper("assets/favicon.ico",
		handlerWrapper(staticCompressHandler, globalData)))
	router.GET("/feed", handlerWrapper(atomHandler, globalData))

	return router, listener, closer
}

func main() {
	router, listener, closer := setup()

	catchSIGINT(closer, true)
	defer closer()

	glog.Infoln(http.Serve(listener, router))
}
<|endoftext|>


<commit_before>// Copyright 2016 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package keybase

import (
	"fmt"
	"time"

	"github.com/keybase/go-updater"
	"github.com/keybase/go-updater/command"
)

// validCodeSigningKIDs are the list of valid code signing IDs for saltpack verify
var validCodeSigningKIDs = map[string]bool{
	"9092ae4e790763dc7343851b977930f35b16cf43ab0ad900a2af3d3ad5cea1a1": true, // keybot (device)
	"d3458bbecdfc0d0ae39fec05722c6e3e897c169223835977a8aa208dfcd902d3": true, // max (device, home)
	"65ae849d1949a8b0021b165b0edaf722e2a7a9036e07817e056e2d721bddcc0e": true, // max (paper key)
	"3a5a45c545ef4f661b8b7573711aaecee3fd5717053484a3a3e725cd68abaa5a": true, // chris (device, ccpro)
	"03d86864fb20e310590042ad3d5492c3f5d06728620175b03c717c211bfaccc2": true, // chris (paper key, clay harbor)
}

// Log is the logging interface for the keybase package
type Log interface {
	Debug(...interface{})
	Info(...interface{})
	Debugf(s string, args ...interface{})
	Infof(s string, args ...interface{})
	Warningf(s string, args ...interface{})
	Errorf(s string, args ...interface{})
}

// context is an updater.Context implementation
type context struct {
	// config is updater config
	config Config
	// log is the logger
	log Log
}

// endpoints define all the url locations for reporting, etc
type endpoints struct {
	update  string
	action  string
	success string
	err     string
}

var defaultEndpoints = endpoints{
	update:  "https://api.keybase.io/_/api/1.0/pkg/update.json",
	action:  "https://api.keybase.io/_/api/1.0/pkg/act.json",
	success: "https://api.keybase.io/_/api/1.0/pkg/success.json",
	err:     "https://api.keybase.io/_/api/1.0/pkg/error.json",
}

func newContext(cfg Config, log Log) *context {
	ctx := context{
		config: cfg,
		log:    log,
	}
	return &ctx
}

// NewUpdaterContext returns an updater context for Keybase
func NewUpdaterContext(pathToKeybase string, log Log) (updater.Context, *updater.Updater) {
	cfg, err := newConfig("Keybase", pathToKeybase, log)
	if err != nil {
		log.Warningf("Error loading config for context: %s", err)
	}

	src := NewUpdateSource(cfg, log)
	// For testing
	// (cd /Applications; ditto -c -k --sequesterRsrc --keepParent Keybase.app /tmp/Keybase.zip)
	//src := updater.NewLocalUpdateSource("/tmp/Keybase.zip", log)
	upd := updater.NewUpdater(src, &cfg, log)
	return newContext(&cfg, log), upd
}

// UpdateOptions returns update options
func (c *context) UpdateOptions() updater.UpdateOptions {
	return c.config.updaterOptions()
}

// GetUpdateUI returns Update UI
func (c *context) GetUpdateUI() updater.UpdateUI {
	return c
}

// GetLog returns log
func (c context) GetLog() Log {
	return c.log
}

// Verify verifies the signature
func (c context) Verify(update updater.Update) error {
	return updater.SaltpackVerifyDetachedFileAtPath(update.Asset.LocalPath, update.Asset.Signature, validCodeSigningKIDs, c.log)
}

type checkInUseResult struct {
	InUse bool `json:"in_use"`
}

func (c context) checkInUse() (bool, error) {
	var result checkInUseResult
	if err := command.ExecForJSON(c.config.keybasePath(), []string{"update", "check-in-use"}, &result, time.Minute, c.log); err != nil {
		return false, err
	}
	return result.InUse, nil
}

// BeforeApply is called before an update is applied
func (c context) BeforeApply(update updater.Update) error {
	inUse, err := c.checkInUse()
	if err != nil {
		c.log.Warningf("Error trying to check in use: %s", err)
	}
	if inUse {
		if cancel := c.PausedPrompt(); cancel {
			return fmt.Errorf("Canceled by user from paused prompt")
		}
	}
	return nil
}

// AfterApply is called after an update is applied
func (c context) AfterApply(update updater.Update) error {
	result, err := command.Exec(c.config.keybasePath(), []string{"update", "notify", "after-apply"}, 2*time.Minute, c.log)
	if err != nil {
		c.log.Warningf("Error in after apply: %s (%s)", err, result.CombinedOutput())
	}
	return nil
}
<commit_msg>my paper key name (#81)<commit_after>// Copyright 2016 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package keybase

import (
	"fmt"
	"time"

	"github.com/keybase/go-updater"
	"github.com/keybase/go-updater/command"
)

// validCodeSigningKIDs are the list of valid code signing IDs for saltpack verify
var validCodeSigningKIDs = map[string]bool{
	"9092ae4e790763dc7343851b977930f35b16cf43ab0ad900a2af3d3ad5cea1a1": true, // keybot (device)
	"d3458bbecdfc0d0ae39fec05722c6e3e897c169223835977a8aa208dfcd902d3": true, // max (device, home)
	"65ae849d1949a8b0021b165b0edaf722e2a7a9036e07817e056e2d721bddcc0e": true, // max (paper key, cry glass)
	"3a5a45c545ef4f661b8b7573711aaecee3fd5717053484a3a3e725cd68abaa5a": true, // chris (device, ccpro)
	"03d86864fb20e310590042ad3d5492c3f5d06728620175b03c717c211bfaccc2": true, // chris (paper key, clay harbor)
}

// Log is the logging interface for the keybase package
type Log interface {
	Debug(...interface{})
	Info(...interface{})
	Debugf(s string, args ...interface{})
	Infof(s string, args ...interface{})
	Warningf(s string, args ...interface{})
	Errorf(s string, args ...interface{})
}

// context is an updater.Context implementation
type context struct {
	// config is updater config
	config Config
	// log is the logger
	log Log
}

// endpoints define all the url locations for reporting, etc
type endpoints struct {
	update  string
	action  string
	success string
	err     string
}

var defaultEndpoints = endpoints{
	update:  "https://api.keybase.io/_/api/1.0/pkg/update.json",
	action:  "https://api.keybase.io/_/api/1.0/pkg/act.json",
	success: "https://api.keybase.io/_/api/1.0/pkg/success.json",
	err:     "https://api.keybase.io/_/api/1.0/pkg/error.json",
}

func newContext(cfg Config, log Log) *context {
	ctx := context{
		config: cfg,
		log:    log,
	}
	return &ctx
}

// NewUpdaterContext returns an updater context for Keybase
func NewUpdaterContext(pathToKeybase string, log Log) (updater.Context, *updater.Updater) {
	cfg, err := newConfig("Keybase", pathToKeybase, log)
	if err != nil {
		log.Warningf("Error loading config for context: %s", err)
	}

	src := NewUpdateSource(cfg, log)
	// For testing
	// (cd /Applications; ditto -c -k --sequesterRsrc --keepParent Keybase.app /tmp/Keybase.zip)
	//src := updater.NewLocalUpdateSource("/tmp/Keybase.zip", log)
	upd := updater.NewUpdater(src, &cfg, log)
	return newContext(&cfg, log), upd
}

// UpdateOptions returns update options
func (c *context) UpdateOptions() updater.UpdateOptions {
	return c.config.updaterOptions()
}

// GetUpdateUI returns Update UI
func (c *context) GetUpdateUI() updater.UpdateUI {
	return c
}

// GetLog returns log
func (c context) GetLog() Log {
	return c.log
}

// Verify verifies the signature
func (c context) Verify(update updater.Update) error {
	return updater.SaltpackVerifyDetachedFileAtPath(update.Asset.LocalPath, update.Asset.Signature, validCodeSigningKIDs, c.log)
}

type checkInUseResult struct {
	InUse bool `json:"in_use"`
}

func (c context) checkInUse() (bool, error) {
	var result checkInUseResult
	if err := command.ExecForJSON(c.config.keybasePath(), []string{"update", "check-in-use"}, &result, time.Minute, c.log); err != nil {
		return false, err
	}
	return result.InUse, nil
}

// BeforeApply is called before an update is applied
func (c context) BeforeApply(update updater.Update) error {
	inUse, err := c.checkInUse()
	if err != nil {
		c.log.Warningf("Error trying to check in use: %s", err)
	}
	if inUse {
		if cancel := c.PausedPrompt(); cancel {
			return fmt.Errorf("Canceled by user from paused prompt")
		}
	}
	return nil
}

// AfterApply is called after an update is applied
func (c context) AfterApply(update updater.Update) error {
	result, err := command.Exec(c.config.keybasePath(), []string{"update", "notify", "after-apply"}, 2*time.Minute, c.log)
	if err != nil {
		c.log.Warningf("Error in after apply: %s (%s)", err, result.CombinedOutput())
	}
	return nil
}
<|endoftext|>


<commit_before>// Package kinesis provides structs for working with AWS Kinesis records.
package kinesis

import (
	"encoding/json"

	"github.com/apex/go-apex"
)

// Event represents a Kinesis event with one or more records.
type Event struct {
	Records []*Record `json:"Records"`
}

// Record represents a single Kinesis record.
type Record struct {
	EventSource       string `json:"eventSource"`
	EventVersion      string `json:"eventVersion"`
	EventID           string `json:"eventID"`
	EventName         string `json:"eventName"`
	InvokeIdentityARN string `json:"invokeIdentityArn"`
	AWSRegion         string `json:"awsRegion"`
	EventSourceARN    string `json:"eventSourceARN"`
	Kinesis           struct {
		SchemaVersion  string `json:"kinesisSchemaVersion"`
		PartitionKey   string `json:"partitionKey"`
		SequenceNumber string `json:"sequenceNumber"`
		Data           []byte `json:"data"`
	}
}

// Handler handles Kinesis events.
type Handler interface {
	HandleKinesis(*Event, *apex.Context) error
}

// HandlerFunc unmarshals Kinesis events before passing control.
type HandlerFunc func(*Event, *apex.Context) error

// Handle implements apex.Handler.
func (h HandlerFunc) Handle(data json.RawMessage, ctx *apex.Context) (interface{}, error) {
	var event Event

	if err := json.Unmarshal(data, &event); err != nil {
		return nil, err
	}

	return nil, h(&event, ctx)
}

// HandleFunc handles Kinesis events with callback function.
func HandleFunc(h HandlerFunc) {
	apex.Handle(h)
}

// Handle Kinesis events with handler.
func Handle(h Handler) {
	HandleFunc(HandlerFunc(h.HandleKinesis))
}
<commit_msg>Add handler implementation to kinesis.HandlerFunc<commit_after>// Package kinesis provides structs for working with AWS Kinesis records.
package kinesis

import (
	"encoding/json"

	"github.com/apex/go-apex"
)

// Event represents a Kinesis event with one or more records.
type Event struct {
	Records []*Record `json:"Records"`
}

// Record represents a single Kinesis record.
type Record struct {
	EventSource       string `json:"eventSource"`
	EventVersion      string `json:"eventVersion"`
	EventID           string `json:"eventID"`
	EventName         string `json:"eventName"`
	InvokeIdentityARN string `json:"invokeIdentityArn"`
	AWSRegion         string `json:"awsRegion"`
	EventSourceARN    string `json:"eventSourceARN"`
	Kinesis           struct {
		SchemaVersion  string `json:"kinesisSchemaVersion"`
		PartitionKey   string `json:"partitionKey"`
		SequenceNumber string `json:"sequenceNumber"`
		Data           []byte `json:"data"`
	}
}

// Handler handles Kinesis events.
type Handler interface {
	HandleKinesis(*Event, *apex.Context) error
}

// HandlerFunc unmarshals Kinesis events before passing control.
type HandlerFunc func(*Event, *apex.Context) error

func (h HandlerFunc) HandleKinesis(event *Event, ctx *apex.Context) error {
	return h(event, ctx)
}

// Handle implements apex.Handler.
func (h HandlerFunc) Handle(data json.RawMessage, ctx *apex.Context) (interface{}, error) {
	var event Event

	if err := json.Unmarshal(data, &event); err != nil {
		return nil, err
	}

	return nil, h(&event, ctx)
}

// HandleFunc handles Kinesis events with callback function.
func HandleFunc(h HandlerFunc) {
	apex.Handle(h)
}

// Handle Kinesis events with handler.
func Handle(h Handler) {
	HandleFunc(HandlerFunc(h.HandleKinesis))
}
<|endoftext|>


<commit_before>// Dnode protocol for net/rpc
package kite

import (
	"code.google.com/p/go.net/websocket"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"koding/newkite/kodingkey"
	"koding/newkite/protocol"
	"koding/newkite/token"
	"koding/tools/dnode"
	"net/rpc"
	"reflect"
	"strconv"
)

// TODO: Needs to be implemented.
func NewDnodeClient(kite *Kite, conn io.ReadWriteCloser) rpc.ClientCodec {
	return &DnodeClientCodec{
		rwc: conn,
		dec: json.NewDecoder(conn),
		enc: json.NewEncoder(conn),
	}
}

type DnodeClientCodec struct {
	dec   *json.Decoder
	enc   *json.Encoder
	rwc   io.ReadWriteCloser
	dnode *dnode.DNode

	req  dnode.Message
	resp dnode.Message

	resultCallback  dnode.Callback
	methodWithID    bool
	closed          bool
	connectedClient *client
	kite            *Kite
}

func (d *DnodeClientCodec) WriteRequest(r *rpc.Request, body interface{}) error {
	log.Info("Dnode WriteRequest")
	return d.enc.Encode(&d.req)
}

func (d *DnodeClientCodec) ReadResponseHeader(r *rpc.Response) error {
	log.Info("Dnode ReadResponseHeader")

	if err := d.dec.Decode(&d.resp); err != nil {
		return err

	}
	return nil
}

func (d *DnodeClientCodec) ReadResponseBody(x interface{}) error {
	log.Info("Dnode ReadResponseBody")
	return nil
}

func (d *DnodeClientCodec) Close() error {
	log.Info("Dnode ClientClose")
	return d.rwc.Close()
}

type DnodeServerCodec struct {
	dec            *json.Decoder
	enc            *json.Encoder
	rwc            io.ReadWriteCloser
	dnode          *dnode.DNode
	req            dnode.Message
	resultCallback dnode.Callback
	methodWithID   bool
	closed         bool
	kite           *Kite

	// connectedClient is setup once for every client.
	connectedClient *client
}

func NewDnodeServerCodec(kite *Kite, conn io.ReadWriteCloser) rpc.ServerCodec {
	return &DnodeServerCodec{
		rwc:   conn,
		dec:   json.NewDecoder(conn),
		enc:   json.NewEncoder(conn),
		dnode: dnode.New(),
		kite:  kite,
	}
}

func (d *DnodeServerCodec) Send(method interface{}, arguments ...interface{}) {
	callbacks := make(map[string]([]string))
	d.dnode.CollectCallbacks(arguments, make([]string, 0), callbacks)

	rawArgs, err := json.Marshal(arguments)
	if err != nil {
		log.Info("collect json unmarshal %+v\n", err)
	}

	message := dnode.Message{
		Method:    method,
		Arguments: &dnode.Partial{Raw: rawArgs},
		Links:     []string{},
		Callbacks: callbacks,
	}

	err = d.enc.Encode(message)
	if err != nil {
		log.Info("encode err %+v\n", err)
	}
}

func (d *DnodeServerCodec) ReadRequestHeader(r *rpc.Request) error {
	// reset values
	d.req = dnode.Message{}
	d.methodWithID = false

	// unmarshall incoming data to our dnode.Message struct
	err := d.dec.Decode(&d.req)
	if err != nil {
		return err
	}

	// if d.req.Method.(string) == "ping" {
	// 	return nil
	// }

	// for debugging: m -> c.req and m.Arguments -> c.req.Arguments
	// log.Info("[received] <- %+v %+v\n", c.req.Method, string(c.req.Arguments.Raw))

	for id, path := range d.req.Callbacks {
		methodId, err := strconv.Atoi(id)
		if err != nil {
			log.Info("WARNING: callback id should be an INTEGER: '%s', '%s'\n", id, path)
			continue
		}

		callback := dnode.Callback(func(args ...interface{}) {
			if d.closed {
				return
			}

			d.Send(methodId, args...)
		})

		d.req.Arguments.Callbacks = append(d.req.Arguments.Callbacks,
			dnode.CallbackSpec{
				Path:     path,
				Callback: callback,
			})
	}

	// received a dnode message with an method of type integer (ID), thus call our
	// stored callback that is related with this incoming ID.
	if index, err := strconv.Atoi(fmt.Sprint(d.req.Method)); err == nil {
		d.methodWithID = true

		// args can be zero or more
		args, err := d.req.Arguments.Array()
		if err != nil {
			log.Info("1 err: %s\n", err)
			return err
		}

		if index < 0 || index >= len(d.dnode.Callbacks) {
			return nil
		}

		callArgs := make([]reflect.Value, len(args))
		for i, v := range args {
			callArgs[i] = reflect.ValueOf(v)
		}

		d.dnode.Callbacks[index].Call(callArgs)
		return nil
	}

	// log.Info(d.kite.Methods)
	method, ok := d.kite.Methods[d.req.Method.(string)]
	if !ok {
		return fmt.Errorf("method %s is not registered", d.req.Method)
	}

	r.ServiceMethod = method

	// This is not used, we use our internal sequence store that is used inside
	// the dnode package, we
	// r.Seq = 0

	return nil
}

func (d *DnodeServerCodec) ReadRequestBody(body interface{}) error {
	if d.methodWithID {
		return nil
	}

	// args is of type *dnode.Partial
	var partials []*dnode.Partial
	err := d.req.Arguments.Unmarshal(&partials)
	if err != nil {
		return err
	}

	var options struct {
		Token           string `json:"token"`
		Kitename        string
		Username        string
		VmName          string
		CorrelationName string `json:"correlationName"`
		WithArgs        *dnode.Partial
	}

	err = partials[0].Unmarshal(&options)
	if err != nil {
		return err
	}

	var resultCallback dnode.Callback
	err = partials[1].Unmarshal(&resultCallback)
	if err != nil {
		return err
	}
	d.resultCallback = resultCallback

	if options.Token == "" {
		return errors.New("Token is not sent")
	}

	if body == nil {
		return nil
	}

	req := body.(*protocol.KiteDnodeRequest)
	req.Args = options.WithArgs
	req.Username = options.Username
	req.Hostname = options.CorrelationName

	// Return when kontrol is not enabled
	if !d.kite.KontrolEnabled {
		return nil
	}

	// Ignoring error because the key will be used in decrypt below.
	key, _ := kodingkey.FromString(d.kite.KodingKey)

	// DecryptString will fail if the key is not valid.
	tkn, err := token.DecryptString(options.Token, key)
	if err != nil {
		return errors.New("Invalid token")
	}

	if !tkn.IsValid(d.kite.ID) {
		log.Info("Invalid token '%s'\n", options.Token)
		return errors.New("Invalid token")
	}

	req.Username = tkn.Username
	d.UpdateClient(tkn.Username)

	log.Info("[%s] allowed token for: '%s'\n", d.ClientAddr(), req.Username)
	return nil
}

// update our clients map with the request data (for now only with username).
// Be aware that this method is called only when a RPC call is made, that
// means this is not called when a connection is established.
func (d *DnodeServerCodec) UpdateClient(username string) {
	if d.connectedClient != nil {
		return // we already got every detail
	}

	client := d.kite.clients.GetClient(d.ClientAddr())
	if client == nil {
		return

	}

	if username == "" {
		return
	}

	client.Username = username
	d.connectedClient = client

	d.kite.clients.AddAddresses(username, d.ClientAddr())

	// update username within client struct
	d.kite.clients.AddClient(d.ClientAddr(), client)

}

func (d *DnodeServerCodec) WriteResponse(r *rpc.Response, body interface{}) error {
	if d.methodWithID {
		// net/rpc is complaining when we exit, with an error like:
		// "rpc: service/method request ill-formed:", however this is OK. No
		// need to worry.
		return nil
	}

	if r.Error != "" {
		d.resultCallback(CreateErrorObject(fmt.Errorf(r.Error)))
		return nil
	}

	log.Info("method called:", r.ServiceMethod)

	d.resultCallback(nil, body)
	return nil
}

func (d *DnodeServerCodec) Close() error {
	log.Info("[%s] disconnected \n", d.ClientAddr())
	d.closed = true
	d.CallOnDisconnectFuncs()

	return d.rwc.Close()
}

func (d *DnodeServerCodec) CallOnDisconnectFuncs() {
	if d.connectedClient == nil {
		return
	}

	client := d.kite.clients.GetClient(d.ClientAddr())
	if client == nil {
		return
	}

	d.kite.clients.RemoveAddresses(client.Username, d.ClientAddr())
	addrs := d.kite.clients.GetAddresses(client.Username)

	if len(addrs) > 0 {
		return
	}

	for _, f := range client.onDisconnect {
		f()
	}

	d.kite.clients.RemoveClient(d.ClientAddr())
}

// Addr returns the connected clients addres
func (d *DnodeServerCodec) ClientAddr() string {
	return d.rwc.(*websocket.Conn).Request().RemoteAddr
}

// Got from kite package
type ErrorObject struct {
	Name    string `json:"name"`
	Message string `json:"message"`
}

func CreateErrorObject(err error) *ErrorObject {
	return &ErrorObject{Name: reflect.TypeOf(err).Elem().Name(), Message: err.Error()}
}
<commit_msg>provisioning: refactor prepare and create vm's based on mongodb id<commit_after>// Dnode protocol for net/rpc
package kite

import (
	"code.google.com/p/go.net/websocket"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"koding/newkite/kodingkey"
	"koding/newkite/protocol"
	"koding/newkite/token"
	"koding/tools/dnode"
	"net/rpc"
	"reflect"
	"strconv"
)

// TODO: Needs to be implemented.
func NewDnodeClient(kite *Kite, conn io.ReadWriteCloser) rpc.ClientCodec {
	return &DnodeClientCodec{
		rwc: conn,
		dec: json.NewDecoder(conn),
		enc: json.NewEncoder(conn),
	}
}

type DnodeClientCodec struct {
	dec   *json.Decoder
	enc   *json.Encoder
	rwc   io.ReadWriteCloser
	dnode *dnode.DNode

	req  dnode.Message
	resp dnode.Message

	resultCallback  dnode.Callback
	methodWithID    bool
	closed          bool
	connectedClient *client
	kite            *Kite
}

func (d *DnodeClientCodec) WriteRequest(r *rpc.Request, body interface{}) error {
	log.Info("Dnode WriteRequest")
	return d.enc.Encode(&d.req)
}

func (d *DnodeClientCodec) ReadResponseHeader(r *rpc.Response) error {
	log.Info("Dnode ReadResponseHeader")

	if err := d.dec.Decode(&d.resp); err != nil {
		return err

	}
	return nil
}

func (d *DnodeClientCodec) ReadResponseBody(x interface{}) error {
	log.Info("Dnode ReadResponseBody")
	return nil
}

func (d *DnodeClientCodec) Close() error {
	log.Info("Dnode ClientClose")
	return d.rwc.Close()
}

type DnodeServerCodec struct {
	dec            *json.Decoder
	enc            *json.Encoder
	rwc            io.ReadWriteCloser
	dnode          *dnode.DNode
	req            dnode.Message
	resultCallback dnode.Callback
	methodWithID   bool
	closed         bool
	kite           *Kite

	// connectedClient is setup once for every client.
	connectedClient *client
}

func NewDnodeServerCodec(kite *Kite, conn io.ReadWriteCloser) rpc.ServerCodec {
	return &DnodeServerCodec{
		rwc:   conn,
		dec:   json.NewDecoder(conn),
		enc:   json.NewEncoder(conn),
		dnode: dnode.New(),
		kite:  kite,
	}
}

func (d *DnodeServerCodec) Send(method interface{}, arguments ...interface{}) {
	callbacks := make(map[string]([]string))
	d.dnode.CollectCallbacks(arguments, make([]string, 0), callbacks)

	rawArgs, err := json.Marshal(arguments)
	if err != nil {
		log.Info("collect json unmarshal %+v\n", err)
	}

	message := dnode.Message{
		Method:    method,
		Arguments: &dnode.Partial{Raw: rawArgs},
		Links:     []string{},
		Callbacks: callbacks,
	}

	err = d.enc.Encode(message)
	if err != nil {
		log.Info("encode err %+v\n", err)
	}
}

func (d *DnodeServerCodec) ReadRequestHeader(r *rpc.Request) error {
	// reset values
	d.req = dnode.Message{}
	d.methodWithID = false

	// unmarshall incoming data to our dnode.Message struct
	err := d.dec.Decode(&d.req)
	if err != nil {
		return err
	}

	// if d.req.Method.(string) == "ping" {
	// 	return nil
	// }

	// for debugging: m -> c.req and m.Arguments -> c.req.Arguments
	// log.Info("[received] <- %+v %+v\n", c.req.Method, string(c.req.Arguments.Raw))

	for id, path := range d.req.Callbacks {
		methodId, err := strconv.Atoi(id)
		if err != nil {
			log.Info("WARNING: callback id should be an INTEGER: '%s', '%s'\n", id, path)
			continue
		}

		callback := dnode.Callback(func(args ...interface{}) {
			if d.closed {
				return
			}

			d.Send(methodId, args...)
		})

		d.req.Arguments.Callbacks = append(d.req.Arguments.Callbacks,
			dnode.CallbackSpec{
				Path:     path,
				Callback: callback,
			})
	}

	// received a dnode message with an method of type integer (ID), thus call our
	// stored callback that is related with this incoming ID.
	if index, err := strconv.Atoi(fmt.Sprint(d.req.Method)); err == nil {
		d.methodWithID = true

		// args can be zero or more
		args, err := d.req.Arguments.Array()
		if err != nil {
			log.Info("1 err: %s\n", err)
			return err
		}

		if index < 0 || index >= len(d.dnode.Callbacks) {
			return nil
		}

		callArgs := make([]reflect.Value, len(args))
		for i, v := range args {
			callArgs[i] = reflect.ValueOf(v)
		}

		d.dnode.Callbacks[index].Call(callArgs)
		return nil
	}

	// log.Info(d.kite.Methods)
	method, ok := d.kite.Methods[d.req.Method.(string)]
	if !ok {
		return fmt.Errorf("method %s is not registered", d.req.Method)
	}

	r.ServiceMethod = method

	// This is not used, we use our internal sequence store that is used inside
	// the dnode package, we
	// r.Seq = 0

	return nil
}

func (d *DnodeServerCodec) ReadRequestBody(body interface{}) error {
	if d.methodWithID {
		return nil
	}

	// args is of type *dnode.Partial
	var partials []*dnode.Partial
	err := d.req.Arguments.Unmarshal(&partials)
	if err != nil {
		return err
	}

	var options struct {
		Token           string `json:"token"`
		Kitename        string
		Username        string
		CorrelationName string `json:"correlationName"`
		WithArgs        *dnode.Partial
	}

	err = partials[0].Unmarshal(&options)
	if err != nil {
		return err
	}

	var resultCallback dnode.Callback
	err = partials[1].Unmarshal(&resultCallback)
	if err != nil {
		return err
	}
	d.resultCallback = resultCallback

	if options.Token == "" {
		return errors.New("Token is not sent")
	}

	if body == nil {
		return nil
	}

	req := body.(*protocol.KiteDnodeRequest)
	req.Args = options.WithArgs
	req.Username = options.Username
	req.Hostname = options.CorrelationName

	// Return when kontrol is not enabled
	if !d.kite.KontrolEnabled {
		return nil
	}

	// Ignoring error because the key will be used in decrypt below.
	key, _ := kodingkey.FromString(d.kite.KodingKey)

	// DecryptString will fail if the key is not valid.
	tkn, err := token.DecryptString(options.Token, key)
	if err != nil {
		return errors.New("Invalid token")
	}

	if !tkn.IsValid(d.kite.ID) {
		log.Info("Invalid token '%s'\n", options.Token)
		return errors.New("Invalid token")
	}

	req.Username = tkn.Username
	d.UpdateClient(tkn.Username)

	log.Info("[%s] allowed token for: '%s'\n", d.ClientAddr(), req.Username)
	return nil
}

// update our clients map with the request data (for now only with username).
// Be aware that this method is called only when a RPC call is made, that
// means this is not called when a connection is established.
func (d *DnodeServerCodec) UpdateClient(username string) {
	if d.connectedClient != nil {
		return // we already got every detail
	}

	client := d.kite.clients.GetClient(d.ClientAddr())
	if client == nil {
		return

	}

	if username == "" {
		return
	}

	client.Username = username
	d.connectedClient = client

	d.kite.clients.AddAddresses(username, d.ClientAddr())

	// update username within client struct
	d.kite.clients.AddClient(d.ClientAddr(), client)

}

func (d *DnodeServerCodec) WriteResponse(r *rpc.Response, body interface{}) error {
	if d.methodWithID {
		// net/rpc is complaining when we exit, with an error like:
		// "rpc: service/method request ill-formed:", however this is OK. No
		// need to worry.
		return nil
	}

	if r.Error != "" {
		d.resultCallback(CreateErrorObject(fmt.Errorf(r.Error)))
		return nil
	}

	log.Info("method called:", r.ServiceMethod)

	d.resultCallback(nil, body)
	return nil
}

func (d *DnodeServerCodec) Close() error {
	log.Info("[%s] disconnected \n", d.ClientAddr())
	d.closed = true
	d.CallOnDisconnectFuncs()

	return d.rwc.Close()
}

func (d *DnodeServerCodec) CallOnDisconnectFuncs() {
	if d.connectedClient == nil {
		return
	}

	client := d.kite.clients.GetClient(d.ClientAddr())
	if client == nil {
		return
	}

	d.kite.clients.RemoveAddresses(client.Username, d.ClientAddr())
	addrs := d.kite.clients.GetAddresses(client.Username)

	if len(addrs) > 0 {
		return
	}

	for _, f := range client.onDisconnect {
		f()
	}

	d.kite.clients.RemoveClient(d.ClientAddr())
}

// Addr returns the connected clients addres
func (d *DnodeServerCodec) ClientAddr() string {
	return d.rwc.(*websocket.Conn).Request().RemoteAddr
}

// Got from kite package
type ErrorObject struct {
	Name    string `json:"name"`
	Message string `json:"message"`
}

func CreateErrorObject(err error) *ErrorObject {
	return &ErrorObject{Name: reflect.TypeOf(err).Elem().Name(), Message: err.Error()}
}
<|endoftext|>