<commit_before>// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.

package main

import (
	"grumble/blobstore"
	"flag"
	"fmt"
	"os"
	"log"
	"net"
	"sqlite"
	"path/filepath"
	"regexp"
	"rpc"
	"runtime"
	"strconv"
	"time"
)

func defaultGrumbleDir() string {
	dirname := ".grumble"
	if runtime.GOOS == "windows" {
		dirname = "grumble"
	}
	return filepath.Join(os.Getenv("HOME"), dirname)
}

func defaultDataDir() string {
	return filepath.Join(defaultGrumbleDir(), "data")
}

func defaultBlobDir() string {
	return filepath.Join(defaultGrumbleDir(), "blob")
}

func defaultCtlNet() string {
	if runtime.GOOS == "windows" {
		return "tcp"
	}
	return "unix"
}

func defaultCtlAddr() string {
	if runtime.GOOS == "windows" {
		return "localhost:5454"
	}
	return filepath.Join(defaultGrumbleDir(), ".ctl")
}

var help *bool = flag.Bool("help", false, "Show this help")
var datadir *string = flag.String("datadir", defaultDataDir(), "Directory to use for server storage")
var blobdir *string = flag.String("blobdir", defaultBlobDir(), "Directory to use for blob storage")
var ctlnet *string = flag.String("ctlnet", defaultCtlNet(), "Network to use for ctl socket")
var ctladdr *string = flag.String("ctladdr", defaultCtlAddr(), "Address to use for ctl socket")
var sqlitedb *string = flag.String("murmurdb", "", "Path to murmur.sqlite to import server structure from")
var cleanup *bool = flag.Bool("clean", false, "Clean up existing data dir content before importing Murmur data")
var gencert *bool = flag.Bool("gencert", false, "Generate a self-signed certificate for use with Grumble")

var servers map[int64]*Server

func Usage() {
	fmt.Fprintf(os.Stderr, "usage: grumble [options]\n")
	fmt.Fprintf(os.Stderr, "remote control: grumble [options] ctl [ctlopts]\n")
	flag.PrintDefaults()
}

func MurmurImport(filename string) (err os.Error) {
	db, err := sqlite.Open(filename)
	if err != nil {
		panic(err.String())
	}

	stmt, err := db.Prepare("SELECT server_id FROM servers")
	if err != nil {
		panic(err.String())
	}

	var serverids []int64
	var sid int64
	for stmt.Next() {
		stmt.Scan(&sid)
		serverids = append(serverids, sid)
	}

	log.Printf("Found servers: %v (%v servers)", serverids, len(serverids))

	for _, sid := range serverids {
		m, err := NewServerFromSQLite(sid, db)
		if err != nil {
			return err
		}

		err = m.FreezeToFile(filepath.Join(*datadir, fmt.Sprintf("%v", sid)))
		if err != nil {
			return err
		}

		log.Printf("Successfully imported server %v", sid)
	}

	return
}

func main() {
	var err os.Error

	flag.Parse()
	if *help == true {
		Usage()
		return
	}

	for i, str := range os.Args {
		if str == "ctl" {
			GrumbleCtl(os.Args[i+1:])
			return
		}
	}

	log.SetPrefix("[G] ")
	log.Printf("Grumble")

	log.Printf("Using blob directory: %s", *blobdir)
	err = blobstore.Open(*blobdir, true)
	if err != nil {
		log.Fatalf("Unable to initialize blobstore: %v", err.String())
	}

	// Generate a cert?
	if *gencert {
		certfn := filepath.Join(*datadir, "cert")
		keyfn := filepath.Join(*datadir, "key")
		log.Printf("Generating 2048-bit RSA keypair for self-signed certificate...")

		err := GenerateSelfSignedCert(certfn, keyfn)
		if err != nil {
			log.Printf("Error: %v", err)
			return
		}

		log.Printf("Certificate output to %v", certfn)
		log.Printf("Private key output to %v", keyfn)

		log.Printf("Done generating certificate and private key.")
		log.Printf("Please restart Grumble to make use of the generated certificate and private key.")
		return
	}

	// Should we import data from a Murmur SQLite file?
	if len(*sqlitedb) > 0 {
		f, err := os.Open(*datadir)
		if err != nil {
			log.Fatalf("Murmur import failed: %s", err.String())
		}
		defer f.Close()

		names, err := f.Readdirnames(-1)
		if err != nil {
			log.Fatalf("Murmur import failed: %s", err.String())
		}

		if !*cleanup && len(names) > 0 {
			log.Fatalf("Non-empty datadir. Refusing to import Murmur data.")
		}
		if *cleanup {
			log.Printf("Cleaning up existing data directory")
			for _, name := range names {
				if err := os.Remove(filepath.Join(*datadir, name)); err != nil {
					log.Fatalf("Unable to cleanup file: %s", name)
				}
			}
		}

		log.Printf("Importing Murmur data from '%s'", *sqlitedb)
		if err = MurmurImport(*sqlitedb); err != nil {
			log.Fatalf("Murmur import failed: %s", err.String())
		}

		log.Printf("Import from Murmur SQLite database succeeded.")
		log.Printf("Please restart Grumble to make use of the imported data.")

		return
	}

	f, err := os.Open(*datadir)
	if err != nil {
		log.Fatalf("Murmur import failed: %s", err.String())
	}
	defer f.Close()

	names, err := f.Readdirnames(-1)
	if err != nil {
		log.Fatalf("Murmur import failed: %s", err.String())
	}

	servers = make(map[int64]*Server)
	for _, name := range names {
		if matched, _ := regexp.MatchString("^[0-9]+$", name); matched {
			log.Printf("Loading server %v", name)
			s, err := NewServerFromFrozen(filepath.Join(*datadir, name))
			if err != nil {
				log.Fatalf("Unable to load server: %s", err.String())
			}
			servers[s.Id] = s
			go s.ListenAndMurmur()
		}
		// win32 special-case
		if matched, _ := regexp.MatchString("^[0-9]+.old$", name); matched {
			sid, _ := strconv.Atoi64(name[0 : len(name)-4])
			_, exists := servers[sid]
			if !exists {
				log.Printf("Recovering lost server %v", name)
				s, err := NewServerFromFrozen(filepath.Join(*datadir, name))
				if err != nil {
					log.Fatalf("Unable to recover server: %S", err.String())
				}
				servers[sid] = s
				go s.ListenAndMurmur()
			}
		}
	}

	if len(servers) == 0 {
		s, err := NewServer(1, "0.0.0.0", 64738)
		if err != nil {
			log.Fatalf("Couldn't start server: %s", err.String())
		}

		servers[s.Id] = s
		go s.ListenAndMurmur()
	}

	if *ctlnet == "unix" {
		os.Remove(*ctladdr)
	}
	lis, err := net.Listen(*ctlnet, *ctladdr)
	if err != nil {
		log.Panicf("Unable to listen on ctl socket: %v", err)
	}

	ctl := &ControlRPC{}
	rpc.RegisterName("ctl", ctl)
	go rpc.Accept(lis)

	if len(servers) > 0 {
		ticker := time.NewTicker(10e9) // 10 secs
		go SignalHandler()
		for {
			select {
			case <-ticker.C:
				for sid, server := range servers {
					err := server.FreezeToFile(filepath.Join(*datadir, fmt.Sprintf("%v", sid)))
					if err != nil {
						log.Printf("Unable to freeze server %v: %s", sid, err.String())
						continue
					}
				}
			}
		}
	}
}
<commit_msg>Add jsonify option to grumble to aid in debugging.<commit_after>// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.

package main

import (
	"grumble/blobstore"
	"flag"
	"fmt"
	"json"
	"os"
	"log"
	"net"
	"sqlite"
	"path/filepath"
	"regexp"
	"rpc"
	"runtime"
	"strconv"
	"time"
)

func defaultGrumbleDir() string {
	dirname := ".grumble"
	if runtime.GOOS == "windows" {
		dirname = "grumble"
	}
	return filepath.Join(os.Getenv("HOME"), dirname)
}

func defaultDataDir() string {
	return filepath.Join(defaultGrumbleDir(), "data")
}

func defaultBlobDir() string {
	return filepath.Join(defaultGrumbleDir(), "blob")
}

func defaultCtlNet() string {
	if runtime.GOOS == "windows" {
		return "tcp"
	}
	return "unix"
}

func defaultCtlAddr() string {
	if runtime.GOOS == "windows" {
		return "localhost:5454"
	}
	return filepath.Join(defaultGrumbleDir(), ".ctl")
}

var help *bool = flag.Bool("help", false, "Show this help")
var datadir *string = flag.String("datadir", defaultDataDir(), "Directory to use for server storage")
var blobdir *string = flag.String("blobdir", defaultBlobDir(), "Directory to use for blob storage")
var ctlnet *string = flag.String("ctlnet", defaultCtlNet(), "Network to use for ctl socket")
var ctladdr *string = flag.String("ctladdr", defaultCtlAddr(), "Address to use for ctl socket")
var sqlitedb *string = flag.String("murmurdb", "", "Path to murmur.sqlite to import server structure from")
var jsonify *string = flag.String("jsonify", "", "Convert the frozen server at the specified path to JSON and output it to stdout")
var cleanup *bool = flag.Bool("clean", false, "Clean up existing data dir content before importing Murmur data")
var gencert *bool = flag.Bool("gencert", false, "Generate a self-signed certificate for use with Grumble")

var servers map[int64]*Server

func Usage() {
	fmt.Fprintf(os.Stderr, "usage: grumble [options]\n")
	fmt.Fprintf(os.Stderr, "remote control: grumble [options] ctl [ctlopts]\n")
	flag.PrintDefaults()
}

func MurmurImport(filename string) (err os.Error) {
	db, err := sqlite.Open(filename)
	if err != nil {
		panic(err.String())
	}

	stmt, err := db.Prepare("SELECT server_id FROM servers")
	if err != nil {
		panic(err.String())
	}

	var serverids []int64
	var sid int64
	for stmt.Next() {
		stmt.Scan(&sid)
		serverids = append(serverids, sid)
	}

	log.Printf("Found servers: %v (%v servers)", serverids, len(serverids))

	for _, sid := range serverids {
		m, err := NewServerFromSQLite(sid, db)
		if err != nil {
			return err
		}

		err = m.FreezeToFile(filepath.Join(*datadir, fmt.Sprintf("%v", sid)))
		if err != nil {
			return err
		}

		log.Printf("Successfully imported server %v", sid)
	}

	return
}

func main() {
	var err os.Error

	flag.Parse()
	if *help == true {
		Usage()
		return
	}

	for i, str := range os.Args {
		if str == "ctl" {
			GrumbleCtl(os.Args[i+1:])
			return
		}
	}

	// JSONify?
	if len(*jsonify) > 0 {
		server, err := NewServerFromFrozen(*jsonify)
		if err != nil {
			log.Fatalf("%v", err)
		}

		frozenServer, err := server.Freeze()
		if err != nil {
			log.Fatalf("%v", err)
		}

		enc := json.NewEncoder(os.Stdout)
		err = enc.Encode(frozenServer)
		if err != nil {
			log.Fatalf("%v", err)
		}

		return
	}

	log.SetPrefix("[G] ")
	log.Printf("Grumble")

	log.Printf("Using blob directory: %s", *blobdir)
	err = blobstore.Open(*blobdir, true)
	if err != nil {
		log.Fatalf("Unable to initialize blobstore: %v", err.String())
	}

	// Generate a cert?
	if *gencert {
		certfn := filepath.Join(*datadir, "cert")
		keyfn := filepath.Join(*datadir, "key")
		log.Printf("Generating 2048-bit RSA keypair for self-signed certificate...")

		err := GenerateSelfSignedCert(certfn, keyfn)
		if err != nil {
			log.Printf("Error: %v", err)
			return
		}

		log.Printf("Certificate output to %v", certfn)
		log.Printf("Private key output to %v", keyfn)

		log.Printf("Done generating certificate and private key.")
		log.Printf("Please restart Grumble to make use of the generated certificate and private key.")
		return
	}

	// Should we import data from a Murmur SQLite file?
	if len(*sqlitedb) > 0 {
		f, err := os.Open(*datadir)
		if err != nil {
			log.Fatalf("Murmur import failed: %s", err.String())
		}
		defer f.Close()

		names, err := f.Readdirnames(-1)
		if err != nil {
			log.Fatalf("Murmur import failed: %s", err.String())
		}

		if !*cleanup && len(names) > 0 {
			log.Fatalf("Non-empty datadir. Refusing to import Murmur data.")
		}
		if *cleanup {
			log.Printf("Cleaning up existing data directory")
			for _, name := range names {
				if err := os.Remove(filepath.Join(*datadir, name)); err != nil {
					log.Fatalf("Unable to cleanup file: %s", name)
				}
			}
		}

		log.Printf("Importing Murmur data from '%s'", *sqlitedb)
		if err = MurmurImport(*sqlitedb); err != nil {
			log.Fatalf("Murmur import failed: %s", err.String())
		}

		log.Printf("Import from Murmur SQLite database succeeded.")
		log.Printf("Please restart Grumble to make use of the imported data.")

		return
	}

	f, err := os.Open(*datadir)
	if err != nil {
		log.Fatalf("Murmur import failed: %s", err.String())
	}
	defer f.Close()

	names, err := f.Readdirnames(-1)
	if err != nil {
		log.Fatalf("Murmur import failed: %s", err.String())
	}

	servers = make(map[int64]*Server)
	for _, name := range names {
		if matched, _ := regexp.MatchString("^[0-9]+$", name); matched {
			log.Printf("Loading server %v", name)
			s, err := NewServerFromFrozen(filepath.Join(*datadir, name))
			if err != nil {
				log.Fatalf("Unable to load server: %s", err.String())
			}
			servers[s.Id] = s
			go s.ListenAndMurmur()
		}
		// win32 special-case
		if matched, _ := regexp.MatchString("^[0-9]+.old$", name); matched {
			sid, _ := strconv.Atoi64(name[0 : len(name)-4])
			_, exists := servers[sid]
			if !exists {
				log.Printf("Recovering lost server %v", name)
				s, err := NewServerFromFrozen(filepath.Join(*datadir, name))
				if err != nil {
					log.Fatalf("Unable to recover server: %S", err.String())
				}
				servers[sid] = s
				go s.ListenAndMurmur()
			}
		}
	}

	if len(servers) == 0 {
		s, err := NewServer(1, "0.0.0.0", 64738)
		if err != nil {
			log.Fatalf("Couldn't start server: %s", err.String())
		}

		servers[s.Id] = s
		go s.ListenAndMurmur()
	}

	if *ctlnet == "unix" {
		os.Remove(*ctladdr)
	}
	lis, err := net.Listen(*ctlnet, *ctladdr)
	if err != nil {
		log.Panicf("Unable to listen on ctl socket: %v", err)
	}

	ctl := &ControlRPC{}
	rpc.RegisterName("ctl", ctl)
	go rpc.Accept(lis)

	if len(servers) > 0 {
		ticker := time.NewTicker(10e9) // 10 secs
		go SignalHandler()
		for {
			select {
			case <-ticker.C:
				for sid, server := range servers {
					err := server.FreezeToFile(filepath.Join(*datadir, fmt.Sprintf("%v", sid)))
					if err != nil {
						log.Printf("Unable to freeze server %v: %s", sid, err.String())
						continue
					}
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/davecgh/go-spew/spew"
	"github.com/nsf/termbox-go"
	"github.com/simulatedsimian/neo"
	"os"
	"path/filepath"
	"unicode/utf8"
)

func printAt(x, y int, s string, fg, bg termbox.Attribute) {
	for len(s) > 0 {
		r, rlen := utf8.DecodeRuneInString(s)
		termbox.SetCell(x, y, r, fg, bg)
		s = s[rlen:]
		x++
	}
}

func printAtDef(x, y int, s string) {
	printAt(x, y, s, termbox.ColorDefault, termbox.ColorDefault)
}

type treeNode struct {
	info     os.FileInfo
	children []*treeNode
	parent   *treeNode
}

func createNodes(path string, parent *treeNode) ([]*treeNode, error) {
	var res []*treeNode

	fmt.Println(path)

	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		if err == nil && path != "." {
			res = append(res, &treeNode{info, nil, parent})
			if info.IsDir() {
				return filepath.SkipDir
			}
		}
		return nil
	})

	return res, err
}

func populateChildren(node *treeNode) error {

	if node.info.IsDir() {
		children, err := createNodes(getPathFromNode(node), node)
		neo.PanicOnError(err)
		node.children = children
	}
	return nil
}

func getPathFromNode(node *treeNode) string {
	path := node.info.Name()
	for node.parent != nil {
		path = filepath.Join(node.info.Name(), path)
	}
	return path
}

func test() {
	nodes, err := createNodes(".", nil)
	neo.PanicOnError(err)

	for _, node := range nodes {
		populateChildren(node)
	}

	spew.Dump(nodes)
	fmt.Println(err)
}

func termtest() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	termbox.HideCursor()

	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			printAt(0, 1, fmt.Sprint(ev), termbox.ColorDefault, termbox.ColorDefault)
			termbox.Flush()

			switch ev.Key {
			case termbox.KeyEsc:
				return
			case termbox.KeyArrowUp:
			case termbox.KeyArrowDown:
			}

			termbox.Flush()

		case termbox.EventResize:
			x, y := ev.Width, ev.Height
			printAt(0, 0, fmt.Sprintf("[%d, %d] ", x, y), termbox.ColorDefault, termbox.ColorDefault)
			termbox.Flush()
		}
	}

	termbox.Flush()
}

func main() {

	test()

}
<commit_msg>more testing<commit_after>package main

import (
	"fmt"
	"github.com/davecgh/go-spew/spew"
	"github.com/nsf/termbox-go"
	"github.com/simulatedsimian/neo"
	"os"
	"path/filepath"
	"unicode/utf8"
)

func printAt(x, y int, s string, fg, bg termbox.Attribute) {
	for len(s) > 0 {
		r, rlen := utf8.DecodeRuneInString(s)
		termbox.SetCell(x, y, r, fg, bg)
		s = s[rlen:]
		x++
	}
}

func printAtDef(x, y int, s string) {
	printAt(x, y, s, termbox.ColorDefault, termbox.ColorDefault)
}

type treeNode struct {
	info     os.FileInfo
	children []*treeNode
	parent   *treeNode
	expanded bool
}

func createNodes(path string, parent *treeNode) ([]*treeNode, error) {
	var res []*treeNode

	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		fmt.Println("---", path)
		if err == nil && path != "." {
			res = append(res, &treeNode{info, nil, parent, false})
			if info.IsDir() {
				return filepath.SkipDir
			}
		}
		return nil
	})

	return res, err
}

func populateChildren(node *treeNode) error {

	if node.info.IsDir() {
		children, err := createNodes(getPathFromNode(node), node)
		neo.PanicOnError(err)
		node.children = children
	}
	return nil
}

func getPathFromNode(node *treeNode) string {
	path := node.info.Name()
	for node.parent != nil {
		path = filepath.Join(node.info.Name(), path)
	}
	return path
}

func drawNodes(nodes []*treeNode) {
	for _, node := range nodes {
		if node.info.IsDir() {
			fmt.Print("+")
		}
		fmt.Println(node.info.Name())
	}
}

func test() {
	nodes, err := createNodes(".", nil)
	neo.PanicOnError(err)

	for _, node := range nodes {
		populateChildren(node)
	}

	drawNodes(nodes)

	spew.Dump(err)
	fmt.Println(err)
}

func termtest() {
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	termbox.HideCursor()

	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			printAt(0, 1, fmt.Sprint(ev), termbox.ColorDefault, termbox.ColorDefault)
			termbox.Flush()

			switch ev.Key {
			case termbox.KeyEsc:
				return
			case termbox.KeyArrowUp:
			case termbox.KeyArrowDown:
			}

			termbox.Flush()

		case termbox.EventResize:
			x, y := ev.Width, ev.Height
			printAt(0, 0, fmt.Sprintf("[%d, %d] ", x, y), termbox.ColorDefault, termbox.ColorDefault)
			termbox.Flush()
		}
	}

	termbox.Flush()
}

func main() {

	test()

}
<|endoftext|>


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

// Arqfs implements a file system interface to a collection of Arq backups.
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os/exec"

	"code.google.com/p/rsc/arq"
	"code.google.com/p/rsc/fuse"
	"code.google.com/p/rsc/keychain"
	"launchpad.net/goamz/aws"
)

func main() {
	access, secret, err := keychain.UserPasswd("s3.amazonaws.com", "")
	if err != nil {
		log.Fatal(err)
	}
	auth := aws.Auth{access, secret}

	conn, err := arq.Dial(auth)
	if err != nil {
		log.Fatal(err)
	}

	comps, err := conn.Computers()
	if err != nil {
		log.Fatal(err)
	}

	fs := &fuse.Tree{}
	for _, c := range comps {
		// TODO: what?
		_, pw, err := keychain.UserPasswd("arq.swtch.com", c.UUID)
		if err != nil {
			log.Fatal(err)
		}
		c.Unlock(pw)

		folders, err := c.Folders()
		if err != nil {
			log.Fatal(err)
		}

		lastDate := ""
		n := 0
		for _, f := range folders {
			if err := f.Load(); err != nil {
				log.Fatal(err)
			}
			trees, err := f.Trees()
			if err != nil {
				log.Fatal(err)
			}
			for _, t := range trees {
				y, m, d := t.Time.Date()
				date := fmt.Sprintf("%04d/%02d%02d", y, m, d)
				suffix := ""
				if date == lastDate {
					n++
					suffix = fmt.Sprintf(".%d", n)
				} else {
					n = 0
				}
				lastDate = date
				f, err := t.Root()
				if err != nil {
					log.Print(err)
				}
				// TODO: Pass times to fs.Add.
				fmt.Printf("%v %s %x\n", t.Time, c.Name+"/"+date+suffix+"/"+t.Path, t.Score)
				fs.Add(c.Name+"/"+date+suffix+"/"+t.Path, &fuseNode{f})
			}
		}
	}

	c, err := fuse.Mount("/mnt/arq")
	if err != nil {
		log.Fatal(err)
	}
	defer exec.Command("umount", "/mnt/arq").Run()

	fmt.Printf("serving /mnt/arq\n")
	c.Serve(fs)
}

type fuseNode struct {
	arq *arq.File
}

func (f *fuseNode) Attr() fuse.Attr {
	de := f.arq.Stat()
	return fuse.Attr{
		Mode:  de.Mode,
		Mtime: de.ModTime,
		Size:  uint64(de.Size),
	}
}

func (f *fuseNode) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) {
	ff, err := f.arq.Lookup(name)
	if err != nil {
		return nil, fuse.ENOENT
	}
	return &fuseNode{ff}, nil
}

func (f *fuseNode) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) {
	adir, err := f.arq.ReadDir()
	if err != nil {
		return nil, fuse.EIO
	}
	var dir []fuse.Dirent
	for _, ade := range adir {
		dir = append(dir, fuse.Dirent{
			Name: ade.Name,
		})
	}
	return dir, nil
}

// TODO: Implement Read+Release, not ReadAll, to avoid giant buffer.
func (f *fuseNode) ReadAll() ([]byte, fuse.Error) {
	rc, err := f.arq.Open()
	if err != nil {
		return nil, fuse.EIO
	}
	defer rc.Close()
	data, err := ioutil.ReadAll(rc)
	if err != nil {
		return data, fuse.EIO
	}
	return data, nil
}
<commit_msg>arq/arqfs: fix ReadAll signature<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Arqfs implements a file system interface to a collection of Arq backups.
package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os/exec"

	"code.google.com/p/rsc/arq"
	"code.google.com/p/rsc/fuse"
	"code.google.com/p/rsc/keychain"
	"launchpad.net/goamz/aws"
)

func main() {
	access, secret, err := keychain.UserPasswd("s3.amazonaws.com", "")
	if err != nil {
		log.Fatal(err)
	}
	auth := aws.Auth{access, secret}

	conn, err := arq.Dial(auth)
	if err != nil {
		log.Fatal(err)
	}

	comps, err := conn.Computers()
	if err != nil {
		log.Fatal(err)
	}

	fs := &fuse.Tree{}
	for _, c := range comps {
		// TODO: what?
		_, pw, err := keychain.UserPasswd("arq.swtch.com", c.UUID)
		if err != nil {
			log.Fatal(err)
		}
		c.Unlock(pw)

		folders, err := c.Folders()
		if err != nil {
			log.Fatal(err)
		}

		lastDate := ""
		n := 0
		for _, f := range folders {
			if err := f.Load(); err != nil {
				log.Fatal(err)
			}
			trees, err := f.Trees()
			if err != nil {
				log.Fatal(err)
			}
			for _, t := range trees {
				y, m, d := t.Time.Date()
				date := fmt.Sprintf("%04d/%02d%02d", y, m, d)
				suffix := ""
				if date == lastDate {
					n++
					suffix = fmt.Sprintf(".%d", n)
				} else {
					n = 0
				}
				lastDate = date
				f, err := t.Root()
				if err != nil {
					log.Print(err)
				}
				// TODO: Pass times to fs.Add.
				fmt.Printf("%v %s %x\n", t.Time, c.Name+"/"+date+suffix+"/"+t.Path, t.Score)
				fs.Add(c.Name+"/"+date+suffix+"/"+t.Path, &fuseNode{f})
			}
		}
	}

	c, err := fuse.Mount("/mnt/arq")
	if err != nil {
		log.Fatal(err)
	}
	defer exec.Command("umount", "/mnt/arq").Run()

	fmt.Printf("serving /mnt/arq\n")
	c.Serve(fs)
}

type fuseNode struct {
	arq *arq.File
}

func (f *fuseNode) Attr() fuse.Attr {
	de := f.arq.Stat()
	return fuse.Attr{
		Mode:  de.Mode,
		Mtime: de.ModTime,
		Size:  uint64(de.Size),
	}
}

func (f *fuseNode) Lookup(name string, intr fuse.Intr) (fuse.Node, fuse.Error) {
	ff, err := f.arq.Lookup(name)
	if err != nil {
		return nil, fuse.ENOENT
	}
	return &fuseNode{ff}, nil
}

func (f *fuseNode) ReadDir(intr fuse.Intr) ([]fuse.Dirent, fuse.Error) {
	adir, err := f.arq.ReadDir()
	if err != nil {
		return nil, fuse.EIO
	}
	var dir []fuse.Dirent
	for _, ade := range adir {
		dir = append(dir, fuse.Dirent{
			Name: ade.Name,
		})
	}
	return dir, nil
}

// TODO: Implement Read+Release, not ReadAll, to avoid giant buffer.
func (f *fuseNode) ReadAll(intr fuse.Intr) ([]byte, fuse.Error) {
	rc, err := f.arq.Open()
	if err != nil {
		return nil, fuse.EIO
	}
	defer rc.Close()
	data, err := ioutil.ReadAll(rc)
	if err != nil {
		return data, fuse.EIO
	}
	return data, nil
}
<|endoftext|>


<commit_before>package article

import (
	"bytes"
	"log"
	"strings"

	"github.com/ptt/pttweb/ansi"
	"github.com/ptt/pttweb/pttbbs"
	"github.com/ptt/pttweb/richcontent"

	"golang.org/x/net/context"
)

const (
	kPreviewContentLines = 5
)

type RenderOption func(*renderer)

func WithContent(content []byte) RenderOption {
	return func(r *renderer) {
		r.content = content
	}
}

func WithContext(ctx context.Context) RenderOption {
	return func(r *renderer) {
		r.ctx = ctx
	}
}

func WithDisableArticleHeader() RenderOption {
	return func(r *renderer) {
		r.disableArticleHeader = true
	}
}

type RenderedArticle interface {
	ParsedTitle() string
	PreviewContent() string
	HTML() []byte
}

func Render(opts ...RenderOption) (RenderedArticle, error) {
	r := newRenderer()
	for _, opt := range opts {
		opt(r)
	}
	if err := r.Render(); err != nil {
		return nil, err
	}
	return r, nil
}

type renderer struct {
	// Options.
	content              []byte
	disableArticleHeader bool
	ctx                  context.Context

	// Internal states.
	buf    bytes.Buffer
	lineNo int

	mapper    *IndexMapper
	lineBuf   bytes.Buffer
	lineSegs  []Segment
	segIndex  int
	segOffset int
	segClosed bool

	terminalState TerminalState

	acceptMetaLines bool

	title string

	previewContent   string
	previewLineCount int
}

func newRenderer() *renderer {
	ar := &renderer{
		ctx:      context.TODO(),
		mapper:   NewIndexMapper(2),
		lineSegs: make([]Segment, 0, 8),
	}
	ar.init()
	return ar
}

func (r *renderer) init() {
	r.buf.Reset()
	r.lineNo = 1

	r.mapper.Reset()
	r.lineBuf.Reset()
	r.lineSegs = r.lineSegs[0:0]
	r.segIndex = 0
	r.segOffset = 0
	r.segClosed = true

	r.terminalState.Reset()

	r.acceptMetaLines = true

	r.title = ""

	r.previewContent = ""
	r.previewLineCount = 0
}

func (r *renderer) ParsedTitle() string {
	return r.title
}

func (r *renderer) PreviewContent() string {
	return r.previewContent
}

func (r *renderer) HTML() []byte {
	return r.buf.Bytes()
}

func (r *renderer) Render() error {
	converter := &ansi.AnsiParser{
		Rune:   r.oneRune,
		Escape: r.escape,
	}
	return converter.ConvertFromUTF8(r.content)
}

func (r *renderer) currSeg() *Segment {
	if len(r.lineSegs) == 0 || r.segClosed {
		r.startSegment()
	}
	return &r.lineSegs[len(r.lineSegs)-1]
}

func (r *renderer) escape(esc ansi.EscapeSequence) {
	r.terminalState.ApplyEscapeSequence(esc)
	if r.segClosed || !r.terminalState.Equal(&r.currSeg().TermState) {
		r.startSegment()
	}
}

func (r *renderer) startSegment() {
	if !r.segClosed {
		r.endSegment()
	}
	r.lineSegs = append(r.lineSegs, Segment{
		Tag:       "span",
		TermState: r.terminalState,
		Buffer:    &bytes.Buffer{},
	})
	r.segClosed = false
}

func (r *renderer) endSegment() {
	// Remove empty segment
	if r.lineSegs[len(r.lineSegs)-1].Len() == 0 {
		r.lineSegs = r.lineSegs[:len(r.lineSegs)-1]
	}

	r.segClosed = true
}

func (r *renderer) oneRune(ru rune) {
	seg := r.currSeg()
	r.mapper.Record(r.lineBuf.Len(), len(r.lineSegs)-1, seg.Len())
	fastWriteHtmlEscapedRune(seg.Buffer, ru)
	r.lineBuf.WriteRune(ru)

	if ru == '\n' {
		r.endOfLine()
	}
}

func (r *renderer) outputToSegment(i, off int) {
	for ; r.segIndex < i; r.segIndex++ {
		s := &r.lineSegs[r.segIndex]
		if r.segOffset > 0 {
			// half output
			if r.segClosed {
				s.WriteOpen(&r.buf)
			}
			r.buf.Write(s.InnerBytes()[r.segOffset:])
			s.WriteClose(&r.buf)
			r.segOffset = 0
			r.segClosed = true
		} else {
			if !r.segClosed {
				panic("Segment not closed at beginning of a segment is not allowed")
			}
			s.WriteTo(&r.buf)
			r.segClosed = true
		}
	}
	if off > 0 {
		s := &r.lineSegs[r.segIndex]
		if r.segClosed {
			s.WriteOpen(&r.buf)
		}
		r.buf.Write(s.InnerBytes()[r.segOffset:off])
		r.segOffset = off
		r.segClosed = false
	}
}

func (r *renderer) skipToSegment(i, off int) {
	r.prematureCloseSegment()
	r.segIndex = i
	r.segOffset = off
}

func (r *renderer) prematureCloseSegment() {
	if !r.segClosed {
		r.lineSegs[r.segIndex].WriteClose(&r.buf)
		r.segClosed = true
	}
}

func (r *renderer) matchFirstLineAndOutput(line []byte) bool {
	tag1, val1, tag2, val2, ok := pttbbs.ParseArticleFirstLine(r.lineBuf.Bytes())
	if !ok {
		return false
	}

	r.writeMetaLine(tag1, val1, ClassArticleMetaLine)
	r.writeMetaLine(tag2, val2, ClassArticleMetaLineRight)
	return true
}

func (r *renderer) writeMetaLine(tag, val []byte, divClass string) {
	r.buf.WriteString(`<div class="` + divClass + `"><span class="` + ClassArticleMetaTag + `">`)
	fastWriteHtmlEscaped(&r.buf, string(tag))
	r.buf.WriteString(`</span>`)
	r.buf.WriteString(`<span class="` + ClassArticleMetaValue + `">`)
	fastWriteHtmlEscaped(&r.buf, string(val))
	r.buf.WriteString(`</span></div>`)
}

func (r *renderer) endOfLine() {
	r.segClosed = true

	line := r.lineBuf.Bytes()
	parsed := false

	if !r.disableArticleHeader && r.acceptMetaLines && r.lineNo < 5 {
		if r.lineNo == 1 && r.matchFirstLineAndOutput(line) {
			parsed = true
		} else if tag, val, ok := pttbbs.ParseArticleMetaLine(line); ok {
			if bytes.Equal(tag, []byte(pttbbs.ArticleTitle)) {
				r.title = string(val)
			}
			r.writeMetaLine(tag, val, ClassArticleMetaLine)
			parsed = true
		} else {
			r.acceptMetaLines = false
		}
	}

	if !parsed {
		isMainContent := false
		if len(r.lineSegs) > 0 {
			if pttbbs.MatchPrefixBytesToStrings(line, pttbbs.QuotePrefixStrings) {
				r.lineSegs[0].TermState.SetColor(6, DefaultBg, NoFlags)
			} else if pttbbs.MatchPrefixBytesToStrings(line, pttbbs.SignaturePrefixStrings) {
				r.lineSegs[0].TermState.SetColor(2, DefaultBg, NoFlags)
			} else {
				// Non-empty, not quote, and not signature line.
				isMainContent = true
			}
		}

		// Collect non-empty lines as preview starting at first main
		// content line.
		isEmpty := len(strings.TrimSpace(string(line))) == 0
		canCollect := !isEmpty && (r.previewLineCount == 0 && isMainContent || r.previewLineCount > 0)
		if canCollect && r.previewLineCount < kPreviewContentLines {
			r.previewContent += string(line)
			r.previewLineCount++
		}
		r.processNormalContentLine(line)
	}

	// Reset and update variables
	r.mapper.Reset()
	r.lineBuf.Reset()
	r.lineSegs = r.lineSegs[0:0]
	r.segIndex = 0
	r.segOffset = 0
	r.segClosed = true
	r.lineNo++
}

func (r *renderer) processNormalContentLine(line []byte) {
	// Detect push line
	isPush := false
	if matchPushLine(r.lineSegs) {
		r.lineSegs[0].ExtraFlags |= PushTag
		r.lineSegs[1].ExtraFlags |= PushUserId
		r.lineSegs[2].ExtraFlags |= PushContent
		r.lineSegs[3].ExtraFlags |= PushIpDateTime
		// Remove trailing spaces
		r.lineSegs[2].TrimRight(" ")
		r.buf.WriteString(`<div class="` + ClassPushDiv + `">`)
		isPush = true
	}

	rcs, err := richcontent.Find(r.ctx, line)
	if err != nil {
		rcs = nil
		log.Println("warning: rendering article: richcontent.Find:", err)
	}

	for _, rc := range rcs {
		linkBegin, linkEnd := makeExternalUrlLink(rc.URLString())

		lbegin, lend := rc.Pos()
		begin := r.mapper.Get(lbegin)
		end := r.mapper.Get(lend)
		r.outputToSegment(begin[0], begin[1])
		if begin[0] == end[0] {
			// same segment: embed
			r.buf.WriteString(linkBegin)
			r.outputToSegment(end[0], end[1])
			r.buf.WriteString(linkEnd)
		} else {
			// different segments: split, wrap-around
			r.prematureCloseSegment()
			r.buf.WriteString(linkBegin)
			r.outputToSegment(end[0], end[1])
			r.prematureCloseSegment()
			r.buf.WriteString(linkEnd)
		}
	}
	r.outputToSegment(len(r.lineSegs), 0)

	if isPush {
		r.buf.WriteString(`</div>`)
	}

	// Append rich contents to next line.
	for _, rc := range rcs {
		for _, comp := range rc.Components() {
			r.buf.WriteString(`<div class="richcontent">` + comp.HTML() + `</div>`)
		}
	}
}
<commit_msg>Fix not rendering last line when there's no end of line.<commit_after>package article

import (
	"bytes"
	"log"
	"strings"

	"github.com/ptt/pttweb/ansi"
	"github.com/ptt/pttweb/pttbbs"
	"github.com/ptt/pttweb/richcontent"

	"golang.org/x/net/context"
)

const (
	kPreviewContentLines = 5
)

type RenderOption func(*renderer)

func WithContent(content []byte) RenderOption {
	return func(r *renderer) {
		r.content = content
	}
}

func WithContext(ctx context.Context) RenderOption {
	return func(r *renderer) {
		r.ctx = ctx
	}
}

func WithDisableArticleHeader() RenderOption {
	return func(r *renderer) {
		r.disableArticleHeader = true
	}
}

type RenderedArticle interface {
	ParsedTitle() string
	PreviewContent() string
	HTML() []byte
}

func Render(opts ...RenderOption) (RenderedArticle, error) {
	r := newRenderer()
	for _, opt := range opts {
		opt(r)
	}
	if err := r.Render(); err != nil {
		return nil, err
	}
	return r, nil
}

type renderer struct {
	// Options.
	content              []byte
	disableArticleHeader bool
	ctx                  context.Context

	// Internal states.
	buf    bytes.Buffer
	lineNo int

	mapper    *IndexMapper
	lineBuf   bytes.Buffer
	lineSegs  []Segment
	segIndex  int
	segOffset int
	segClosed bool

	terminalState TerminalState

	acceptMetaLines bool

	title string

	previewContent   string
	previewLineCount int
}

func newRenderer() *renderer {
	ar := &renderer{
		ctx:      context.TODO(),
		mapper:   NewIndexMapper(2),
		lineSegs: make([]Segment, 0, 8),
	}
	ar.init()
	return ar
}

func (r *renderer) init() {
	r.buf.Reset()
	r.lineNo = 1

	r.mapper.Reset()
	r.lineBuf.Reset()
	r.lineSegs = r.lineSegs[0:0]
	r.segIndex = 0
	r.segOffset = 0
	r.segClosed = true

	r.terminalState.Reset()

	r.acceptMetaLines = true

	r.title = ""

	r.previewContent = ""
	r.previewLineCount = 0
}

func (r *renderer) ParsedTitle() string {
	return r.title
}

func (r *renderer) PreviewContent() string {
	return r.previewContent
}

func (r *renderer) HTML() []byte {
	return r.buf.Bytes()
}

func (r *renderer) Render() error {
	converter := &ansi.AnsiParser{
		Rune:   r.oneRune,
		Escape: r.escape,
	}
	if err := converter.ConvertFromUTF8(r.content); err != nil {
		return err
	}
	// Simulate end of line if there isn't one at the end.
	if r.lineBuf.Len() > 0 {
		r.endOfLine()
	}
	return nil
}

func (r *renderer) currSeg() *Segment {
	if len(r.lineSegs) == 0 || r.segClosed {
		r.startSegment()
	}
	return &r.lineSegs[len(r.lineSegs)-1]
}

func (r *renderer) escape(esc ansi.EscapeSequence) {
	r.terminalState.ApplyEscapeSequence(esc)
	if r.segClosed || !r.terminalState.Equal(&r.currSeg().TermState) {
		r.startSegment()
	}
}

func (r *renderer) startSegment() {
	if !r.segClosed {
		r.endSegment()
	}
	r.lineSegs = append(r.lineSegs, Segment{
		Tag:       "span",
		TermState: r.terminalState,
		Buffer:    &bytes.Buffer{},
	})
	r.segClosed = false
}

func (r *renderer) endSegment() {
	// Remove empty segment
	if r.lineSegs[len(r.lineSegs)-1].Len() == 0 {
		r.lineSegs = r.lineSegs[:len(r.lineSegs)-1]
	}

	r.segClosed = true
}

func (r *renderer) oneRune(ru rune) {
	seg := r.currSeg()
	r.mapper.Record(r.lineBuf.Len(), len(r.lineSegs)-1, seg.Len())
	fastWriteHtmlEscapedRune(seg.Buffer, ru)
	r.lineBuf.WriteRune(ru)

	if ru == '\n' {
		r.endOfLine()
	}
}

func (r *renderer) outputToSegment(i, off int) {
	for ; r.segIndex < i; r.segIndex++ {
		s := &r.lineSegs[r.segIndex]
		if r.segOffset > 0 {
			// half output
			if r.segClosed {
				s.WriteOpen(&r.buf)
			}
			r.buf.Write(s.InnerBytes()[r.segOffset:])
			s.WriteClose(&r.buf)
			r.segOffset = 0
			r.segClosed = true
		} else {
			if !r.segClosed {
				panic("Segment not closed at beginning of a segment is not allowed")
			}
			s.WriteTo(&r.buf)
			r.segClosed = true
		}
	}
	if off > 0 {
		s := &r.lineSegs[r.segIndex]
		if r.segClosed {
			s.WriteOpen(&r.buf)
		}
		r.buf.Write(s.InnerBytes()[r.segOffset:off])
		r.segOffset = off
		r.segClosed = false
	}
}

func (r *renderer) skipToSegment(i, off int) {
	r.prematureCloseSegment()
	r.segIndex = i
	r.segOffset = off
}

func (r *renderer) prematureCloseSegment() {
	if !r.segClosed {
		r.lineSegs[r.segIndex].WriteClose(&r.buf)
		r.segClosed = true
	}
}

func (r *renderer) matchFirstLineAndOutput(line []byte) bool {
	tag1, val1, tag2, val2, ok := pttbbs.ParseArticleFirstLine(r.lineBuf.Bytes())
	if !ok {
		return false
	}

	r.writeMetaLine(tag1, val1, ClassArticleMetaLine)
	r.writeMetaLine(tag2, val2, ClassArticleMetaLineRight)
	return true
}

func (r *renderer) writeMetaLine(tag, val []byte, divClass string) {
	r.buf.WriteString(`<div class="` + divClass + `"><span class="` + ClassArticleMetaTag + `">`)
	fastWriteHtmlEscaped(&r.buf, string(tag))
	r.buf.WriteString(`</span>`)
	r.buf.WriteString(`<span class="` + ClassArticleMetaValue + `">`)
	fastWriteHtmlEscaped(&r.buf, string(val))
	r.buf.WriteString(`</span></div>`)
}

func (r *renderer) endOfLine() {
	r.segClosed = true

	line := r.lineBuf.Bytes()
	parsed := false

	if !r.disableArticleHeader && r.acceptMetaLines && r.lineNo < 5 {
		if r.lineNo == 1 && r.matchFirstLineAndOutput(line) {
			parsed = true
		} else if tag, val, ok := pttbbs.ParseArticleMetaLine(line); ok {
			if bytes.Equal(tag, []byte(pttbbs.ArticleTitle)) {
				r.title = string(val)
			}
			r.writeMetaLine(tag, val, ClassArticleMetaLine)
			parsed = true
		} else {
			r.acceptMetaLines = false
		}
	}

	if !parsed {
		isMainContent := false
		if len(r.lineSegs) > 0 {
			if pttbbs.MatchPrefixBytesToStrings(line, pttbbs.QuotePrefixStrings) {
				r.lineSegs[0].TermState.SetColor(6, DefaultBg, NoFlags)
			} else if pttbbs.MatchPrefixBytesToStrings(line, pttbbs.SignaturePrefixStrings) {
				r.lineSegs[0].TermState.SetColor(2, DefaultBg, NoFlags)
			} else {
				// Non-empty, not quote, and not signature line.
				isMainContent = true
			}
		}

		// Collect non-empty lines as preview starting at first main
		// content line.
		isEmpty := len(strings.TrimSpace(string(line))) == 0
		canCollect := !isEmpty && (r.previewLineCount == 0 && isMainContent || r.previewLineCount > 0)
		if canCollect && r.previewLineCount < kPreviewContentLines {
			r.previewContent += string(line)
			r.previewLineCount++
		}
		r.processNormalContentLine(line)
	}

	// Reset and update variables
	r.mapper.Reset()
	r.lineBuf.Reset()
	r.lineSegs = r.lineSegs[0:0]
	r.segIndex = 0
	r.segOffset = 0
	r.segClosed = true
	r.lineNo++
}

func (r *renderer) processNormalContentLine(line []byte) {
	// Detect push line
	isPush := false
	if matchPushLine(r.lineSegs) {
		r.lineSegs[0].ExtraFlags |= PushTag
		r.lineSegs[1].ExtraFlags |= PushUserId
		r.lineSegs[2].ExtraFlags |= PushContent
		r.lineSegs[3].ExtraFlags |= PushIpDateTime
		// Remove trailing spaces
		r.lineSegs[2].TrimRight(" ")
		r.buf.WriteString(`<div class="` + ClassPushDiv + `">`)
		isPush = true
	}

	rcs, err := richcontent.Find(r.ctx, line)
	if err != nil {
		rcs = nil
		log.Println("warning: rendering article: richcontent.Find:", err)
	}

	for _, rc := range rcs {
		linkBegin, linkEnd := makeExternalUrlLink(rc.URLString())

		lbegin, lend := rc.Pos()
		begin := r.mapper.Get(lbegin)
		end := r.mapper.Get(lend)
		r.outputToSegment(begin[0], begin[1])
		if begin[0] == end[0] {
			// same segment: embed
			r.buf.WriteString(linkBegin)
			r.outputToSegment(end[0], end[1])
			r.buf.WriteString(linkEnd)
		} else {
			// different segments: split, wrap-around
			r.prematureCloseSegment()
			r.buf.WriteString(linkBegin)
			r.outputToSegment(end[0], end[1])
			r.prematureCloseSegment()
			r.buf.WriteString(linkEnd)
		}
	}
	r.outputToSegment(len(r.lineSegs), 0)

	if isPush {
		r.buf.WriteString(`</div>`)
	}

	// Append rich contents to next line.
	for _, rc := range rcs {
		for _, comp := range rc.Components() {
			r.buf.WriteString(`<div class="richcontent">` + comp.HTML() + `</div>`)
		}
	}
}
<|endoftext|>


<commit_before>package push

import (
	"io/ioutil"
	"os"
	"path/filepath"

	"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
	"code.cloudfoundry.org/cli/integration/helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("push with symlinked resources", func() {
	var (
		appName string
	)

	BeforeEach(func() {
		helpers.SkipIfVersionLessThan(ccversion.MinVersionSymlinkedFilesV2)
		appName = helpers.NewAppName()
	})

	Context("when pushing a directory", func() {
		Context("when the directory contains a symlink to a file in the directory", func() {
			Context("when the file exists", func() {
				It("should push the symlink", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						targetFile := filepath.Join(dir, "targetFile")
						Expect(ioutil.WriteFile(targetFile, []byte("foo bar baz"), 0777)).ToNot(HaveOccurred())
						relativePath, err := filepath.Rel(dir, targetFile)
						Expect(err).ToNot(HaveOccurred())

						err = os.Symlink(relativePath, filepath.Join(dir, "symlinkFile"))
						Expect(err).ToNot(HaveOccurred())

						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")

						Eventually(session).Should(Exit(0))
					})

					helpers.VerifyAppPackageContentsV2(appName, "symlinkFile", "targetFile", "Staticfile", "index.html")
				})
			})

			Context("when the file doesn't exists", func() {
				It("should push the symlink", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						tempFile, err := ioutil.TempFile(dir, "tempFile")
						Expect(err).ToNot(HaveOccurred())
						tempFile.Close()
						relativePath, err := filepath.Rel(dir, tempFile.Name())
						Expect(err).ToNot(HaveOccurred())

						err = os.Symlink(relativePath, filepath.Join(dir, "symlinkFile"))
						Expect(err).ToNot(HaveOccurred())
						Expect(os.Remove(tempFile.Name()))

						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")

						Eventually(session).Should(Exit(0))
					})

					helpers.VerifyAppPackageContentsV2(appName, "symlinkFile", "Staticfile", "index.html")
				})
			})
		})

		Context("when the directory contains a symlink to subdirectory in the directory", func() {
			It("should push the symlink", func() {
				helpers.WithHelloWorldApp(func(dir string) {
					targetDir, err := ioutil.TempDir(dir, "target-dir")
					Expect(err).ToNot(HaveOccurred())
					relativePath, err := filepath.Rel(dir, targetDir)
					Expect(err).ToNot(HaveOccurred())

					err = os.Symlink(relativePath, filepath.Join(dir, "symlinkFile"))
					Expect(err).ToNot(HaveOccurred())
					Expect(os.RemoveAll(targetDir)).ToNot(HaveOccurred())

					session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")

					Eventually(session).Should(Exit(0))
				})

				helpers.VerifyAppPackageContentsV2(appName, "symlinkFile", "Staticfile", "index.html")
			})
		})

		Context("when the directory contains a symlink to a file outside the directory", func() {
			var targetPath string

			BeforeEach(func() {
				tmpFile, err := ioutil.TempFile("", "push-symlink-integration-")
				Expect(err).ToNot(HaveOccurred())
				tmpFile.Close()

				targetPath = tmpFile.Name()
			})

			AfterEach(func() {
				Expect(os.Remove(targetPath))
			})

			It("it should fail with an upload invalid error", func() {
				helpers.WithHelloWorldApp(func(dir string) {
					err := os.Symlink(targetPath, filepath.Join(dir, "symlinkFile"))
					Expect(err).ToNot(HaveOccurred())

					session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName)

					Eventually(session.Err).Should(Say("The app upload is invalid: Symlink\\(s\\) point outside of root folder"))
					Eventually(session).Should(Exit(1))
				})
			})
		})
	})

	Context("when pushing an archive", func() {
		var archive string

		AfterEach(func() {
			Expect(os.RemoveAll(archive)).ToNot(HaveOccurred())
		})

		Context("when the archive contains a symlink to a file in the directory", func() {
			BeforeEach(func() {
				helpers.WithHelloWorldApp(func(appDir string) {
					helpers.WithHelloWorldApp(func(appDir string) {
						tmpfile, err := ioutil.TempFile("", "push-archive-integration")
						Expect(err).ToNot(HaveOccurred())
						archive = tmpfile.Name()
						Expect(tmpfile.Close()).ToNot(HaveOccurred())

						targetFile := filepath.Join(appDir, "targetFile")
						Expect(ioutil.WriteFile(targetFile, []byte("some random data"), 0777)).ToNot(HaveOccurred())
						relativePath, err := filepath.Rel(appDir, targetFile)
						Expect(err).ToNot(HaveOccurred())

						err = os.Symlink(relativePath, filepath.Join(appDir, "symlinkFile"))
						Expect(err).ToNot(HaveOccurred())

						err = helpers.Zipit(appDir, archive, "")
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})

			It("should push the symlink", func() {
				session := helpers.CF(PushCommandName, appName, "--no-start", "-p", archive)

				Eventually(session).Should(Exit(0))
				helpers.VerifyAppPackageContentsV2(appName, "symlinkFile", "targetFile", "Staticfile", "index.html")
			})
		})
	})
})
<commit_msg>pend symlink outside of root folder test<commit_after>package push

import (
	"io/ioutil"
	"os"
	"path/filepath"

	"code.cloudfoundry.org/cli/api/cloudcontroller/ccversion"
	"code.cloudfoundry.org/cli/integration/helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("push with symlinked resources", func() {
	var (
		appName string
	)

	BeforeEach(func() {
		helpers.SkipIfVersionLessThan(ccversion.MinVersionSymlinkedFilesV2)
		appName = helpers.NewAppName()
	})

	Context("when pushing a directory", func() {
		Context("when the directory contains a symlink to a file in the directory", func() {
			Context("when the file exists", func() {
				It("should push the symlink", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						targetFile := filepath.Join(dir, "targetFile")
						Expect(ioutil.WriteFile(targetFile, []byte("foo bar baz"), 0777)).ToNot(HaveOccurred())
						relativePath, err := filepath.Rel(dir, targetFile)
						Expect(err).ToNot(HaveOccurred())

						err = os.Symlink(relativePath, filepath.Join(dir, "symlinkFile"))
						Expect(err).ToNot(HaveOccurred())

						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")

						Eventually(session).Should(Exit(0))
					})

					helpers.VerifyAppPackageContentsV2(appName, "symlinkFile", "targetFile", "Staticfile", "index.html")
				})
			})

			Context("when the file doesn't exists", func() {
				It("should push the symlink", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						tempFile, err := ioutil.TempFile(dir, "tempFile")
						Expect(err).ToNot(HaveOccurred())
						tempFile.Close()
						relativePath, err := filepath.Rel(dir, tempFile.Name())
						Expect(err).ToNot(HaveOccurred())

						err = os.Symlink(relativePath, filepath.Join(dir, "symlinkFile"))
						Expect(err).ToNot(HaveOccurred())
						Expect(os.Remove(tempFile.Name()))

						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")

						Eventually(session).Should(Exit(0))
					})

					helpers.VerifyAppPackageContentsV2(appName, "symlinkFile", "Staticfile", "index.html")
				})
			})
		})

		Context("when the directory contains a symlink to subdirectory in the directory", func() {
			It("should push the symlink", func() {
				helpers.WithHelloWorldApp(func(dir string) {
					targetDir, err := ioutil.TempDir(dir, "target-dir")
					Expect(err).ToNot(HaveOccurred())
					relativePath, err := filepath.Rel(dir, targetDir)
					Expect(err).ToNot(HaveOccurred())

					err = os.Symlink(relativePath, filepath.Join(dir, "symlinkFile"))
					Expect(err).ToNot(HaveOccurred())
					Expect(os.RemoveAll(targetDir)).ToNot(HaveOccurred())

					session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")

					Eventually(session).Should(Exit(0))
				})

				helpers.VerifyAppPackageContentsV2(appName, "symlinkFile", "Staticfile", "index.html")
			})
		})

		// See #153499645
		PContext("when the directory contains a symlink to a file outside the directory", func() {
			var targetPath string

			BeforeEach(func() {
				tmpFile, err := ioutil.TempFile("", "push-symlink-integration-")
				Expect(err).ToNot(HaveOccurred())
				tmpFile.Close()

				targetPath = tmpFile.Name()
			})

			AfterEach(func() {
				Expect(os.Remove(targetPath))
			})

			It("it should fail with an upload invalid error", func() {
				helpers.WithHelloWorldApp(func(dir string) {
					err := os.Symlink(targetPath, filepath.Join(dir, "symlinkFile"))
					Expect(err).ToNot(HaveOccurred())

					session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName)

					Eventually(session.Err).Should(Say("The app upload is invalid: Symlink\\(s\\) point outside of root folder"))
					Eventually(session).Should(Exit(1))
				})
			})
		})
	})

	Context("when pushing an archive", func() {
		var archive string

		AfterEach(func() {
			Expect(os.RemoveAll(archive)).ToNot(HaveOccurred())
		})

		Context("when the archive contains a symlink to a file in the directory", func() {
			BeforeEach(func() {
				helpers.WithHelloWorldApp(func(appDir string) {
					helpers.WithHelloWorldApp(func(appDir string) {
						tmpfile, err := ioutil.TempFile("", "push-archive-integration")
						Expect(err).ToNot(HaveOccurred())
						archive = tmpfile.Name()
						Expect(tmpfile.Close()).ToNot(HaveOccurred())

						targetFile := filepath.Join(appDir, "targetFile")
						Expect(ioutil.WriteFile(targetFile, []byte("some random data"), 0777)).ToNot(HaveOccurred())
						relativePath, err := filepath.Rel(appDir, targetFile)
						Expect(err).ToNot(HaveOccurred())

						err = os.Symlink(relativePath, filepath.Join(appDir, "symlinkFile"))
						Expect(err).ToNot(HaveOccurred())

						err = helpers.Zipit(appDir, archive, "")
						Expect(err).ToNot(HaveOccurred())
					})
				})
			})

			It("should push the symlink", func() {
				session := helpers.CF(PushCommandName, appName, "--no-start", "-p", archive)

				Eventually(session).Should(Exit(0))
				helpers.VerifyAppPackageContentsV2(appName, "symlinkFile", "targetFile", "Staticfile", "index.html")
			})
		})
	})
})
<|endoftext|>


<commit_before>package influxdb

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/bmizerany/pat"
	"github.com/influxdb/influxdb/influxql"
)

// TODO: Standard response headers (see: HeaderHandler)
// TODO: Compression (see: CompressionHeaderHandler)

// TODO: Check HTTP response codes: 400, 401, 403, 409.

// getUsernameAndPassword returns the username and password encoded in
// a request. The credentials may be present as URL query params, or as
// a Basic Authentication header.
func getUsernameAndPassword(r *http.Request) (string, string, error) {
	q := r.URL.Query()
	username, password := q.Get("u"), q.Get("p")
	if username != "" && password != "" {
		return username, password, nil
	}
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return "", "", nil
	}
	fields := strings.Split(auth, " ")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("invalid Basic Authentication header")
	}
	bs, err := base64.StdEncoding.DecodeString(fields[1])
	if err != nil {
		return "", "", fmt.Errorf("invalid Base64 encoding")
	}
	fields = strings.Split(string(bs), ":")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("invalid Basic Authentication value")
	}
	return fields[0], fields[1], nil
}

// Handler represents an HTTP handler for the InfluxDB server.
type Handler struct {
	server *Server
	mux    *pat.PatternServeMux

	// Whether endpoints require authentication.
	AuthenticationEnabled bool

	// The InfluxDB verion returned by the HTTP response header.
	Version string
}

// NewHandler returns a new instance of Handler.
func NewHandler(s *Server) *Handler {
	h := &Handler{
		server: s,
		mux:    pat.New(),
	}

	// Query serving route.
	h.mux.Get("/query", h.makeAuthenticationHandler(h.serveQuery))

	// Data-ingest route.
	h.mux.Post("/db/:db/series", h.makeAuthenticationHandler(h.serveWriteSeries))

	// Data node routes.
	h.mux.Get("/data_nodes", h.makeAuthenticationHandler(h.serveDataNodes))
	h.mux.Post("/data_nodes", h.makeAuthenticationHandler(h.serveCreateDataNode))
	h.mux.Del("/data_nodes/:id", h.makeAuthenticationHandler(h.serveDeleteDataNode))

	// Utilities
	h.mux.Get("/metastore", h.makeAuthenticationHandler(h.serveMetastore))
	h.mux.Get("/ping", h.makeAuthenticationHandler(h.servePing))

	return h
}

// ServeHTTP responds to HTTP request to the handler.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Max-Age", "2592000")
	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
	w.Header().Add("X-Influxdb-Version", h.Version)

	// If this is a CORS OPTIONS request then send back okie-dokie.
	if r.Method == "OPTIONS" {
		w.WriteHeader(http.StatusOK)
		return
	}

	// Otherwise handle it via pat.
	h.mux.ServeHTTP(w, r)
}

// makeAuthenticationHandler takes a custom handler and returns a standard handler, ensuring that
// if user credentials are passed in, an attempt is made to authenticate that user. If authentication
// fails, an error is returned to the user.
//
// There is one exception: if there are no users in the system, authentication is not required. This
// is to facilitate bootstrapping of a system with authentication enabled.
func (h *Handler) makeAuthenticationHandler(fn func(http.ResponseWriter, *http.Request, *User)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var user *User
		if h.AuthenticationEnabled && len(h.server.Users()) > 0 {
			username, password, err := getUsernameAndPassword(r)
			if err != nil {
				h.error(w, err.Error(), http.StatusUnauthorized)
				return
			}
			if username == "" {
				h.error(w, "username required", http.StatusUnauthorized)
				return
			}

			user, err = h.server.Authenticate(username, password)
			if err != nil {
				h.error(w, err.Error(), http.StatusUnauthorized)
				return
			}
		}
		fn(w, r, user)
	}
}

// serveQuery parses an incoming query and returns the results.
func (h *Handler) serveQuery(w http.ResponseWriter, r *http.Request, u *User) {
	q := r.URL.Query()
	p := influxql.NewParser(strings.NewReader(q.Get("q")))

	// Parse query from query string.
	query, err := p.ParseQuery()
	if err != nil {
		h.error(w, "error parsing query: "+err.Error(), http.StatusBadRequest)
		return
	}

	// Bootstrapping a secured cluster involves a special authentication policy.
	// Until an admin-level user exists on the cluster one, and only one operation
	// is permitted without authentication -- the creation of the first user.
	// This user is also automatically granted admin-level access.
	if h.AuthenticationEnabled && len(h.server.Users()) > 0 {
		if len(query.Statements) != 1 {
			_, ok := query.Statements[0].(*influxql.CreateUserStatement)
			if !ok {
				h.error(w, "initial admin user does not exist", http.StatusUnauthorized)
				return
			}
		}
	}

	for _, s := range query.Statements {
		switch c := s.(type) {
		case *influxql.CreateDatabaseStatement:
			if err = h.server.CreateDatabase(c.Name); err == ErrDatabaseExists {
				h.error(w, "database exists", http.StatusInternalServerError)
			} else if err != nil {
				h.error(w, "error creating database: "+err.Error(), http.StatusInternalServerError)
			}
			w.WriteHeader(http.StatusCreated)
		case *influxql.DropDatabaseStatement:
			if err = h.server.DeleteDatabase(c.Name); err == ErrDatabaseNotFound {
				h.error(w, "database not found", http.StatusInternalServerError)
			} else if err != nil {
				h.error(w, "error deleting database: "+err.Error(), http.StatusInternalServerError)
			}
			w.WriteHeader(http.StatusNoContent)
		case *influxql.ListDatabasesStatement:
			databases := h.server.Databases()
			w.Header().Add("content-type", "application/json")
			if databases != nil {
				_ = json.NewEncoder(w).Encode(databases)
			} else {
				_ = json.NewEncoder(w).Encode([]string{})
			}

		case *influxql.CreateUserStatement:
			continue
		case *influxql.DropUserStatement:
			continue

		case *influxql.SelectStatement:
			continue
		case *influxql.DropSeriesStatement:
			continue
		case *influxql.ListSeriesStatement:
			continue
		case *influxql.ListMeasurementsStatement:
			continue
		case *influxql.ListTagKeysStatement:
			continue
		case *influxql.ListTagValuesStatement:
			continue
		case *influxql.ListFieldKeysStatement:
			continue
		case *influxql.ListFieldValuesStatement:
			continue

		case *influxql.GrantStatement:
			continue
		case *influxql.RevokeStatement:
			continue

		case *influxql.CreateRetentionPolicyStatement:
			rp := NewRetentionPolicy(c.Name)
			rp.Duration = c.Duration
			rp.ReplicaN = uint32(c.Replication)
			if err = h.server.CreateRetentionPolicy(c.Database, rp); err != nil {
				h.error(w, "error creating retention policy: "+err.Error(), http.StatusInternalServerError)
			} else {
				w.WriteHeader(http.StatusCreated)
			}
		case *influxql.AlterRetentionPolicyStatement:
			rp := NewRetentionPolicy(c.Name)
			if c.Duration != nil {
				rp.Duration = *c.Duration
			}
			if c.Replication != nil {
				rp.ReplicaN = uint32(*c.Replication)
			}
			if err = h.server.UpdateRetentionPolicy(c.Database, c.Name, rp); err != nil {
				h.error(w, "error altering retention policy: "+err.Error(), http.StatusInternalServerError)
			} else {
				w.WriteHeader(http.StatusCreated)
			}

		case *influxql.CreateContinuousQueryStatement:
			continue
		case *influxql.DropContinuousQueryStatement:
			continue
		case *influxql.ListContinuousQueriesStatement:
			continue
		}
	}
}

// serveWriteSeries receives incoming series data and writes it to the database.
func (h *Handler) serveWriteSeries(w http.ResponseWriter, r *http.Request, u *User) {
	// TODO: Authentication.

	/* TEMPORARILY REMOVED FOR PROTOBUFS.
	// Retrieve database from server.
	db := h.server.Database(r.URL.Query().Get(":db"))
	if db == nil {
		h.error(w, ErrDatabaseNotFound.Error(), http.StatusNotFound)
		return
	}

	// Parse time precision from query parameters.
	precision, err := parseTimePrecision(r.URL.Query().Get("time_precision"))
	if err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Setup HTTP request reader. Wrap in a gzip reader if encoding set in header.
	reader := r.Body
	if r.Header.Get("Content-Encoding") == "gzip" {
		if reader, err = gzip.NewReader(r.Body); err != nil {
			h.error(w, err.Error(), http.StatusBadRequest)
			return
		}
	}

	// Decode series from reader.
	ss := []*serializedSeries{}
	dec := json.NewDecoder(reader)
	dec.UseNumber()
	if err := dec.Decode(&ss); err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Convert the wire format to the internal representation of the time series.
	series, err := serializedSeriesSlice(ss).series(precision)
	if err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Write series data to the database.
	// TODO: Allow multiple series written to DB at once.
	for _, s := range series {
		if err := db.WriteSeries(s); err != nil {
			h.error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	*/
}

// serveMetastore returns a copy of the metastore.
func (h *Handler) serveMetastore(w http.ResponseWriter, r *http.Request, u *User) {
	// Set headers.
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", `attachment; filename="meta"`)

	if err := h.server.CopyMetastore(w); err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
	}
}

// servePing returns a simple response to let the client know the server is running.
func (h *Handler) servePing(w http.ResponseWriter, r *http.Request, u *User) {}

// serveDataNodes returns a list of all data nodes in the cluster.
func (h *Handler) serveDataNodes(w http.ResponseWriter, r *http.Request, u *User) {
	// Generate a list of objects for encoding to the API.
	a := make([]*dataNodeJSON, 0)
	for _, n := range h.server.DataNodes() {
		a = append(a, &dataNodeJSON{
			ID:  n.ID,
			URL: n.URL.String(),
		})
	}

	w.Header().Add("content-type", "application/json")
	_ = json.NewEncoder(w).Encode(a)
}

// serveCreateDataNode creates a new data node in the cluster.
func (h *Handler) serveCreateDataNode(w http.ResponseWriter, r *http.Request, _ *User) {
	// Read in data node from request body.
	var n dataNodeJSON
	if err := json.NewDecoder(r.Body).Decode(&n); err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Parse the URL.
	u, err := url.Parse(n.URL)
	if err != nil {
		h.error(w, "invalid data node url", http.StatusBadRequest)
		return
	}

	// Create the data node.
	if err := h.server.CreateDataNode(u); err == ErrDataNodeExists {
		h.error(w, err.Error(), http.StatusConflict)
		return
	} else if err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Retrieve data node reference.
	node := h.server.DataNodeByURL(u)

	// Create a new replica on the broker.
	if err := h.server.client.CreateReplica(node.ID); err != nil {
		h.error(w, err.Error(), http.StatusBadGateway)
		return
	}

	// Write new node back to client.
	w.WriteHeader(http.StatusCreated)
	w.Header().Add("content-type", "application/json")
	_ = json.NewEncoder(w).Encode(&dataNodeJSON{ID: node.ID, URL: node.URL.String()})
}

// serveDeleteDataNode removes an existing node.
func (h *Handler) serveDeleteDataNode(w http.ResponseWriter, r *http.Request, u *User) {
	// Parse node id.
	nodeID, err := strconv.ParseUint(r.URL.Query().Get(":id"), 10, 64)
	if err != nil {
		h.error(w, "invalid node id", http.StatusBadRequest)
		return
	}

	// Delete the node.
	if err := h.server.DeleteDataNode(nodeID); err == ErrDataNodeNotFound {
		h.error(w, err.Error(), http.StatusNotFound)
		return
	} else if err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

type dataNodeJSON struct {
	ID  uint64 `json:"id"`
	URL string `json:"url"`
}

// error returns an error to the client in a standard format.
func (h *Handler) error(w http.ResponseWriter, error string, code int) {
	// TODO: Return error as JSON.
	http.Error(w, error, code)
}
<commit_msg>Add 'continue' for now<commit_after>package influxdb

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/bmizerany/pat"
	"github.com/influxdb/influxdb/influxql"
)

// TODO: Standard response headers (see: HeaderHandler)
// TODO: Compression (see: CompressionHeaderHandler)

// TODO: Check HTTP response codes: 400, 401, 403, 409.

// getUsernameAndPassword returns the username and password encoded in
// a request. The credentials may be present as URL query params, or as
// a Basic Authentication header.
func getUsernameAndPassword(r *http.Request) (string, string, error) {
	q := r.URL.Query()
	username, password := q.Get("u"), q.Get("p")
	if username != "" && password != "" {
		return username, password, nil
	}
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return "", "", nil
	}
	fields := strings.Split(auth, " ")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("invalid Basic Authentication header")
	}
	bs, err := base64.StdEncoding.DecodeString(fields[1])
	if err != nil {
		return "", "", fmt.Errorf("invalid Base64 encoding")
	}
	fields = strings.Split(string(bs), ":")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("invalid Basic Authentication value")
	}
	return fields[0], fields[1], nil
}

// Handler represents an HTTP handler for the InfluxDB server.
type Handler struct {
	server *Server
	mux    *pat.PatternServeMux

	// Whether endpoints require authentication.
	AuthenticationEnabled bool

	// The InfluxDB verion returned by the HTTP response header.
	Version string
}

// NewHandler returns a new instance of Handler.
func NewHandler(s *Server) *Handler {
	h := &Handler{
		server: s,
		mux:    pat.New(),
	}

	// Query serving route.
	h.mux.Get("/query", h.makeAuthenticationHandler(h.serveQuery))

	// Data-ingest route.
	h.mux.Post("/db/:db/series", h.makeAuthenticationHandler(h.serveWriteSeries))

	// Data node routes.
	h.mux.Get("/data_nodes", h.makeAuthenticationHandler(h.serveDataNodes))
	h.mux.Post("/data_nodes", h.makeAuthenticationHandler(h.serveCreateDataNode))
	h.mux.Del("/data_nodes/:id", h.makeAuthenticationHandler(h.serveDeleteDataNode))

	// Utilities
	h.mux.Get("/metastore", h.makeAuthenticationHandler(h.serveMetastore))
	h.mux.Get("/ping", h.makeAuthenticationHandler(h.servePing))

	return h
}

// ServeHTTP responds to HTTP request to the handler.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Max-Age", "2592000")
	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
	w.Header().Add("X-Influxdb-Version", h.Version)

	// If this is a CORS OPTIONS request then send back okie-dokie.
	if r.Method == "OPTIONS" {
		w.WriteHeader(http.StatusOK)
		return
	}

	// Otherwise handle it via pat.
	h.mux.ServeHTTP(w, r)
}

// makeAuthenticationHandler takes a custom handler and returns a standard handler, ensuring that
// if user credentials are passed in, an attempt is made to authenticate that user. If authentication
// fails, an error is returned to the user.
//
// There is one exception: if there are no users in the system, authentication is not required. This
// is to facilitate bootstrapping of a system with authentication enabled.
func (h *Handler) makeAuthenticationHandler(fn func(http.ResponseWriter, *http.Request, *User)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var user *User
		if h.AuthenticationEnabled && len(h.server.Users()) > 0 {
			username, password, err := getUsernameAndPassword(r)
			if err != nil {
				h.error(w, err.Error(), http.StatusUnauthorized)
				return
			}
			if username == "" {
				h.error(w, "username required", http.StatusUnauthorized)
				return
			}

			user, err = h.server.Authenticate(username, password)
			if err != nil {
				h.error(w, err.Error(), http.StatusUnauthorized)
				return
			}
		}
		fn(w, r, user)
	}
}

// serveQuery parses an incoming query and returns the results.
func (h *Handler) serveQuery(w http.ResponseWriter, r *http.Request, u *User) {
	q := r.URL.Query()
	p := influxql.NewParser(strings.NewReader(q.Get("q")))

	// Parse query from query string.
	query, err := p.ParseQuery()
	if err != nil {
		h.error(w, "error parsing query: "+err.Error(), http.StatusBadRequest)
		return
	}

	// Bootstrapping a secured cluster involves a special authentication policy.
	// Until an admin-level user exists on the cluster one, and only one operation
	// is permitted without authentication -- the creation of the first user.
	// This user is also automatically granted admin-level access.
	if h.AuthenticationEnabled && len(h.server.Users()) > 0 {
		if len(query.Statements) != 1 {
			_, ok := query.Statements[0].(*influxql.CreateUserStatement)
			if !ok {
				h.error(w, "initial admin user does not exist", http.StatusUnauthorized)
				return
			}
		}
	}

	for _, s := range query.Statements {
		switch c := s.(type) {
		case *influxql.CreateDatabaseStatement:
			if err = h.server.CreateDatabase(c.Name); err == ErrDatabaseExists {
				h.error(w, "database exists", http.StatusInternalServerError)
				continue
			} else if err != nil {
				h.error(w, "error creating database: "+err.Error(), http.StatusInternalServerError)
				continue
			}
			w.WriteHeader(http.StatusCreated)
		case *influxql.DropDatabaseStatement:
			if err = h.server.DeleteDatabase(c.Name); err == ErrDatabaseNotFound {
				h.error(w, "database not found", http.StatusInternalServerError)
				continue
			} else if err != nil {
				h.error(w, "error deleting database: "+err.Error(), http.StatusInternalServerError)
				continue
			}
			w.WriteHeader(http.StatusNoContent)
		case *influxql.ListDatabasesStatement:
			databases := h.server.Databases()
			w.Header().Add("content-type", "application/json")
			if databases != nil {
				_ = json.NewEncoder(w).Encode(databases)
			} else {
				_ = json.NewEncoder(w).Encode([]string{})
			}

		case *influxql.CreateUserStatement:
			continue
		case *influxql.DropUserStatement:
			continue

		case *influxql.SelectStatement:
			continue
		case *influxql.DropSeriesStatement:
			continue
		case *influxql.ListSeriesStatement:
			continue
		case *influxql.ListMeasurementsStatement:
			continue
		case *influxql.ListTagKeysStatement:
			continue
		case *influxql.ListTagValuesStatement:
			continue
		case *influxql.ListFieldKeysStatement:
			continue
		case *influxql.ListFieldValuesStatement:
			continue

		case *influxql.GrantStatement:
			continue
		case *influxql.RevokeStatement:
			continue

		case *influxql.CreateRetentionPolicyStatement:
			rp := NewRetentionPolicy(c.Name)
			rp.Duration = c.Duration
			rp.ReplicaN = uint32(c.Replication)
			if err = h.server.CreateRetentionPolicy(c.Database, rp); err != nil {
				h.error(w, "error creating retention policy: "+err.Error(), http.StatusInternalServerError)
			} else {
				w.WriteHeader(http.StatusCreated)
			}
		case *influxql.AlterRetentionPolicyStatement:
			rp := NewRetentionPolicy(c.Name)
			if c.Duration != nil {
				rp.Duration = *c.Duration
			}
			if c.Replication != nil {
				rp.ReplicaN = uint32(*c.Replication)
			}
			if err = h.server.UpdateRetentionPolicy(c.Database, c.Name, rp); err != nil {
				h.error(w, "error altering retention policy: "+err.Error(), http.StatusInternalServerError)
			} else {
				w.WriteHeader(http.StatusCreated)
			}
		case *influxql.DropRetentionPolicyStatement:
			continue

		case *influxql.CreateContinuousQueryStatement:
			continue
		case *influxql.DropContinuousQueryStatement:
			continue
		case *influxql.ListContinuousQueriesStatement:
			continue
		}
	}
}

// serveWriteSeries receives incoming series data and writes it to the database.
func (h *Handler) serveWriteSeries(w http.ResponseWriter, r *http.Request, u *User) {
	// TODO: Authentication.

	/* TEMPORARILY REMOVED FOR PROTOBUFS.
	// Retrieve database from server.
	db := h.server.Database(r.URL.Query().Get(":db"))
	if db == nil {
		h.error(w, ErrDatabaseNotFound.Error(), http.StatusNotFound)
		return
	}

	// Parse time precision from query parameters.
	precision, err := parseTimePrecision(r.URL.Query().Get("time_precision"))
	if err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Setup HTTP request reader. Wrap in a gzip reader if encoding set in header.
	reader := r.Body
	if r.Header.Get("Content-Encoding") == "gzip" {
		if reader, err = gzip.NewReader(r.Body); err != nil {
			h.error(w, err.Error(), http.StatusBadRequest)
			return
		}
	}

	// Decode series from reader.
	ss := []*serializedSeries{}
	dec := json.NewDecoder(reader)
	dec.UseNumber()
	if err := dec.Decode(&ss); err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Convert the wire format to the internal representation of the time series.
	series, err := serializedSeriesSlice(ss).series(precision)
	if err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Write series data to the database.
	// TODO: Allow multiple series written to DB at once.
	for _, s := range series {
		if err := db.WriteSeries(s); err != nil {
			h.error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	*/
}

// serveMetastore returns a copy of the metastore.
func (h *Handler) serveMetastore(w http.ResponseWriter, r *http.Request, u *User) {
	// Set headers.
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", `attachment; filename="meta"`)

	if err := h.server.CopyMetastore(w); err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
	}
}

// servePing returns a simple response to let the client know the server is running.
func (h *Handler) servePing(w http.ResponseWriter, r *http.Request, u *User) {}

// serveDataNodes returns a list of all data nodes in the cluster.
func (h *Handler) serveDataNodes(w http.ResponseWriter, r *http.Request, u *User) {
	// Generate a list of objects for encoding to the API.
	a := make([]*dataNodeJSON, 0)
	for _, n := range h.server.DataNodes() {
		a = append(a, &dataNodeJSON{
			ID:  n.ID,
			URL: n.URL.String(),
		})
	}

	w.Header().Add("content-type", "application/json")
	_ = json.NewEncoder(w).Encode(a)
}

// serveCreateDataNode creates a new data node in the cluster.
func (h *Handler) serveCreateDataNode(w http.ResponseWriter, r *http.Request, _ *User) {
	// Read in data node from request body.
	var n dataNodeJSON
	if err := json.NewDecoder(r.Body).Decode(&n); err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Parse the URL.
	u, err := url.Parse(n.URL)
	if err != nil {
		h.error(w, "invalid data node url", http.StatusBadRequest)
		return
	}

	// Create the data node.
	if err := h.server.CreateDataNode(u); err == ErrDataNodeExists {
		h.error(w, err.Error(), http.StatusConflict)
		return
	} else if err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Retrieve data node reference.
	node := h.server.DataNodeByURL(u)

	// Create a new replica on the broker.
	if err := h.server.client.CreateReplica(node.ID); err != nil {
		h.error(w, err.Error(), http.StatusBadGateway)
		return
	}

	// Write new node back to client.
	w.WriteHeader(http.StatusCreated)
	w.Header().Add("content-type", "application/json")
	_ = json.NewEncoder(w).Encode(&dataNodeJSON{ID: node.ID, URL: node.URL.String()})
}

// serveDeleteDataNode removes an existing node.
func (h *Handler) serveDeleteDataNode(w http.ResponseWriter, r *http.Request, u *User) {
	// Parse node id.
	nodeID, err := strconv.ParseUint(r.URL.Query().Get(":id"), 10, 64)
	if err != nil {
		h.error(w, "invalid node id", http.StatusBadRequest)
		return
	}

	// Delete the node.
	if err := h.server.DeleteDataNode(nodeID); err == ErrDataNodeNotFound {
		h.error(w, err.Error(), http.StatusNotFound)
		return
	} else if err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

type dataNodeJSON struct {
	ID  uint64 `json:"id"`
	URL string `json:"url"`
}

// error returns an error to the client in a standard format.
func (h *Handler) error(w http.ResponseWriter, error string, code int) {
	// TODO: Return error as JSON.
	http.Error(w, error, code)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"io"
	"math/rand"
	"net/http"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	auth := r.Header.Get("X-Vip-Token")
	if auth != authToken {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	h(w, r)
}

func fileKey(bucket string) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))

	return
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard 5mb limit on files
	if r.ContentLength > 6<<20 {
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	key := fileKey(bucket)
	err := storage.PutReader(bucket, key, r.Body,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: fmt.Sprintf("%s/%s", bucket, key),
	})

	return
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")

	return
}
<commit_msg>Remove superfluous returns<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"io"
	"math/rand"
	"net/http"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	auth := r.Header.Get("X-Vip-Token")
	if auth != authToken {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	h(w, r)
}

func fileKey(bucket string) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard 5mb limit on files
	if r.ContentLength > 6<<20 {
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	key := fileKey(bucket)
	err := storage.PutReader(bucket, key, r.Body,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: fmt.Sprintf("%s/%s", bucket, key),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<|endoftext|>


<commit_before>package l2switch

var SwitchSecurityPolicy = `
#define BPF_TRACE
//#undef BPF_TRACE

//Ports 32+1
#define MAX_PORTS 33

struct mac_t {
	u64 mac;
};

struct interface {
	u32 ifindex;
};

struct ifindex{
	u32 ifindex;
};

struct ip_leaf{
	u32 ip;
};

BPF_TABLE("hash", struct mac_t, struct interface, fwdtable, 10240);
BPF_TABLE("array",u32,u32,ports,MAX_PORTS);
BPF_TABLE("hash",struct ifindex,struct mac_t, securitymac, MAX_PORTS*2);
BPF_TABLE("hash",struct ifindex,struct ip_leaf, securityip, MAX_PORTS*2);

static int handle_rx(void *skb, struct metadata *md) {
	u8 *cursor = 0;
	struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));

	#ifdef BPF_TRACE
		bpf_trace_printk("in_ifc=%d\n",md->in_ifc);
	#endif

	//set in-interface for lookup ports security
	struct ifindex in_iface = {};
	in_iface.ifindex = md->in_ifc;

	//port security on source mac
	struct mac_t * mac_lookup;
	mac_lookup = securitymac.lookup(&in_iface);
	if (mac_lookup)
		if (ethernet->src != mac_lookup->mac){
			#ifdef BPF_TRACE
				bpf_trace_printk("mac %lx mismatch %lx -> DROP\n",ethernet->src, mac_lookup->mac);
			#endif
			return RX_DROP;
		}

	//port security on source ip
	if(ethernet->type == 0x0800){
		struct ip_leaf * ip_lookup;
		ip_lookup = securityip.lookup(&in_iface);
		if (ip_lookup){
			struct ip_t *ip = cursor_advance(cursor,sizeof(*ip));
			if (ip->src != ip_lookup->ip){
				#ifdef BPF_TRACE
					bpf_trace_printk("IP %x mismatch %x -> DROP\n",ip->src, ip_lookup->ip);
				#endif
				return RX_DROP;
			}
		}
	}

	#ifdef BPF_TRACE
		bpf_trace_printk("mac src:%lx dst:%lx\n",ethernet->src,ethernet->dst);
	#endif

	//LEARNING PHASE: mapping in_iface with src_interface
	struct mac_t src_key = {};
	struct interface interface = {};

	//set in_iface as key
	src_key.mac = ethernet->src;

	//set in_ifc, and 0 counters as leaf
	interface.ifindex = md->in_ifc;

	//lookup in fwdtable. if no key present initialize with interface
	struct interface *interface_lookup = fwdtable.lookup_or_init(&src_key, &interface);

	//if the same mac has changed interface, update it
	if(interface_lookup->ifindex!=md->in_ifc)
		interface_lookup->ifindex = md->in_ifc;

	//FORWARDING PHASE: select interface(s) to send the packet
	struct mac_t dst_mac = {ethernet->dst};

	//lookup in forwarding table fwdtable
	struct interface *dst_interface = fwdtable.lookup(&dst_mac);

	if (dst_interface) {
		//HIT in forwarding table
		//redirect packet to dst_interface
		pkt_redirect(skb, md, dst_interface->ifindex);

		#ifdef BPF_TRACE
			bpf_trace_printk("redirect out_ifc=%d\n",dst_interface->ifindex);
		#endif

		return RX_REDIRECT;

	} else {
		//MISS in forwarding table
		#ifdef BPF_TRACE
			bpf_trace_printk("broadcast\n");
		#endif

		u32  iface_n = 1;
		u32 *iface_p;

		//Manual unroll to avoid ebpf verifier error due to loops
		//#pragma unroll seems not to work

		iface_n = 1; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 2; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 3; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 4; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 5; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 6; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 7; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 8; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 9; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 10; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 11; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 12; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 13; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 14; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 15; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 16; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 17; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 18; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 19; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 20; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 21; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 22; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 23; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 24; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 25; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 26; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 27; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 28; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 29; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 30; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 31; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		iface_n = 32; iface_p = ports.lookup(&iface_n);
		if(iface_p)
			if(*iface_p != 0)
				bpf_clone_redirect(skb,*iface_p,0);

		return RX_DROP;
	}
}
`
<commit_msg>add comments to Switch IOModule<commit_after>package l2switch

var SwitchSecurityPolicy = `
#define BPF_TRACE
//#undef BPF_TRACE

//Ports 32+1
#define MAX_PORTS 33

struct mac_t {
  u64 mac;
};

struct interface {
  u32 ifindex;
};

struct ifindex{
  u32 ifindex;
};

struct ip_leaf{
  u32 ip;
};

/*
  The Forwarding Table (fwdtable) contains the association between mac Addresses
  and	ports learned by the switch in the learning phase.
  This table is used also in the forwarding phase when the switch has to decide
  the port to use for forwarding the packet.
  The interface number uses the convention of hover, so is an incremental number
  given by hover daemon each time a port is attached to the IOModule (1, 2,..).
*/
BPF_TABLE("hash", struct mac_t, struct interface, fwdtable, 10240);

/*
  The Ports Table (ports) is a fixed lenght array that identifies the fd (file
  descriptors) of the network interfaces attached to the switch.
  This is a workaround for broadcast implementation, in order to be able to call
  bpf_clone_redirect that accepts as parameter the fd of the network interface.
  This array is not ordered. The index of the array does NOT represent the
  interface number.
*/
BPF_TABLE("array",u32,u32,ports,MAX_PORTS);

/*
  The Security Mac Table (securitymac) associate to each port the allowed mac
  address. If no entry is associated with the port, the port security is not
  applied to the port.
*/
BPF_TABLE("hash",struct ifindex,struct mac_t, securitymac, MAX_PORTS*2);

/*
  The Security Ip Table (securityip) associate to each port the allowed ip
  address. If no entry is associated with the port, the port security is not
  applied to the port.
*/
BPF_TABLE("hash",struct ifindex,struct ip_leaf, securityip, MAX_PORTS*2);

static int handle_rx(void *skb, struct metadata *md) {
  u8 *cursor = 0;
  struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));

  #ifdef BPF_TRACE
    bpf_trace_printk("in_ifc=%d\n",md->in_ifc);
  #endif

  //set in-interface for lookup ports security
  struct ifindex in_iface = {};
  in_iface.ifindex = md->in_ifc;

  //port security on source mac
  struct mac_t * mac_lookup;
  mac_lookup = securitymac.lookup(&in_iface);
  if (mac_lookup)
    if (ethernet->src != mac_lookup->mac){
      #ifdef BPF_TRACE
        bpf_trace_printk("mac %lx mismatch %lx -> DROP\n",ethernet->src, mac_lookup->mac);
      #endif
      return RX_DROP;
    }

  //port security on source ip
  if(ethernet->type == 0x0800){
    struct ip_leaf * ip_lookup;
    ip_lookup = securityip.lookup(&in_iface);
    if (ip_lookup){
      struct ip_t *ip = cursor_advance(cursor,sizeof(*ip));
      if (ip->src != ip_lookup->ip){
        #ifdef BPF_TRACE
          bpf_trace_printk("IP %x mismatch %x -> DROP\n",ip->src, ip_lookup->ip);
        #endif
        return RX_DROP;
      }
    }
  }

  #ifdef BPF_TRACE
    bpf_trace_printk("mac src:%lx dst:%lx\n",ethernet->src,ethernet->dst);
  #endif

  //LEARNING PHASE: mapping in_iface with src_interface
  struct mac_t src_key = {};
  struct interface interface = {};

  //set in_iface as key
  src_key.mac = ethernet->src;

  //set in_ifc, and 0 counters as leaf
  interface.ifindex = md->in_ifc;

  //lookup in fwdtable. if no key present initialize with interface
  struct interface *interface_lookup = fwdtable.lookup_or_init(&src_key, &interface);

  //if the same mac has changed interface, update it
  if(interface_lookup->ifindex!=md->in_ifc)
    interface_lookup->ifindex = md->in_ifc;

  //FORWARDING PHASE: select interface(s) to send the packet
  struct mac_t dst_mac = {ethernet->dst};

  //lookup in forwarding table fwdtable
  struct interface *dst_interface = fwdtable.lookup(&dst_mac);

  if (dst_interface) {
    //HIT in forwarding table
    //redirect packet to dst_interface
    pkt_redirect(skb, md, dst_interface->ifindex);

    #ifdef BPF_TRACE
      bpf_trace_printk("redirect out_ifc=%d\n",dst_interface->ifindex);
    #endif

    return RX_REDIRECT;

  } else {
    //MISS in forwarding table
    #ifdef BPF_TRACE
      bpf_trace_printk("broadcast\n");
    #endif

    u32  iface_n = 1;
    u32 *iface_p;

    //Manual unroll to avoid ebpf verifier error due to loops
    //#pragma unroll seems not to work

    iface_n = 1; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 2; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 3; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 4; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 5; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 6; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 7; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 8; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 9; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 10; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 11; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 12; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 13; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 14; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 15; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 16; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 17; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 18; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 19; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 20; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 21; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 22; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 23; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 24; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 25; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 26; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 27; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 28; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 29; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 30; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 31; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    iface_n = 32; iface_p = ports.lookup(&iface_n);
    if(iface_p)
      if(*iface_p != 0)
        bpf_clone_redirect(skb,*iface_p,0);

    return RX_DROP;
  }
}
`
<|endoftext|>