<commit_before>package mego

import (
	"encoding/json"
	"encoding/xml"
	"net/http"
	"net/url"
	"strings"
	"errors"
	"sync"
	"sync/atomic"
	"path"
	"os"
	"github.com/simbory/mego/assert"
)

type routeSetting struct {
	method     string
	routePath  string
	reqHandler ReqHandler
	area       *Area
}

type endCtxSignal struct {}

type Server struct {
	webRoot       string
	contentRoot   string
	addr          string
	locked        bool
	routing       *routeTree
	initEvents    []func()
	err404Handler http.HandlerFunc
	err500Handler ErrHandler
	err400Handler http.HandlerFunc
	err403Handler http.HandlerFunc
	filters       filterContainer
	routeSettings []*routeSetting
	viewEngine    *ViewEngine
	engineLock    *sync.RWMutex
	urlSuffix     string
	ctxId         uint64
}

// NewServer create a new server
//
// webRoot: the root of this web server. and the content root is '${webRoot}/www'
//
// addr: the address the server is listen on
//
// urlSuffix: the dynamic url suffix. if this value is empty, all the dynamic url
// should be like 'https://example.com/path/to/url' or 'https://example.com/path/to/url/'
func NewServer(webRoot, addr string, urlSuffix string) *Server {
	webRoot = path.Clean(strings.Replace(webRoot, "\\", "/", -1))
	var s = &Server{
		webRoot: webRoot,
		contentRoot: webRoot + "/www",
		addr: addr,
		locked: false,
		routing: newRouteTree(),
		initEvents: []func(){},
		err404Handler: handle404,
		err500Handler: handle500,
		err400Handler: handle400,
		err403Handler: handle403,
		filters: make(filterContainer),
		engineLock: &sync.RWMutex{},
		urlSuffix: urlSuffix,
	}
	return s
}

// assertUnlocked assert that the server is not running
func (s *Server) assertUnlocked() {
	if s.locked {
		panic(errors.New("The s is locked."))
	}
}

func (s *Server) addRoute(m, p string, h ReqHandler) {
	s.routeSettings = append(s.routeSettings, &routeSetting{
		method: m,
		routePath: p,
		reqHandler: h,
		area: nil,
	})
}

func (s *Server) addAreaRoute(m, p string, area *Area, h ReqHandler) {
	s.routeSettings = append(s.routeSettings, &routeSetting{
		method: m,
		routePath: p,
		reqHandler: h,
		area: area,
	})
}

func (s *Server) onInit() {
	if len(s.initEvents) > 0 {
		for _, h := range s.initEvents {
			h()
		}
	}
	if len(s.routeSettings) > 0 {
		for _, setting := range s.routeSettings {
			s.routing.addRoute(setting.method, setting.routePath, setting.area, setting.reqHandler)
		}
	}
}

func (s *Server) processStaticRequest(w http.ResponseWriter, r *http.Request) {
	filePath := s.MapContentPath(r.URL.Path)
	stat,err := os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			s.err404Handler(w, r)
		} else {
			s.err403Handler(w, r)
		}
		return
	}
	if stat.IsDir() {
		filePath += "/index.html"
	}
	http.ServeFile(w, r, filePath)
}

func (s *Server) processDynamicRequest(w http.ResponseWriter, r *http.Request, urlPath string) interface{} {
	method := strings.ToUpper(r.Method)

	handlers, routeData, area, err := s.routing.lookup(urlPath)
	assert.PanicErr(err)
	var handler ReqHandler
	var ok bool
	if handlers != nil {
		handler, ok = handlers[method]
		if !ok {
			handler, ok = handlers["*"]
		}
	}
	if handler != nil && ok {
		ctxId := atomic.AddUint64(&(s.ctxId), 1)
		var ctx = &HttpCtx{
			req:       r,
			res:       w,
			routeData: routeData,
			server:    s,
			area:      area,
			ctxId:     ctxId,
		}
		s.filters.exec(urlPath, ctx)
		if ctx.ended {
			return &emptyResult{}
		}
		return handler(ctx)
	}
	return nil
}

func (s *Server) flush(w http.ResponseWriter, req *http.Request, result interface{}) {
	switch result.(type) {
	case Result:
		result.(Result).ExecResult(w, req)
		return
	case *url.URL:
		res := result.(*url.URL)
		http.Redirect(w, req, res.String(), 302)
		return
	case string:
		content := result.(string)
		w.Header().Add("Content-Type", "text/plain; charset=utf-8")
		w.Write(str2Byte(content))
		return
	case []byte:
		w.Header().Add("Content-Type", "text/plain; charset=utf-8")
		w.Write(result.([]byte))
		return
	case byte:
		w.Header().Add("Content-Type", "text/plain; charset=utf-8")
		w.Write([]byte{result.(byte)})
		return
	default:
		var cType = req.Header.Get("Content-Type")
		var contentBytes []byte
		var err error
		if cType == "text/xml" {
			contentBytes, err = xml.Marshal(result)
			assert.PanicErr(err)
			w.Header().Add("Content-Type", "text/xml; charset=utf-8")
		} else {
			contentBytes, err = json.Marshal(result)
			assert.PanicErr(err)
			w.Header().Add("Content-Type", "application/json; charset=utf-8")
		}
		w.Write(contentBytes)
	}
}

func (s *Server) initViewEngine() {
	if s.viewEngine == nil {
		s.engineLock.Lock()
		defer s.engineLock.Unlock()
		if s.viewEngine == nil {
			s.viewEngine = NewViewEngine(s.MapRootPath("views"), ".html")
		}
	}
}

func (s *Server) isDynamic(urlPath, cleanUrlPath string) bool {
	if len(urlPath) == 0 || urlPath == "/" {
		return true
	}
	if len(s.urlSuffix) > 0 {
		return strings.HasSuffix(urlPath, s.urlSuffix)
	}
	index1 := strings.LastIndex(urlPath, ".")
	if index1 < 0 {
		return true
	}
	return index1 < strings.LastIndex(cleanUrlPath, "/")
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// catch the panic error
	defer func() {
		rec := recover()
		if rec == nil {
			return
		}
		if _,ok := rec.(*endCtxSignal); ok {
			return
		}
		s.err500Handler(w, r, rec)
		rec1 := recover()
		if rec1 != nil {
			handle500(w, r, rec)
		}
	}()

	var urlPath string
	var urlCleanPath string
	if r.URL.Path == "/" {
		urlPath = "/"
		urlCleanPath = "/"
	} else {
		urlCleanPath = path.Clean(r.URL.Path)
		if strings.HasSuffix(r.URL.Path, "/") {
			urlPath = urlCleanPath + "/"
		} else {
			urlPath = urlCleanPath
		}
	}
	isDynamic := s.isDynamic(urlPath, urlCleanPath)
	if isDynamic {
		if urlCleanPath != "/" && len(s.urlSuffix) > 0 {
			urlCleanPath = strings.TrimRight(urlCleanPath, s.urlSuffix)
		}
		var result= s.processDynamicRequest(w, r, urlCleanPath)
		if result != nil {
			s.flush(w, r, result)
		} else {
			s.err404Handler(w, r)
		}
	} else {
		s.processStaticRequest(w, r)
	}
}<commit_msg>fix route with file extension<commit_after>package mego

import (
	"encoding/json"
	"encoding/xml"
	"net/http"
	"net/url"
	"strings"
	"errors"
	"sync"
	"sync/atomic"
	"path"
	"os"
	"github.com/simbory/mego/assert"
)

type routeSetting struct {
	method     string
	routePath  string
	reqHandler ReqHandler
	area       *Area
}

type endCtxSignal struct {}

type Server struct {
	webRoot       string
	contentRoot   string
	addr          string
	locked        bool
	routing       *routeTree
	initEvents    []func()
	err404Handler http.HandlerFunc
	err500Handler ErrHandler
	err400Handler http.HandlerFunc
	err403Handler http.HandlerFunc
	filters       filterContainer
	routeSettings []*routeSetting
	viewEngine    *ViewEngine
	engineLock    *sync.RWMutex
	urlSuffix     string
	ctxId         uint64
}

// NewServer create a new server
//
// webRoot: the root of this web server. and the content root is '${webRoot}/www'
//
// addr: the address the server is listen on
//
// urlSuffix: the dynamic url suffix. if this value is empty, all the dynamic url
// should be like 'https://example.com/path/to/url' or 'https://example.com/path/to/url/'
func NewServer(webRoot, addr string, urlSuffix string) *Server {
	webRoot = path.Clean(strings.Replace(webRoot, "\\", "/", -1))
	var s = &Server{
		webRoot: webRoot,
		contentRoot: webRoot + "/www",
		addr: addr,
		locked: false,
		routing: newRouteTree(),
		initEvents: []func(){},
		err404Handler: handle404,
		err500Handler: handle500,
		err400Handler: handle400,
		err403Handler: handle403,
		filters: make(filterContainer),
		engineLock: &sync.RWMutex{},
		urlSuffix: urlSuffix,
	}
	return s
}

// assertUnlocked assert that the server is not running
func (s *Server) assertUnlocked() {
	if s.locked {
		panic(errors.New("The s is locked."))
	}
}

func (s *Server) addRoute(m, p string, h ReqHandler) {
	s.routeSettings = append(s.routeSettings, &routeSetting{
		method: m,
		routePath: p,
		reqHandler: h,
		area: nil,
	})
}

func (s *Server) addAreaRoute(m, p string, area *Area, h ReqHandler) {
	s.routeSettings = append(s.routeSettings, &routeSetting{
		method: m,
		routePath: p,
		reqHandler: h,
		area: area,
	})
}

func (s *Server) onInit() {
	if len(s.initEvents) > 0 {
		for _, h := range s.initEvents {
			h()
		}
	}
	if len(s.routeSettings) > 0 {
		for _, setting := range s.routeSettings {
			s.routing.addRoute(setting.method, setting.routePath, setting.area, setting.reqHandler)
		}
	}
}

func (s *Server) processStaticRequest(w http.ResponseWriter, r *http.Request) {
	filePath := s.MapContentPath(r.URL.Path)
	stat,err := os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			s.err404Handler(w, r)
		} else {
			s.err403Handler(w, r)
		}
		return
	}
	if stat.IsDir() {
		filePath += "/index.html"
	}
	http.ServeFile(w, r, filePath)
}

func (s *Server) processDynamicRequest(w http.ResponseWriter, r *http.Request, urlPath string) interface{} {
	method := strings.ToUpper(r.Method)

	handlers, routeData, area, err := s.routing.lookup(urlPath)
	assert.PanicErr(err)
	var handler ReqHandler
	var ok bool
	if handlers != nil {
		handler, ok = handlers[method]
		if !ok {
			handler, ok = handlers["*"]
		}
	}
	if handler != nil && ok {
		ctxId := atomic.AddUint64(&(s.ctxId), 1)
		var ctx = &HttpCtx{
			req:       r,
			res:       w,
			routeData: routeData,
			server:    s,
			area:      area,
			ctxId:     ctxId,
		}
		s.filters.exec(urlPath, ctx)
		if ctx.ended {
			return &emptyResult{}
		}
		return handler(ctx)
	}
	return nil
}

func (s *Server) flush(w http.ResponseWriter, req *http.Request, result interface{}) {
	switch result.(type) {
	case Result:
		result.(Result).ExecResult(w, req)
		return
	case *url.URL:
		res := result.(*url.URL)
		http.Redirect(w, req, res.String(), 302)
		return
	case string:
		content := result.(string)
		w.Header().Add("Content-Type", "text/plain; charset=utf-8")
		w.Write(str2Byte(content))
		return
	case []byte:
		w.Header().Add("Content-Type", "text/plain; charset=utf-8")
		w.Write(result.([]byte))
		return
	case byte:
		w.Header().Add("Content-Type", "text/plain; charset=utf-8")
		w.Write([]byte{result.(byte)})
		return
	default:
		var cType = req.Header.Get("Content-Type")
		var contentBytes []byte
		var err error
		if cType == "text/xml" {
			contentBytes, err = xml.Marshal(result)
			assert.PanicErr(err)
			w.Header().Add("Content-Type", "text/xml; charset=utf-8")
		} else {
			contentBytes, err = json.Marshal(result)
			assert.PanicErr(err)
			w.Header().Add("Content-Type", "application/json; charset=utf-8")
		}
		w.Write(contentBytes)
	}
}

func (s *Server) initViewEngine() {
	if s.viewEngine == nil {
		s.engineLock.Lock()
		defer s.engineLock.Unlock()
		if s.viewEngine == nil {
			s.viewEngine = NewViewEngine(s.MapRootPath("views"), ".html")
		}
	}
}

func (s *Server) isDynamic(urlPath, cleanUrlPath string) bool {
	if len(urlPath) == 0 || urlPath == "/" {
		return true
	}
	if len(s.urlSuffix) > 0 {
		return strings.HasSuffix(urlPath, s.urlSuffix)
	}
	index1 := strings.LastIndex(urlPath, ".")
	if index1 < 0 {
		return true
	}
	return index1 < strings.LastIndex(cleanUrlPath, "/")
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// catch the panic error
	defer func() {
		rec := recover()
		if rec == nil {
			return
		}
		if _,ok := rec.(*endCtxSignal); ok {
			return
		}
		s.err500Handler(w, r, rec)
		rec1 := recover()
		if rec1 != nil {
			handle500(w, r, rec)
		}
	}()

	var urlPath string
	var urlCleanPath string
	if r.URL.Path == "/" {
		urlPath = "/"
		urlCleanPath = "/"
	} else {
		urlCleanPath = path.Clean(r.URL.Path)
		if strings.HasSuffix(r.URL.Path, "/") {
			urlPath = urlCleanPath + "/"
		} else {
			urlPath = urlCleanPath
		}
	}
	isDynamic := s.isDynamic(urlPath, urlCleanPath)
	if isDynamic {
		if urlCleanPath != "/" && len(s.urlSuffix) > 0 {
			urlCleanPath = urlCleanPath[0:len(urlCleanPath)-len(s.urlSuffix)]
		}
		var result= s.processDynamicRequest(w, r, urlCleanPath)
		if result != nil {
			s.flush(w, r, result)
		} else {
			s.err404Handler(w, r)
		}
	} else {
		s.processStaticRequest(w, r)
	}
}<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/byuoitav/av-api/controllers"
	"github.com/byuoitav/hateoas"
	"github.com/jessemillar/health"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/fasthttp"
	"github.com/labstack/echo/middleware"
)

func main() {
	err := hateoas.Load("https://raw.githubusercontent.com/byuoitav/av-api/master/swagger.yml")
	if err != nil {
		fmt.Println("Could not load Swagger file")
		panic(err)
	}

	port := ":8000"
	e := echo.New()
	e.Pre(middleware.RemoveTrailingSlash())

	// GET requests
	e.Get("/", hateoas.RootResponse)

	e.Get("/health", health.Check)

	e.Get("/rooms", controllers.GetAllRooms)
	e.Get("/rooms/:room", controllers.GetRoomByName)

	e.Get("/buildings", controllers.GetAllBuildings)
	e.Get("/buildings/:building", controllers.GetBuildingByName)
	e.Get("/buildings/:building/rooms", controllers.GetAllRoomsByBuilding)
	e.Get("/buildings/:building/rooms/:room", controllers.GetRoomByNameAndBuilding)
	e.Get("/buildings/:building/rooms/:room/signals", controllers.GetAllSignalsByRoomAndBuilding)
	e.Get("/buildings/:building/rooms/:room/signals/:signal", controllers.GetSignalByRoomAndBuilding)

	// POST requests
	e.Post("/rooms", controllers.UnimplementedResponse)
	e.Post("/buildings", controllers.UnimplementedResponse)
	e.Post("/buildings/:building/rooms/:room/signals", controllers.UnimplementedResponse)

	// PUT requests
	e.Put("/rooms/:room", controllers.UnimplementedResponse)
	e.Put("/buildings/:building", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room", controllers.UnimplementedResponse)
	e.Put("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)

	// DELETE requests
	e.Delete("/rooms/:room", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room", controllers.UnimplementedResponse)
	e.Delete("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)

	fmt.Printf("AV API is listening on %s\n", port)
	e.Run(fasthttp.New(port))
}
<commit_msg>Making echo routing more verbose<commit_after>package main

import (
	"fmt"

	"github.com/byuoitav/av-api/controllers"
	"github.com/byuoitav/hateoas"
	"github.com/jessemillar/health"
	"github.com/labstack/echo"
	"github.com/labstack/echo/engine/fasthttp"
	"github.com/labstack/echo/middleware"
)

func main() {
	err := hateoas.Load("https://raw.githubusercontent.com/byuoitav/av-api/master/swagger.yml")
	if err != nil {
		fmt.Println("Could not load Swagger file")
		panic(err)
	}

	port := ":8000"
	router := echo.New()
	router.Pre(middleware.RemoveTrailingSlash())

	// GET requests
	router.Get("/", hateoas.RootResponse)

	router.Get("/health", health.Check)

	router.Get("/rooms", controllers.GetAllRooms)
	router.Get("/rooms/:room", controllers.GetRoomByName)

	router.Get("/buildings", controllers.GetAllBuildings)
	router.Get("/buildings/:building", controllers.GetBuildingByName)
	router.Get("/buildings/:building/rooms", controllers.GetAllRoomsByBuilding)
	router.Get("/buildings/:building/rooms/:room", controllers.GetRoomByNameAndBuilding)
	router.Get("/buildings/:building/rooms/:room/signals", controllers.GetAllSignalsByRoomAndBuilding)
	router.Get("/buildings/:building/rooms/:room/signals/:signal", controllers.GetSignalByRoomAndBuilding)

	// POST requests
	router.Post("/rooms", controllers.UnimplementedResponse)
	router.Post("/buildings", controllers.UnimplementedResponse)
	router.Post("/buildings/:building/rooms/:room/signals", controllers.UnimplementedResponse)

	// PUT requests
	router.Put("/rooms/:room", controllers.UnimplementedResponse)
	router.Put("/buildings/:building", controllers.UnimplementedResponse)
	router.Put("/buildings/:building/rooms/:room", controllers.UnimplementedResponse)
	router.Put("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)

	// DELETE requests
	router.Delete("/rooms/:room", controllers.UnimplementedResponse)
	router.Delete("/buildings/:building", controllers.UnimplementedResponse)
	router.Delete("/buildings/:building/rooms/:room", controllers.UnimplementedResponse)
	router.Delete("/buildings/:building/rooms/:room/signals/:signal", controllers.UnimplementedResponse)

	fmt.Printf("AV API is listening on %s\n", port)
	router.Run(fasthttp.New(port))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/anachronistic/apns"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type Post struct {
	ID           bson.ObjectId `bson:"_id,omitempty"`
	User         string
	Position     string
	FileName     string
	IsHorizontal string
	Filter       string
	Separator    string
}

type Push struct {
	ID    bson.ObjectId `bson:"_id,omitempty"`
	User  string
	Token string
}

func getDeviceTokenForUser(user string) string {
	session, _ := mgo.Dial("127.0.0.1")
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	c := session.DB("chocoshot").C("push")
	var result Push
	c.Find(bson.M{"user": user}).One(&result)
	return result.Token
}

func setPushTokenForUser(user string, token string) {
	session, _ := mgo.Dial("127.0.0.1")
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	c := session.DB("chocoshot").C("push")
	c.Insert(&Push{User: user, Token: token})
}

func sendPushToUser(user string, fromUser string) {
	payload := apns.NewPayload()
	payload.Alert = fromUser + " vous a envoyé un shot 😏📸"
	payload.Badge = 1
	payload.Sound = "bingbong.aiff"

	pn := apns.NewPushNotification()
	pn.DeviceToken = getDeviceTokenForUser(user)
	pn.AddPayload(payload)

	client := apns.NewClient("gateway.sandbox.push.apple.com:2195", "PushChocoshotCert.pem", "key.unencrypted.pem")
	client.Send(pn)
}

func insertDatabase(p Post) {
	session, _ := mgo.Dial("127.0.0.1")
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	c := session.DB("chocoshot").C("post")
	c.Insert(&Post{User: p.User, Position: p.Position, FileName: p.FileName, IsHorizontal: p.IsHorizontal, Filter: p.Filter, Separator: p.Separator})
}

func getFromDataBaseWithUser(u string, deletation bool) Post {
	session, _ := mgo.Dial("127.0.0.1")
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	c := session.DB("chocoshot").C("post")
	var results Post
	c.Find(bson.M{"user": u}).One(&results)
	if deletation == true {
		c.Remove(bson.M{"filename": results.FileName})
	}
	return results
}

func getPost(w http.ResponseWriter, r *http.Request) {
	user := r.Header.Get("user")
	deletation := r.Header.Get("deletation")
	var res = getFromDataBaseWithUser(user, deletation == "true")
	json.NewEncoder(w).Encode(res)
}

func updatePushToken(w http.ResponseWriter, r *http.Request) {
	user := strings.TrimSpace(r.FormValue("user"))
	token := strings.TrimSpace(r.FormValue("token"))
	setPushTokenForUser(user, token)
	var res = getDeviceTokenForUser(user)
	json.NewEncoder(w).Encode(res)
}

func uploadPost(w http.ResponseWriter, r *http.Request) {
	user := strings.TrimSpace(r.FormValue("user"))
	destination := strings.TrimSpace(r.FormValue("destination"))
	position := strings.TrimSpace(r.FormValue("position"))
	isHorizontal := strings.TrimSpace(r.FormValue("isHorizontal"))
	filter := strings.TrimSpace(r.FormValue("filter"))
	separator := strings.TrimSpace(r.FormValue("separator"))

	r.ParseMultipartForm(32 << 20)
	file, _, err := r.FormFile("uploadfile")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	fileName := RandomString(20)
	f, err := os.OpenFile("./img/"+fileName+".png", os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}

	insertDatabase(Post{User: destination, Position: position, FileName: fileName + ".png", IsHorizontal: isHorizontal, Filter: filter, Separator: separator})

	sendPushToUser(destination, user)

	defer f.Close()
	io.Copy(f, file)
}

func RandomString(strlen int) string {
	rand.Seed(time.Now().UTC().UnixNano())
	const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
	result := make([]byte, strlen)
	for i := 0; i < strlen; i++ {
		result[i] = chars[rand.Intn(len(chars))]
	}
	return string(result)
}

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("/post", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "GET" {
			getPost(w, r)
		} else {
			uploadPost(w, r)
		}
	})

	mux.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			updatePushToken(w, r)
		}
	})

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "hello world")
	})

	mux.Handle("/image/", http.StripPrefix("/image/", http.FileServer(http.Dir("./img/"))))
	/*if strings.HasPrefix(r.URL.RequestURI(), "/image/") {
		file := strings.Replace(r.URL.RequestURI(), "/image/", "./img/", 1)
		fmt.Println("deleting " + file)
		defer os.Remove(file)
	}*/

	fmt.Println("listening at :9000")
	http.ListenAndServe(":9000", mux)
}
<commit_msg>fix bug<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/anachronistic/apns"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type Post struct {
	ID           bson.ObjectId `bson:"_id,omitempty"`
	User         string
	Position     string
	FileName     string
	IsHorizontal string
	Filter       string
	Separator    string
}

type Push struct {
	ID    bson.ObjectId `bson:"_id,omitempty"`
	User  string
	Token string
}

func getDeviceTokenForUser(user string) string {
	session, _ := mgo.Dial("127.0.0.1")
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	c := session.DB("chocoshot").C("push")
	var result Push
	c.Find(bson.M{"user": user}).One(&result)
	return result.Token
}

func setPushTokenForUser(user string, token string) {
	session, _ := mgo.Dial("127.0.0.1")
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	c := session.DB("chocoshot").C("push")
	c.Insert(&Push{User: user, Token: token})
}

func sendPushToUser(user string, fromUser string) {
	payload := apns.NewPayload()
	payload.Alert = fromUser + " vous a envoyé un shot 😏📸"
	payload.Badge = 1
	payload.Sound = "bingbong.aiff"

	pn := apns.NewPushNotification()
	pn.DeviceToken = getDeviceTokenForUser(user)
	pn.AddPayload(payload)

	client := apns.NewClient("gateway.push.apple.com:2195", "./Push/Prod/PushChocoshotCert.pem", "./Push/Prod/key.unencrypted.pem")
	client.Send(pn)
}

func insertDatabase(p Post) {
	session, _ := mgo.Dial("127.0.0.1")
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	c := session.DB("chocoshot").C("post")
	c.Insert(&Post{User: p.User, Position: p.Position, FileName: p.FileName, IsHorizontal: p.IsHorizontal, Filter: p.Filter, Separator: p.Separator})
}

func getFromDataBaseWithUser(u string, deletation bool) Post {
	session, _ := mgo.Dial("127.0.0.1")
	defer session.Close()
	session.SetMode(mgo.Monotonic, true)
	c := session.DB("chocoshot").C("post")
	var results Post
	c.Find(bson.M{"user": u}).One(&results)
	if deletation == true {
		c.Remove(bson.M{"filename": results.FileName})
	}
	return results
}

func getPost(w http.ResponseWriter, r *http.Request) {
	user := r.Header.Get("user")
	deletation := r.Header.Get("deletation")
	var res = getFromDataBaseWithUser(user, deletation == "true")
	json.NewEncoder(w).Encode(res)
}

func updatePushToken(w http.ResponseWriter, r *http.Request) {
	user := strings.TrimSpace(r.FormValue("user"))
	token := strings.TrimSpace(r.FormValue("token"))
	setPushTokenForUser(user, token)
	var res = getDeviceTokenForUser(user)
	json.NewEncoder(w).Encode(res)
}

func uploadPost(w http.ResponseWriter, r *http.Request) {
	user := strings.TrimSpace(r.FormValue("user"))
	destination := strings.TrimSpace(r.FormValue("destination"))
	position := strings.TrimSpace(r.FormValue("position"))
	isHorizontal := strings.TrimSpace(r.FormValue("isHorizontal"))
	filter := strings.TrimSpace(r.FormValue("filter"))
	separator := strings.TrimSpace(r.FormValue("separator"))

	r.ParseMultipartForm(32 << 20)
	file, _, err := r.FormFile("uploadfile")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer file.Close()

	fileName := RandomString(20)
	f, err := os.OpenFile("./img/"+fileName+".png", os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Println(err)
		return
	}

	insertDatabase(Post{User: destination, Position: position, FileName: fileName + ".png", IsHorizontal: isHorizontal, Filter: filter, Separator: separator})

	sendPushToUser(destination, user)

	defer f.Close()
	io.Copy(f, file)
}

func RandomString(strlen int) string {
	rand.Seed(time.Now().UTC().UnixNano())
	const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
	result := make([]byte, strlen)
	for i := 0; i < strlen; i++ {
		result[i] = chars[rand.Intn(len(chars))]
	}
	return string(result)
}

func main() {
	mux := http.NewServeMux()

	mux.HandleFunc("/post", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "GET" {
			getPost(w, r)
		} else {
			uploadPost(w, r)
		}
	})

	mux.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			updatePushToken(w, r)
		}
	})

	mux.Handle("/image/", http.StripPrefix("/image/", http.FileServer(http.Dir("./img/"))))
	/*if strings.HasPrefix(r.URL.RequestURI(), "/image/") {
		file := strings.Replace(r.URL.RequestURI(), "/image/", "./img/", 1)
		fmt.Println("deleting " + file)
		defer os.Remove(file)
	}*/

	fmt.Println("listening at :80")
	http.ListenAndServe(":80", mux)
}
<|endoftext|>


<commit_before>package steamscreenshots

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/big"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type Settings struct {
	RemoteDirectory string
	Address         string
	AppidOverrides  []struct {
		Appid string `json:"id"`
		Name  string `json:"name"`
	}
	RefreshInterval int    // In minutes
	ApiKey          string // This will be regenerated if it is empty.
}

var re_gamename = regexp.MustCompile(`<td itemprop="name">(.+?)</td>`)

var (
	gitCommit string
	version   string
)

// Structure of json from steam's servers
type steamapps struct {
	Applist struct {
		Apps []struct {
			Appid uint64 `json:"appid"`
			Name  string `json:"name"`
		} `json:"apps"`
	} `json:"applist"`
}

type Server struct {
	// stats stuff
	startTime time.Time
	lastScan  time.Time

	lastUpdate *time.Time

	settings Settings

	Games      *GameList
	ImageCache *GameImages

	SettingsFile string
}

func (s *Server) Run() {
	fmt.Println("Starting server")

	if len(gitCommit) == 0 {
		gitCommit = "Missing commit hash"
	}

	if len(version) == 0 {
		version = "Missing version info"
	}
	fmt.Printf("%s@%s\n", version, gitCommit)

	s.startTime = time.Now()
	s.Games = NewGameList()

	if err := s.loadSettings("settings.json"); err != nil {
		fmt.Printf("Error loading settings: %s\n", err)
		return
	}

	if err := init_templates(); err != nil {
		fmt.Printf("Error loading templates: %s\n", err)
		return
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/", s.handler_main)
	mux.HandleFunc("/thumb/", s.handler_thumb)
	mux.HandleFunc("/img/", s.handler_image)
	mux.HandleFunc("/banner/", s.handler_banner)
	mux.HandleFunc("/static/", s.handler_static)
	mux.HandleFunc("/debug/", s.handler_debug)
	mux.HandleFunc("/api/get-cache", s.handler_api_cache)
	mux.HandleFunc("/api/get-games", s.handler_api_games)
	mux.HandleFunc("/api/add-image", s.handler_api_addImage)

	server := &http.Server{
		Addr:           s.settings.Address,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	var err error
	s.ImageCache, err = LoadImageCache("image.cache")
	if err != nil {
		fmt.Println("Unable to load image.cache: ", err)

		s.ImageCache = NewGameImages()
		err = s.scan(true)
		if err != nil {
			fmt.Println("Initial scan error: ", err)
			return
		}
	} else {
		fmt.Println("Refreshing RemoteDirectory...")
		if err = s.scan(true); err != nil {
			fmt.Println("Error refreshing RemoteDirectory: ", err)
			return
		}
	}
	fmt.Println("Initial scan OK")

	// Fire and forget.  TODO: graceful shutdown
	go func() {
		for {
			time.Sleep(time.Minute * time.Duration(s.settings.RefreshInterval))
			if err := s.scan(false); err != nil {
				fmt.Printf("Error scanning: %s", err)
			}
		}
	}()

	// Generate a new API key if it's empty
	if s.settings.ApiKey == "" {
		out := ""
		large := big.NewInt(int64(1 << 60))
		large = large.Add(large, large)
		for len(out) < 50 {
			num, err := rand.Int(rand.Reader, large)
			if err != nil {
				panic("Error generating session key: " + err.Error())
			}
			out = fmt.Sprintf("%s%X", out, num)
		}
		s.settings.ApiKey = out
		fmt.Println("New API key generated: " + s.settings.ApiKey)
		if err = s.saveSettings("settings.json"); err != nil {
			panic(fmt.Sprintf("unable to save settings: %v", err))
		}
	} else {
		fmt.Printf("using API key in config: %q\n", s.settings.ApiKey)
	}

	go func(s *Server) {
		for {
			s.ImageCache.Save("image.cache")
			time.Sleep(10 * time.Minute)
		}
	}(s)

	fmt.Println("Listening on address: " + s.settings.Address)
	fmt.Println("Fisnished startup.")
	server.ListenAndServe()
}

// TODO: use GameImages.FullScan for this?
func (s *Server) scan(printOutput bool) error {
	s.lastScan = time.Now()

	if printOutput {
		fmt.Printf("Scanning %q\n", s.settings.RemoteDirectory)
	}

	dir, err := filepath.Glob(filepath.Join(s.settings.RemoteDirectory, "*"))
	if err != nil {
		return fmt.Errorf("Unable to glob RemoteDirectory: %s", err)
	}
	tmpTree := make(map[string][]string)

	for _, d := range dir {
		base := filepath.Base(d)

		// Ignore dotfiles
		if strings.HasPrefix(base, ".") {
			continue
		}

		if printOutput {
			fmt.Printf("[%s] %s\n", base, s.Games.Get(base))
		}

		jpg, err := filepath.Glob(filepath.Join(d, "screenshots", "*.jpg"))
		if err != nil {
			fmt.Printf("JPG glob error in %q: %s", d, err)
			continue
		}
		tmpTree[base] = jpg

		// TODO: merge ImageCache.ScanPath() and ImagePath.RefreshPath(), possibly removing the jpg glob above as well.
		err = s.ImageCache.ScanPath(d)
		if err != nil {
			fmt.Println(err)
		}
	}

	// Write cache to disk after it's updated in-memory so failing this doesn't skip updating.
	if err := s.ImageCache.Save("image.cache"); err != nil {
		return fmt.Errorf("Unable to save image cache: %s\n", err)
	}

	return nil
}

func SliceContains(s []string, val string) bool {
	for _, v := range s {
		if v == val {
			return true
		}
	}
	return false
}

func (s *Server) saveSettings(filename string) error {
	raw, err := json.MarshalIndent(s.settings, "", "    ")
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(filename, raw, 0600)
	if err != nil {
		return err
	}

	return os.Chmod(filename, 0600)
}

// FIXME: pass the filename in here as an argument
func (s *Server) loadSettings(filename string) error {
	settingsFile, err := ioutil.ReadFile(filename)
	if err != nil {
		return fmt.Errorf("Error reading settings file: %s", err)
	}

	err = json.Unmarshal(settingsFile, &s.settings)
	if err != nil {
		return fmt.Errorf("Error unmarshaling settings: %s", err)
	}

	fmt.Println("Settings loaded")

	if s.settings.RefreshInterval < 1 {
		s.settings.RefreshInterval = 1
	}

	return s.loadGames()
}

func (s *Server) loadGames() error {
	if ex := exists("games.cache"); !ex {
		fmt.Println("games.cache doesn't exist.  Getting a new one.")
		if err := s.updateGamesJson(); err != nil {
			return fmt.Errorf("Unable update game list: %s", err)
		}
	}

	gamesFile, err := ioutil.ReadFile("games.cache")
	if err != nil {
		return fmt.Errorf("Error reading games file: %s", err)
	}

	var games GameIDs
	err = json.Unmarshal(gamesFile, &games)
	if err != nil {
		return fmt.Errorf("Error unmarshaling games: %s", err)
	}

	s.Games.Update(games)
	//fmt.Println("Number of games loaded: ", Games.Length())
	return nil
}

func (s *Server) getGameName(appid string) (string, error) {
	if appid == ".stfolder" {
		return appid, nil
	}

	//fmt.Printf("Getting name for appid %q\n", appid)
	if name := s.Games.Get(appid); name != appid {
		return name, nil
	}

	// Large appid, must be a non-steam game.  This could have some edge cases
	// as non-steam games' appids are CRCs.
	if len(appid) > 18 {
		return s.Games.Set(appid, fmt.Sprintf("Non-Steam game (%s)", appid)), nil
	}

	// TODO: rate limiting/cache age
	if err := s.updateGamesJson(); err == nil {
		if name := s.Games.Get(appid); name != appid {
			return name, nil
		}
	}
	return appid, nil
}

// Update the local cache of appids from steam's servers.
func (s *Server) updateGamesJson() error {
	if s.lastUpdate != nil && time.Since(*s.lastUpdate).Minutes() < 30 {
		//return fmt.Errorf("Cache still good.")
		fmt.Println("Not updating games list; cache still good.")
		return nil
	}

	now := time.Now()
	//fmt.Printf("time.Now(): {}\n", now)
	s.lastUpdate = &now

	fmt.Println("Updating games list")
	resp, err := http.Get("http://api.steampowered.com/ISteamApps/GetAppList/v2")
	if err != nil {
		return fmt.Errorf("Unable to get appid list from steam: %s", err)
	}
	defer resp.Body.Close()

	js, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Unable to read appid json: %s", err)
	}

	alist := &steamapps{}
	if err := json.Unmarshal(js, alist); err != nil {
		return fmt.Errorf("Unable to unmarshal json: %s", err)
	}

	for _, a := range alist.Applist.Apps {
		id := fmt.Sprintf("%d", a.Appid)
		s.Games.Set(id, a.Name)

	}

	for _, ovr := range s.settings.AppidOverrides {
		s.Games.Set(ovr.Appid, ovr.Name)
		fmt.Printf("Setting override for [%s]: %q\n", ovr.Appid, ovr.Name)
	}

	// save games.cache
	games := s.Games.GetMap()
	marshaled, err := json.Marshal(games)
	if err != nil {
		return fmt.Errorf("Unable to marshal game json: %s", err)
	}

	err = ioutil.WriteFile("games.cache", marshaled, 0644)
	if err != nil {
		return fmt.Errorf("Unable to save games.cache: %s", err)
	}

	fmt.Printf("Finished updating games list.  Appids: %d\n", len(games))
	return nil
}

// Returns a filename
func (s *Server) getGameBanner(appid uint64) (string, error) {
	appstr := fmt.Sprintf("%d", appid)
	if exist := exists("banners/" + appstr + ".jpg"); exist {
		return "banners/" + appstr + ".jpg", nil
	}

	resp, err := http.Get("http://cdn.akamai.steamstatic.com/steam/apps/" + appstr + "/header.jpg")
	if err != nil {
		return "", fmt.Errorf("Unable to DL header: %s", err)
	}

	if resp.StatusCode >= 400 && resp.StatusCode < 500 {
		// Game not found.  Use unknown.

		raw, err := ioutil.ReadFile("banners/unknown.jpg")
		if err != nil {
			return "", fmt.Errorf("Unable to read unknown.jpg")
		}

		if err = ioutil.WriteFile("banners/"+appstr+".jpg", raw, 0777); err != nil {
			return "", fmt.Errorf("Unable to save file: %s", err)
		}

		return "banners/" + appstr + ".jpg", nil
	}

	defer resp.Body.Close()

	file, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("Unable to read file: %s", err)
	}

	if err = ioutil.WriteFile("banners/"+appstr+".jpg", file, 0777); err != nil {
		return "", fmt.Errorf("Unable to save file: %s", err)
	}

	return "banners/" + appstr + ".jpg", nil
}

// exists returns whether the given file or directory exists or not.
// Taken from https://stackoverflow.com/a/10510783
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return true
}
<commit_msg>Don't add non-directories in root directory<commit_after>package steamscreenshots

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/big"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type Settings struct {
	RemoteDirectory string
	Address         string
	AppidOverrides  []struct {
		Appid string `json:"id"`
		Name  string `json:"name"`
	}
	RefreshInterval int    // In minutes
	ApiKey          string // This will be regenerated if it is empty.
}

var re_gamename = regexp.MustCompile(`<td itemprop="name">(.+?)</td>`)

var (
	gitCommit string
	version   string
)

// Structure of json from steam's servers
type steamapps struct {
	Applist struct {
		Apps []struct {
			Appid uint64 `json:"appid"`
			Name  string `json:"name"`
		} `json:"apps"`
	} `json:"applist"`
}

type Server struct {
	// stats stuff
	startTime time.Time
	lastScan  time.Time

	lastUpdate *time.Time

	settings Settings

	Games      *GameList
	ImageCache *GameImages

	SettingsFile string
}

func (s *Server) Run() {
	fmt.Println("Starting server")

	if len(gitCommit) == 0 {
		gitCommit = "Missing commit hash"
	}

	if len(version) == 0 {
		version = "Missing version info"
	}
	fmt.Printf("%s@%s\n", version, gitCommit)

	s.startTime = time.Now()
	s.Games = NewGameList()

	if err := s.loadSettings("settings.json"); err != nil {
		fmt.Printf("Error loading settings: %s\n", err)
		return
	}

	if err := init_templates(); err != nil {
		fmt.Printf("Error loading templates: %s\n", err)
		return
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/", s.handler_main)
	mux.HandleFunc("/thumb/", s.handler_thumb)
	mux.HandleFunc("/img/", s.handler_image)
	mux.HandleFunc("/banner/", s.handler_banner)
	mux.HandleFunc("/static/", s.handler_static)
	mux.HandleFunc("/debug/", s.handler_debug)
	mux.HandleFunc("/api/get-cache", s.handler_api_cache)
	mux.HandleFunc("/api/get-games", s.handler_api_games)
	mux.HandleFunc("/api/add-image", s.handler_api_addImage)

	server := &http.Server{
		Addr:           s.settings.Address,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	var err error
	s.ImageCache, err = LoadImageCache("image.cache")
	if err != nil {
		fmt.Println("Unable to load image.cache: ", err)

		s.ImageCache = NewGameImages()
		err = s.scan(true)
		if err != nil {
			fmt.Println("Initial scan error: ", err)
			return
		}
	} else {
		fmt.Println("Refreshing RemoteDirectory...")
		if err = s.scan(true); err != nil {
			fmt.Println("Error refreshing RemoteDirectory: ", err)
			return
		}
	}
	fmt.Println("Initial scan OK")

	// Fire and forget.  TODO: graceful shutdown
	go func() {
		for {
			time.Sleep(time.Minute * time.Duration(s.settings.RefreshInterval))
			if err := s.scan(false); err != nil {
				fmt.Printf("Error scanning: %s", err)
			}
		}
	}()

	// Generate a new API key if it's empty
	if s.settings.ApiKey == "" {
		out := ""
		large := big.NewInt(int64(1 << 60))
		large = large.Add(large, large)
		for len(out) < 50 {
			num, err := rand.Int(rand.Reader, large)
			if err != nil {
				panic("Error generating session key: " + err.Error())
			}
			out = fmt.Sprintf("%s%X", out, num)
		}
		s.settings.ApiKey = out
		fmt.Println("New API key generated: " + s.settings.ApiKey)
		if err = s.saveSettings("settings.json"); err != nil {
			panic(fmt.Sprintf("unable to save settings: %v", err))
		}
	} else {
		fmt.Printf("using API key in config: %q\n", s.settings.ApiKey)
	}

	go func(s *Server) {
		for {
			s.ImageCache.Save("image.cache")
			time.Sleep(10 * time.Minute)
		}
	}(s)

	fmt.Println("Listening on address: " + s.settings.Address)
	fmt.Println("Fisnished startup.")
	server.ListenAndServe()
}

// TODO: use GameImages.FullScan for this?
func (s *Server) scan(printOutput bool) error {
	s.lastScan = time.Now()

	if printOutput {
		fmt.Printf("Scanning %q\n", s.settings.RemoteDirectory)
	}

	dir, err := filepath.Glob(filepath.Join(s.settings.RemoteDirectory, "*"))
	if err != nil {
		return fmt.Errorf("Unable to glob RemoteDirectory: %s", err)
	}
	tmpTree := make(map[string][]string)

	for _, d := range dir {
		base := filepath.Base(d)

		// Ignore dotfiles
		if strings.HasPrefix(base, ".") {
			continue
		}

		if !isDir(d) {
			fmt.Printf("%q is not a directory\n", d)
			continue
		}

		if printOutput {
			fmt.Printf("[%s] %s\n", base, s.Games.Get(base))
		}

		jpg, err := filepath.Glob(filepath.Join(d, "screenshots", "*.jpg"))
		if err != nil {
			fmt.Printf("JPG glob error in %q: %s", d, err)
			continue
		}
		tmpTree[base] = jpg

		// TODO: merge ImageCache.ScanPath() and ImagePath.RefreshPath(), possibly removing the jpg glob above as well.
		err = s.ImageCache.ScanPath(d)
		if err != nil {
			fmt.Println(err)
		}
	}

	// Write cache to disk after it's updated in-memory so failing this doesn't skip updating.
	if err := s.ImageCache.Save("image.cache"); err != nil {
		return fmt.Errorf("Unable to save image cache: %s\n", err)
	}

	return nil
}

func SliceContains(s []string, val string) bool {
	for _, v := range s {
		if v == val {
			return true
		}
	}
	return false
}

func (s *Server) saveSettings(filename string) error {
	raw, err := json.MarshalIndent(s.settings, "", "    ")
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(filename, raw, 0600)
	if err != nil {
		return err
	}

	return os.Chmod(filename, 0600)
}

// FIXME: pass the filename in here as an argument
func (s *Server) loadSettings(filename string) error {
	settingsFile, err := ioutil.ReadFile(filename)
	if err != nil {
		return fmt.Errorf("Error reading settings file: %s", err)
	}

	err = json.Unmarshal(settingsFile, &s.settings)
	if err != nil {
		return fmt.Errorf("Error unmarshaling settings: %s", err)
	}

	fmt.Println("Settings loaded")

	if s.settings.RefreshInterval < 1 {
		s.settings.RefreshInterval = 1
	}

	return s.loadGames()
}

func (s *Server) loadGames() error {
	if ex := exists("games.cache"); !ex {
		fmt.Println("games.cache doesn't exist.  Getting a new one.")
		if err := s.updateGamesJson(); err != nil {
			return fmt.Errorf("Unable update game list: %s", err)
		}
	}

	gamesFile, err := ioutil.ReadFile("games.cache")
	if err != nil {
		return fmt.Errorf("Error reading games file: %s", err)
	}

	var games GameIDs
	err = json.Unmarshal(gamesFile, &games)
	if err != nil {
		return fmt.Errorf("Error unmarshaling games: %s", err)
	}

	s.Games.Update(games)
	//fmt.Println("Number of games loaded: ", Games.Length())
	return nil
}

func (s *Server) getGameName(appid string) (string, error) {
	if appid == ".stfolder" {
		return appid, nil
	}

	//fmt.Printf("Getting name for appid %q\n", appid)
	if name := s.Games.Get(appid); name != appid {
		return name, nil
	}

	// Large appid, must be a non-steam game.  This could have some edge cases
	// as non-steam games' appids are CRCs.
	if len(appid) > 18 {
		return s.Games.Set(appid, fmt.Sprintf("Non-Steam game (%s)", appid)), nil
	}

	// TODO: rate limiting/cache age
	if err := s.updateGamesJson(); err == nil {
		if name := s.Games.Get(appid); name != appid {
			return name, nil
		}
	}
	return appid, nil
}

// Update the local cache of appids from steam's servers.
func (s *Server) updateGamesJson() error {
	if s.lastUpdate != nil && time.Since(*s.lastUpdate).Minutes() < 30 {
		//return fmt.Errorf("Cache still good.")
		fmt.Println("Not updating games list; cache still good.")
		return nil
	}

	now := time.Now()
	//fmt.Printf("time.Now(): {}\n", now)
	s.lastUpdate = &now

	fmt.Println("Updating games list")
	resp, err := http.Get("http://api.steampowered.com/ISteamApps/GetAppList/v2")
	if err != nil {
		return fmt.Errorf("Unable to get appid list from steam: %s", err)
	}
	defer resp.Body.Close()

	js, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("Unable to read appid json: %s", err)
	}

	alist := &steamapps{}
	if err := json.Unmarshal(js, alist); err != nil {
		return fmt.Errorf("Unable to unmarshal json: %s", err)
	}

	for _, a := range alist.Applist.Apps {
		id := fmt.Sprintf("%d", a.Appid)
		s.Games.Set(id, a.Name)

	}

	for _, ovr := range s.settings.AppidOverrides {
		s.Games.Set(ovr.Appid, ovr.Name)
		fmt.Printf("Setting override for [%s]: %q\n", ovr.Appid, ovr.Name)
	}

	// save games.cache
	games := s.Games.GetMap()
	marshaled, err := json.Marshal(games)
	if err != nil {
		return fmt.Errorf("Unable to marshal game json: %s", err)
	}

	err = ioutil.WriteFile("games.cache", marshaled, 0644)
	if err != nil {
		return fmt.Errorf("Unable to save games.cache: %s", err)
	}

	fmt.Printf("Finished updating games list.  Appids: %d\n", len(games))
	return nil
}

// Returns a filename
func (s *Server) getGameBanner(appid uint64) (string, error) {
	appstr := fmt.Sprintf("%d", appid)
	if exist := exists("banners/" + appstr + ".jpg"); exist {
		return "banners/" + appstr + ".jpg", nil
	}

	resp, err := http.Get("http://cdn.akamai.steamstatic.com/steam/apps/" + appstr + "/header.jpg")
	if err != nil {
		return "", fmt.Errorf("Unable to DL header: %s", err)
	}

	if resp.StatusCode >= 400 && resp.StatusCode < 500 {
		// Game not found.  Use unknown.

		raw, err := ioutil.ReadFile("banners/unknown.jpg")
		if err != nil {
			return "", fmt.Errorf("Unable to read unknown.jpg")
		}

		if err = ioutil.WriteFile("banners/"+appstr+".jpg", raw, 0777); err != nil {
			return "", fmt.Errorf("Unable to save file: %s", err)
		}

		return "banners/" + appstr + ".jpg", nil
	}

	defer resp.Body.Close()

	file, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("Unable to read file: %s", err)
	}

	if err = ioutil.WriteFile("banners/"+appstr+".jpg", file, 0777); err != nil {
		return "", fmt.Errorf("Unable to save file: %s", err)
	}

	return "banners/" + appstr + ".jpg", nil
}

// exists returns whether the given file or directory exists or not.
// Taken from https://stackoverflow.com/a/10510783
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return true
}

func isDir(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}

	return fi.IsDir()
}
<|endoftext|>


<commit_before>package main

import (
  "github.com/codegangsta/martini"
  "github.com/martini-contrib/render"
)

func main() {
  m := martini.Classic()
  m.Use(render.Renderer())

  m.Get("/", func(r render.Render) {
    r.HTML(200, "base", "yolo")
  })

  m.Run()
}
<commit_msg>taaaaaabs<commit_after>package main

import (
    "github.com/codegangsta/martini"
    "github.com/martini-contrib/render"
)

func main() {
    m := martini.Classic()
    m.Use(render.Renderer())

    m.Get("/", func(r render.Render) {
        r.HTML(200, "base", "yolo")
    })

    m.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	"github.com/UniversityRadioYork/2016-site/controllers"
	"github.com/UniversityRadioYork/2016-site/structs"
	"github.com/UniversityRadioYork/myradio-go"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
)

// Server is the type of the main 2016site web application.
type Server struct {
	*negroni.Negroni
}

// NewServer creates a 2016site server based on the config c.
func NewServer(c *structs.Config) (*Server, error) {

	s := Server{negroni.Classic()}

	session, err := myradio.NewSessionFromKeyFile()

	if err != nil {
		return &s, err
	}

	router := mux.NewRouter().StrictSlash(true)

	getRouter := router.Methods("GET").Subrouter()
	postRouter := router.Methods("POST").Subrouter()

	// Routes go in here
	nfc := controllers.NewNotFoundController(c)
	router.NotFoundHandler = http.HandlerFunc(nfc.Get)

	ic := controllers.NewIndexController(session, c)
	getRouter.HandleFunc("/", ic.Get)

	sc := controllers.NewSearchController(session, c)
	getRouter.HandleFunc("/search/", sc.Get)

	showC := controllers.NewShowController(session, c)
	//	getRouter.HandleFunc("/schedule/shows", showC.Get) // @TODO: Implement this
	getRouter.HandleFunc("/schedule/shows/{id:[0-9]+}/", showC.GetShow).Name("show")
	getRouter.HandleFunc("/schedule/shows/timeslots/{id:[0-9]+}/", showC.GetTimeslot).Name("timeslot")
	getRouter.HandleFunc("/schedule/shows/seasons/{id:[0-9]+}/", showC.GetSeason).Name("season")

	// NOTE: NewScheduleWeekController assumes 'timeslot' is installed BEFORE it is called.
	schedWeekC := controllers.NewScheduleWeekController(session, getRouter, c)
	getRouter.HandleFunc("/schedule/thisweek/", schedWeekC.GetThisWeek).Name("schedule-thisweek")

	getRouter.HandleFunc("/schedule/{year:[1-9][0-9][0-9][0-9]}/w{week:[0-5]?[0-9]}/", schedWeekC.GetByYearWeek).Name("schedule-week")
	// This route exists so that day schedule links from the previous website aren't broken.
	getRouter.HandleFunc("/schedule/{year:[1-9][0-9][0-9][0-9]}/w{week:[0-5]?[0-9]}/{day:[1-7]}/", schedWeekC.GetByYearWeek).Name("schedule-week-day-compat")

	onDemandC := controllers.NewOnDemandController(session, c)
	getRouter.HandleFunc("/ontap/", onDemandC.Get)
	// Backwards compatability with previous sites
	getRouter.HandleFunc("/uryplayer/", onDemandC.Get)

	podcastsC := controllers.NewPodcastController(session, c)
	getRouter.HandleFunc("/podcasts/", podcastsC.GetAllPodcasts)
	getRouter.HandleFunc("/podcasts/{id:[0-9]+}/", podcastsC.Get)
	getRouter.HandleFunc("/podcasts/{id:[0-9]+}/player/", podcastsC.GetEmbed)
	// Backwards compatability with previous sites
	getRouter.HandleFunc("/uryplayer/podcasts/", podcastsC.GetAllPodcasts)
	getRouter.HandleFunc("/uryplayer/podcasts/{id:[0-9]+}/", podcastsC.Get)
	getRouter.HandleFunc("/uryplayer/podcasts/{id:[0-9]+}/player/", podcastsC.GetEmbed)

	pc := controllers.NewPeopleController(session, c)
	getRouter.HandleFunc("/people/{id:[0-9]+}/", pc.Get)

	teamC := controllers.NewTeamController(session, c)
	getRouter.HandleFunc("/teams/", teamC.GetAll)
	getRouter.HandleFunc("/teams/{alias}/", teamC.Get)

	getinvolvedC := controllers.NewGetInvolvedController(session, c)
	getRouter.HandleFunc("/getinvolved/", getinvolvedC.Get)

	signupC := controllers.NewSignUpController(session, c)
	postRouter.HandleFunc("/signup/", signupC.Post)

	staticC := controllers.NewStaticController(c)
	getRouter.HandleFunc("/about/", staticC.GetAbout)
	getRouter.HandleFunc("/contact/", staticC.GetContact)
	getRouter.HandleFunc("/competitions/", staticC.GetCompetitions)

	// End routes

	s.UseHandler(router)

	return &s, nil

}
<commit_msg>Remove the wrong way of redirects.<commit_after>package main

import (
	"net/http"

	"github.com/UniversityRadioYork/2016-site/controllers"
	"github.com/UniversityRadioYork/2016-site/structs"
	"github.com/UniversityRadioYork/myradio-go"
	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"
)

// Server is the type of the main 2016site web application.
type Server struct {
	*negroni.Negroni
}

// NewServer creates a 2016site server based on the config c.
func NewServer(c *structs.Config) (*Server, error) {

	s := Server{negroni.Classic()}

	session, err := myradio.NewSessionFromKeyFile()

	if err != nil {
		return &s, err
	}

	router := mux.NewRouter().StrictSlash(true)

	getRouter := router.Methods("GET").Subrouter()
	postRouter := router.Methods("POST").Subrouter()

	// Routes go in here
	nfc := controllers.NewNotFoundController(c)
	router.NotFoundHandler = http.HandlerFunc(nfc.Get)

	ic := controllers.NewIndexController(session, c)
	getRouter.HandleFunc("/", ic.Get)

	sc := controllers.NewSearchController(session, c)
	getRouter.HandleFunc("/search/", sc.Get)

	showC := controllers.NewShowController(session, c)
	//	getRouter.HandleFunc("/schedule/shows", showC.Get) // @TODO: Implement this
	getRouter.HandleFunc("/schedule/shows/{id:[0-9]+}/", showC.GetShow).Name("show")
	getRouter.HandleFunc("/schedule/shows/timeslots/{id:[0-9]+}/", showC.GetTimeslot).Name("timeslot")
	getRouter.HandleFunc("/schedule/shows/seasons/{id:[0-9]+}/", showC.GetSeason).Name("season")

	// NOTE: NewScheduleWeekController assumes 'timeslot' is installed BEFORE it is called.
	schedWeekC := controllers.NewScheduleWeekController(session, getRouter, c)
	getRouter.HandleFunc("/schedule/thisweek/", schedWeekC.GetThisWeek).Name("schedule-thisweek")

	getRouter.HandleFunc("/schedule/{year:[1-9][0-9][0-9][0-9]}/w{week:[0-5]?[0-9]}/", schedWeekC.GetByYearWeek).Name("schedule-week")
	// This route exists so that day schedule links from the previous website aren't broken.
	getRouter.HandleFunc("/schedule/{year:[1-9][0-9][0-9][0-9]}/w{week:[0-5]?[0-9]}/{day:[1-7]}/", schedWeekC.GetByYearWeek).Name("schedule-week-day-compat")

	onDemandC := controllers.NewOnDemandController(session, c)
	getRouter.HandleFunc("/ontap/", onDemandC.Get)

	podcastsC := controllers.NewPodcastController(session, c)
	getRouter.HandleFunc("/podcasts/", podcastsC.GetAllPodcasts)
	getRouter.HandleFunc("/podcasts/page/{page:[0-9]+}", podcastsC.GetAllPodcasts)
	getRouter.HandleFunc("/podcasts/{id:[0-9]+}/", podcastsC.Get)
	getRouter.HandleFunc("/podcasts/{id:[0-9]+}/player/", podcastsC.GetEmbed)

	pc := controllers.NewPeopleController(session, c)
	getRouter.HandleFunc("/people/{id:[0-9]+}/", pc.Get)

	teamC := controllers.NewTeamController(session, c)
	getRouter.HandleFunc("/teams/", teamC.GetAll)
	getRouter.HandleFunc("/teams/{alias}/", teamC.Get)

	getinvolvedC := controllers.NewGetInvolvedController(session, c)
	getRouter.HandleFunc("/getinvolved/", getinvolvedC.Get)

	signupC := controllers.NewSignUpController(session, c)
	postRouter.HandleFunc("/signup/", signupC.Post)

	staticC := controllers.NewStaticController(c)
	getRouter.HandleFunc("/about/", staticC.GetAbout)
	getRouter.HandleFunc("/contact/", staticC.GetContact)
	getRouter.HandleFunc("/competitions/", staticC.GetCompetitions)

	// End routes

	s.UseHandler(router)

	return &s, nil

}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package grpc

import (
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"reflect"
	"strings"
	"sync"

	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/transport"
)

type methodHandler func(srv interface{}, ctx context.Context, codec Codec, buf []byte) (interface{}, error)

// MethodDesc represents an RPC service's method specification.
type MethodDesc struct {
	MethodName string
	Handler    methodHandler
}

// ServiceDesc represents an RPC service's specification.
type ServiceDesc struct {
	ServiceName string
	// The pointer to the service interface. Used to check whether the user
	// provided implementation satisfies the interface requirements.
	HandlerType interface{}
	Methods     []MethodDesc
	Streams     []StreamDesc
}

// service consists of the information of the server serving this service and
// the methods in this service.
type service struct {
	server interface{} // the server for service methods
	md     map[string]*MethodDesc
	sd     map[string]*StreamDesc
}

// Server is a gRPC server to serve RPC requests.
type Server struct {
	opts  options
	mu    sync.Mutex
	lis   map[net.Listener]bool
	conns map[transport.ServerTransport]bool
	m     map[string]*service // service name -> service info
}

type options struct {
	codec                Codec
	maxConcurrentStreams uint32
}

// A ServerOption sets options.
type ServerOption func(*options)

func CustomCodec(codec Codec) ServerOption {
	return func(o *options) {
		o.codec = codec
	}
}

// MaxConcurrentStreams returns an Option that will apply a limit on the number
// of concurrent streams to each ServerTransport.
func MaxConcurrentStreams(n uint32) ServerOption {
	return func(o *options) {
		o.maxConcurrentStreams = n
	}
}

// NewServer creates a gRPC server which has no service registered and has not
// started to accept requests yet.
func NewServer(opt ...ServerOption) *Server {
	var opts options
	for _, o := range opt {
		o(&opts)
	}
	if opts.codec == nil {
		// Set the default codec.
		opts.codec = protoCodec{}
	}
	return &Server{
		lis:   make(map[net.Listener]bool),
		opts:  opts,
		conns: make(map[transport.ServerTransport]bool),
		m:     make(map[string]*service),
	}
}

// RegisterService register a service and its implementation to the gRPC
// server. Called from the IDL generated code. This must be called before
// invoking Serve.
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
	s.mu.Lock()
	defer s.mu.Unlock()
	// Does some sanity checks.
	if _, ok := s.m[sd.ServiceName]; ok {
		log.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
	}
	ht := reflect.TypeOf(sd.HandlerType).Elem()
	st := reflect.TypeOf(ss)
	if !st.Implements(ht) {
		log.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
	}
	srv := &service{
		server: ss,
		md:     make(map[string]*MethodDesc),
		sd:     make(map[string]*StreamDesc),
	}
	for i := range sd.Methods {
		d := &sd.Methods[i]
		srv.md[d.MethodName] = d
	}
	for i := range sd.Streams {
		d := &sd.Streams[i]
		srv.sd[d.StreamName] = d
	}
	s.m[sd.ServiceName] = srv
}

var (
	// ErrServerStopped indicates that the operation is now illegal because of
	// the server being stopped.
	ErrServerStopped = errors.New("grpc: the server has been stopped")
)

// Serve accepts incoming connections on the listener lis, creating a new
// ServerTransport and service goroutine for each. The service goroutines
// read gRPC request and then call the registered handlers to reply to them.
// Service returns when lis.Accept fails.
func (s *Server) Serve(lis net.Listener) error {
	s.mu.Lock()
	if s.lis == nil {
		s.mu.Unlock()
		return ErrServerStopped
	}
	s.lis[lis] = true
	s.mu.Unlock()
	defer func() {
		lis.Close()
		s.mu.Lock()
		delete(s.lis, lis)
		s.mu.Unlock()
	}()
	for {
		c, err := lis.Accept()
		if err != nil {
			return err
		}

		s.mu.Lock()
		if s.conns == nil {
			s.mu.Unlock()
			c.Close()
			return nil
		}
		st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams)
		if err != nil {
			s.mu.Unlock()
			c.Close()
			log.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
			continue
		}
		s.conns[st] = true
		s.mu.Unlock()

		go func() {
			st.HandleStreams(func(stream *transport.Stream) {
				s.handleStream(st, stream)
			})
			s.mu.Lock()
			delete(s.conns, st)
			s.mu.Unlock()
		}()
	}
}

func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, pf payloadFormat, opts *transport.Options) error {
	p, err := encode(s.opts.codec, msg, pf)
	if err != nil {
		// This typically indicates a fatal issue (e.g., memory
		// corruption or hardware faults) the application program
		// cannot handle.
		//
		// TODO(zhaoq): There exist other options also such as only closing the
		// faulty stream locally and remotely (Other streams can keep going). Find
		// the optimal option.
		log.Fatalf("grpc: Server failed to encode response %v", err)
	}
	return t.Write(stream, p, opts)
}

func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc) {
	p := &parser{s: stream}
	for {
		pf, req, err := p.recvMsg()
		if err == io.EOF {
			// The entire stream is done (for unary RPC only).
			return
		}
		if err != nil {
			switch err := err.(type) {
			case transport.ConnectionError:
				// Nothing to do here.
			case transport.StreamError:
				if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
					log.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
				}
			default:
				panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
			}
			return
		}
		switch pf {
		case compressionNone:
			statusCode := codes.OK
			statusDesc := ""
			reply, appErr := md.Handler(srv.server, stream.Context(), s.opts.codec, req)
			if appErr != nil {
				if err, ok := appErr.(rpcError); ok {
					statusCode = err.code
					statusDesc = err.desc
				} else {
					statusCode = convertCode(appErr)
					statusDesc = appErr.Error()
				}
				if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
					log.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
				}
				return
			}
			opts := &transport.Options{
				Last:  true,
				Delay: false,
			}
			if err := s.sendResponse(t, stream, reply, compressionNone, opts); err != nil {
				if _, ok := err.(transport.ConnectionError); ok {
					return
				}
				if e, ok := err.(transport.StreamError); ok {
					statusCode = e.Code
					statusDesc = e.Desc
				} else {
					statusCode = codes.Unknown
					statusDesc = err.Error()
				}
			}
			t.WriteStatus(stream, statusCode, statusDesc)
		default:
			panic(fmt.Sprintf("payload format to be supported: %d", pf))
		}
	}
}

func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc) {
	ss := &serverStream{
		t:     t,
		s:     stream,
		p:     &parser{s: stream},
		codec: s.opts.codec,
	}
	if appErr := sd.Handler(srv.server, ss); appErr != nil {
		if err, ok := appErr.(rpcError); ok {
			ss.statusCode = err.code
			ss.statusDesc = err.desc
		} else {
			ss.statusCode = convertCode(appErr)
			ss.statusDesc = appErr.Error()
		}
	}
	t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
}

func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) {
	sm := stream.Method()
	if sm != "" && sm[0] == '/' {
		sm = sm[1:]
	}
	pos := strings.LastIndex(sm, "/")
	if pos == -1 {
		if err := t.WriteStatus(stream, codes.InvalidArgument, fmt.Sprintf("malformed method name: %q", stream.Method())); err != nil {
			log.Printf("grpc: Server.handleStream failed to write status: %v", err)
		}
		return
	}
	service := sm[:pos]
	method := sm[pos+1:]
	srv, ok := s.m[service]
	if !ok {
		if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown service %v", service)); err != nil {
			log.Printf("grpc: Server.handleStream failed to write status: %v", err)
		}
		return
	}
	// Unary RPC or Streaming RPC?
	if md, ok := srv.md[method]; ok {
		s.processUnaryRPC(t, stream, srv, md)
		return
	}
	if sd, ok := srv.sd[method]; ok {
		s.processStreamingRPC(t, stream, srv, sd)
		return
	}
	if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown method %v", method)); err != nil {
		log.Printf("grpc: Server.handleStream failed to write status: %v", err)
	}
}

// Stop stops the gRPC server. Once Stop returns, the server stops accepting
// connection requests and closes all the connected connections.
func (s *Server) Stop() {
	s.mu.Lock()
	listeners := s.lis
	s.lis = nil
	cs := s.conns
	s.conns = nil
	s.mu.Unlock()
	for lis := range listeners {
		lis.Close()
	}
	for c := range cs {
		c.Close()
	}
}

// TestingCloseConns closes all exiting transports but keeps s.lis accepting new
// connections. This is for test only now.
func (s *Server) TestingCloseConns() {
	s.mu.Lock()
	for c := range s.conns {
		c.Close()
	}
	s.conns = make(map[transport.ServerTransport]bool)
	s.mu.Unlock()
}

// SendHeader sends header metadata. It may be called at most once from a unary
// RPC handler. The ctx is the RPC handler's Context or one derived from it.
func SendHeader(ctx context.Context, md metadata.MD) error {
	if md.Len() == 0 {
		return nil
	}
	stream, ok := transport.StreamFromContext(ctx)
	if !ok {
		return fmt.Errorf("grpc: failed to fetch the stream from the context %v", ctx)
	}
	t := stream.ServerTransport()
	if t == nil {
		log.Fatalf("grpc: SendHeader: %v has no ServerTransport to send header metadata.", stream)
	}
	return t.WriteHeader(stream, md)
}

// SetTrailer sets the trailer metadata that will be sent when an RPC returns.
// It may be called at most once from a unary RPC handler. The ctx is the RPC
// handler's Context or one derived from it.
func SetTrailer(ctx context.Context, md metadata.MD) error {
	if md.Len() == 0 {
		return nil
	}
	stream, ok := transport.StreamFromContext(ctx)
	if !ok {
		return fmt.Errorf("grpc: failed to fetch the stream from the context %v", ctx)
	}
	return stream.SetTrailer(md)
}
<commit_msg>Add a handshaker option to server<commit_after>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

package grpc

import (
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"reflect"
	"strings"
	"sync"

	"golang.org/x/net/context"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/transport"
)

type methodHandler func(srv interface{}, ctx context.Context, codec Codec, buf []byte) (interface{}, error)

// MethodDesc represents an RPC service's method specification.
type MethodDesc struct {
	MethodName string
	Handler    methodHandler
}

// ServiceDesc represents an RPC service's specification.
type ServiceDesc struct {
	ServiceName string
	// The pointer to the service interface. Used to check whether the user
	// provided implementation satisfies the interface requirements.
	HandlerType interface{}
	Methods     []MethodDesc
	Streams     []StreamDesc
}

// service consists of the information of the server serving this service and
// the methods in this service.
type service struct {
	server interface{} // the server for service methods
	md     map[string]*MethodDesc
	sd     map[string]*StreamDesc
}

// Server is a gRPC server to serve RPC requests.
type Server struct {
	opts  options
	mu    sync.Mutex
	lis   map[net.Listener]bool
	conns map[transport.ServerTransport]bool
	m     map[string]*service // service name -> service info
}

type options struct {
	handshaker           func(net.Conn) error
	codec                Codec
	maxConcurrentStreams uint32
}

// A ServerOption sets options.
type ServerOption func(*options)

func Handshaker(f func(net.Conn) error) ServerOption {
	return func(o *options) {
		o.handshaker = f
	}
}

func CustomCodec(codec Codec) ServerOption {
	return func(o *options) {
		o.codec = codec
	}
}

// MaxConcurrentStreams returns an Option that will apply a limit on the number
// of concurrent streams to each ServerTransport.
func MaxConcurrentStreams(n uint32) ServerOption {
	return func(o *options) {
		o.maxConcurrentStreams = n
	}
}

// NewServer creates a gRPC server which has no service registered and has not
// started to accept requests yet.
func NewServer(opt ...ServerOption) *Server {
	var opts options
	for _, o := range opt {
		o(&opts)
	}
	if opts.codec == nil {
		// Set the default codec.
		opts.codec = protoCodec{}
	}
	return &Server{
		lis:   make(map[net.Listener]bool),
		opts:  opts,
		conns: make(map[transport.ServerTransport]bool),
		m:     make(map[string]*service),
	}
}

// RegisterService register a service and its implementation to the gRPC
// server. Called from the IDL generated code. This must be called before
// invoking Serve.
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
	s.mu.Lock()
	defer s.mu.Unlock()
	// Does some sanity checks.
	if _, ok := s.m[sd.ServiceName]; ok {
		log.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
	}
	ht := reflect.TypeOf(sd.HandlerType).Elem()
	st := reflect.TypeOf(ss)
	if !st.Implements(ht) {
		log.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
	}
	srv := &service{
		server: ss,
		md:     make(map[string]*MethodDesc),
		sd:     make(map[string]*StreamDesc),
	}
	for i := range sd.Methods {
		d := &sd.Methods[i]
		srv.md[d.MethodName] = d
	}
	for i := range sd.Streams {
		d := &sd.Streams[i]
		srv.sd[d.StreamName] = d
	}
	s.m[sd.ServiceName] = srv
}

var (
	// ErrServerStopped indicates that the operation is now illegal because of
	// the server being stopped.
	ErrServerStopped = errors.New("grpc: the server has been stopped")
)

// Serve accepts incoming connections on the listener lis, creating a new
// ServerTransport and service goroutine for each. The service goroutines
// read gRPC request and then call the registered handlers to reply to them.
// Service returns when lis.Accept fails.
func (s *Server) Serve(lis net.Listener) error {
	s.mu.Lock()
	if s.lis == nil {
		s.mu.Unlock()
		return ErrServerStopped
	}
	s.lis[lis] = true
	s.mu.Unlock()
	defer func() {
		lis.Close()
		s.mu.Lock()
		delete(s.lis, lis)
		s.mu.Unlock()
	}()
	for {
		c, err := lis.Accept()
		if err != nil {
			return err
		}
		// Perform handshaking if it is required.
		if s.opts.handshaker != nil {
			if err := s.opts.handshaker(c); err != nil {
				log.Println("grpc: Server.Serve failed to complete handshake.")
				c.Close()
				continue
			}
		}
		s.mu.Lock()
		if s.conns == nil {
			s.mu.Unlock()
			c.Close()
			return nil
		}
		st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams)
		if err != nil {
			s.mu.Unlock()
			c.Close()
			log.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
			continue
		}
		s.conns[st] = true
		s.mu.Unlock()

		go func() {
			st.HandleStreams(func(stream *transport.Stream) {
				s.handleStream(st, stream)
			})
			s.mu.Lock()
			delete(s.conns, st)
			s.mu.Unlock()
		}()
	}
}

func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, pf payloadFormat, opts *transport.Options) error {
	p, err := encode(s.opts.codec, msg, pf)
	if err != nil {
		// This typically indicates a fatal issue (e.g., memory
		// corruption or hardware faults) the application program
		// cannot handle.
		//
		// TODO(zhaoq): There exist other options also such as only closing the
		// faulty stream locally and remotely (Other streams can keep going). Find
		// the optimal option.
		log.Fatalf("grpc: Server failed to encode response %v", err)
	}
	return t.Write(stream, p, opts)
}

func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc) {
	p := &parser{s: stream}
	for {
		pf, req, err := p.recvMsg()
		if err == io.EOF {
			// The entire stream is done (for unary RPC only).
			return
		}
		if err != nil {
			switch err := err.(type) {
			case transport.ConnectionError:
				// Nothing to do here.
			case transport.StreamError:
				if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
					log.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
				}
			default:
				panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
			}
			return
		}
		switch pf {
		case compressionNone:
			statusCode := codes.OK
			statusDesc := ""
			reply, appErr := md.Handler(srv.server, stream.Context(), s.opts.codec, req)
			if appErr != nil {
				if err, ok := appErr.(rpcError); ok {
					statusCode = err.code
					statusDesc = err.desc
				} else {
					statusCode = convertCode(appErr)
					statusDesc = appErr.Error()
				}
				if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
					log.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
				}
				return
			}
			opts := &transport.Options{
				Last:  true,
				Delay: false,
			}
			if err := s.sendResponse(t, stream, reply, compressionNone, opts); err != nil {
				if _, ok := err.(transport.ConnectionError); ok {
					return
				}
				if e, ok := err.(transport.StreamError); ok {
					statusCode = e.Code
					statusDesc = e.Desc
				} else {
					statusCode = codes.Unknown
					statusDesc = err.Error()
				}
			}
			t.WriteStatus(stream, statusCode, statusDesc)
		default:
			panic(fmt.Sprintf("payload format to be supported: %d", pf))
		}
	}
}

func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc) {
	ss := &serverStream{
		t:     t,
		s:     stream,
		p:     &parser{s: stream},
		codec: s.opts.codec,
	}
	if appErr := sd.Handler(srv.server, ss); appErr != nil {
		if err, ok := appErr.(rpcError); ok {
			ss.statusCode = err.code
			ss.statusDesc = err.desc
		} else {
			ss.statusCode = convertCode(appErr)
			ss.statusDesc = appErr.Error()
		}
	}
	t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
}

func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) {
	sm := stream.Method()
	if sm != "" && sm[0] == '/' {
		sm = sm[1:]
	}
	pos := strings.LastIndex(sm, "/")
	if pos == -1 {
		if err := t.WriteStatus(stream, codes.InvalidArgument, fmt.Sprintf("malformed method name: %q", stream.Method())); err != nil {
			log.Printf("grpc: Server.handleStream failed to write status: %v", err)
		}
		return
	}
	service := sm[:pos]
	method := sm[pos+1:]
	srv, ok := s.m[service]
	if !ok {
		if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown service %v", service)); err != nil {
			log.Printf("grpc: Server.handleStream failed to write status: %v", err)
		}
		return
	}
	// Unary RPC or Streaming RPC?
	if md, ok := srv.md[method]; ok {
		s.processUnaryRPC(t, stream, srv, md)
		return
	}
	if sd, ok := srv.sd[method]; ok {
		s.processStreamingRPC(t, stream, srv, sd)
		return
	}
	if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown method %v", method)); err != nil {
		log.Printf("grpc: Server.handleStream failed to write status: %v", err)
	}
}

// Stop stops the gRPC server. Once Stop returns, the server stops accepting
// connection requests and closes all the connected connections.
func (s *Server) Stop() {
	s.mu.Lock()
	listeners := s.lis
	s.lis = nil
	cs := s.conns
	s.conns = nil
	s.mu.Unlock()
	for lis := range listeners {
		lis.Close()
	}
	for c := range cs {
		c.Close()
	}
}

// TestingCloseConns closes all exiting transports but keeps s.lis accepting new
// connections. This is for test only now.
func (s *Server) TestingCloseConns() {
	s.mu.Lock()
	for c := range s.conns {
		c.Close()
	}
	s.conns = make(map[transport.ServerTransport]bool)
	s.mu.Unlock()
}

// SendHeader sends header metadata. It may be called at most once from a unary
// RPC handler. The ctx is the RPC handler's Context or one derived from it.
func SendHeader(ctx context.Context, md metadata.MD) error {
	if md.Len() == 0 {
		return nil
	}
	stream, ok := transport.StreamFromContext(ctx)
	if !ok {
		return fmt.Errorf("grpc: failed to fetch the stream from the context %v", ctx)
	}
	t := stream.ServerTransport()
	if t == nil {
		log.Fatalf("grpc: SendHeader: %v has no ServerTransport to send header metadata.", stream)
	}
	return t.WriteHeader(stream, md)
}

// SetTrailer sets the trailer metadata that will be sent when an RPC returns.
// It may be called at most once from a unary RPC handler. The ctx is the RPC
// handler's Context or one derived from it.
func SetTrailer(ctx context.Context, md metadata.MD) error {
	if md.Len() == 0 {
		return nil
	}
	stream, ok := transport.StreamFromContext(ctx)
	if !ok {
		return fmt.Errorf("grpc: failed to fetch the stream from the context %v", ctx)
	}
	return stream.SetTrailer(md)
}
<|endoftext|>


<commit_before>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"sort"
	"time"
)

// Event stores the relevant information on events or updates to the storage layer.
type Event struct {
	Source      string
	Name        string
	Message     string
	Time        time.Time
	IsDirectory bool
}

var events = make([]Event, 0, 100)

// FileEvent stores the nodeid, string, and time of file related events
type FileEvent struct {
	NodeID int32
	Name   string
	Time   time.Time
}

// SendEvent gets events that have happened off to the peer servers so they can replicate the same change
func SendEvent(event Event, fullPath string) {
	// sendEvent to manager
	sendEvent(&event, fullPath, globalSettings.ManagerAddress, globalSettings.ManagerCredentials)

	if globalSettings.Name == "NodeB" {
		fmt.Printf("Skipping sending from NodeB for test")
		return
	}

	// SendEvent to all peers
	for k, v := range serverMap {
		fmt.Printf("Considering sending to: %s\n", k)
		if k != globalSettings.Name {
			fmt.Printf("sending to peer %s at %s\n", k, v.Address)
			sendEvent(&event, fullPath, v.Address, globalSettings.ManagerCredentials)
		}
	}
}

func sendEvent(event *Event, fullPath string, address string, credentials string) {
	url := "http://" + address + "/event/"
	fmt.Printf("target url: %s\n", url)

	// Set the event source  (server name)
	event.Source = globalSettings.Name

	jsonStr, _ := json.Marshal(event)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json")

	data := []byte(credentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
		return
	}

	defer resp.Body.Close()

	fmt.Println("response Status:", resp.Status)
	fmt.Println("response Headers:", resp.Header)
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("response Body:", string(body))

	if event.Name == "notify.Write" {
		fmt.Printf("source: notify.Write => %s\n", fullPath)
		url := "http://" + address + "/upload/"
		postFile(event.Message, fullPath, url, credentials)
	}
}

func eventHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		json.NewEncoder(w).Encode(events)
	case "POST":
		log.Println("Got POST: ", r.Body)
		decoder := json.NewDecoder(r.Body)
		var event Event
		err := decoder.Decode(&event)
		if err != nil {
			panic("bad json body")
		}

		log.Println(event.Name + ", path: " + event.Message)
		log.Printf("Event info: %v\n", event)

		pathName := globalSettings.Directory + "/" + event.Message
		relativePath := event.Message

		//todo remember these events and skip the logging on the other side. Possibly use nodeID?

		switch event.Name {
		case "notify.Create":
			fmt.Printf("notify.Create: %s\n", pathName)

			server, exists := serverMap[globalSettings.Name]
			if !exists {
				panic("Unable to find server definition")
			}

			fmt.Println("eventHandler->CreatePath")
			server.storage.CreatePath(relativePath, event.IsDirectory)
			fmt.Println("eventHandler->/CreatePath")
		case "notify.Remove":
			err = os.Remove(pathName)
			if err != nil && !os.IsNotExist(err) {
				panic(fmt.Sprintf("Error deleting folder %s: %v\n", pathName, err))
			}
			fmt.Printf("notify.Remove: %s\n", pathName)
		// todo fix this to handle the two rename events to be one event
		case "notify.Rename":
			fmt.Printf("notify.Rename: %s\n", pathName)

			server, exists := serverMap[globalSettings.Name]
			if !exists {
				panic("Unable to find server definition")
			}

			fmt.Println("eventHandler->CreatePath")
			server.storage.CreatePath(relativePath, event.IsDirectory)
			fmt.Println("eventHandler->/CreatePath")

		default:
			fmt.Printf("Unknown event found, doing nothing. Event: %v\n", event)
		}

		events = append([]Event{event}, events...)
	}
}

/*
Send the folder tree from this node to another node for comparison
*/
func sendFolderTree(initialTree DirTreeMap) {
	var tree = make(DirTreeMap)
	prefixLength := 1 // the slash prefix
	for key, value := range initialTree {
		if key == "" {
			continue
		}
		fmt.Printf("key is: '%s', length is: %d\n", key, prefixLength)
		key = key[prefixLength:]
		tree[key] = value
	}

	// package up the tree
	jsonStr, err := json.Marshal(tree)
	if err != nil {
		panic(err)
	}
	buffer := bytes.NewBuffer(jsonStr)
	client := &http.Client{}

	data := []byte(globalSettings.ManagerCredentials)
	authHash := base64.StdEncoding.EncodeToString(data)

	for k, v := range serverMap {
		// don't send an update to ourselves
		if k == globalSettings.Name {
			continue
		}

		url := "http://" + v.Address + "/tree/"
		fmt.Printf("Posting folder tree to node: %s at URL: %s\n", k, url)

		req, err := http.NewRequest("POST", url, buffer)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Basic "+authHash)

		resp, err := client.Do(req)
		if err != nil {
			fmt.Printf("we encountered an error!\n%s\n", err)
			//panic(err)
			continue
		}

		//fmt.Println("response Status:", resp.Status)
		//fmt.Println("response Headers:", resp.Header)
		_, _ = ioutil.ReadAll(resp.Body)
		//body, _ := ioutil.ReadAll(resp.Body)
		//fmt.Println("response Body:", string(body))
		resp.Body.Close()
	}
}

func getListOfFolders(w http.ResponseWriter) {
	listOfFileInfo, err := scanDirectoryContents()
	if err != nil {
		log.Fatal(err)
	}
	json.NewEncoder(w).Encode(listOfFileInfo)
	fmt.Printf("Sending tree of size %d back to client\n", len(listOfFileInfo))
}

func deletePaths(deletedPaths []string) {
	if globalSettings.Directory == "" {
		panic("globalSettings.Directory is not configured correctly. Aborting")
	}

	fmt.Println("Paths were deleted on the other side. Delete them here")

	fmt.Printf("Paths to delete\nBefore sort:\n%v\n", deletedPaths)
	// Reverse sort the paths so the most specific is first. This allows us to get away without a recursive delete
	sort.Sort(sort.Reverse(sort.StringSlice(deletedPaths)))
	fmt.Printf("Paths to delete after sort\nAfter sort:\n%v\n", deletedPaths)
	for _, relativePath := range deletedPaths {
		if relativePath == "" || relativePath == "/" {
			fmt.Printf("We had a request to delete the base path. Skipping: %s\n", relativePath)
			continue
		}
		fullPath := globalSettings.Directory + relativePath
		fmt.Printf("Full path is: %s\n", fullPath)

		fmt.Printf("%s: about to remove\n", fullPath)

		// stop on any error except for not exist. We are trying to delete it anyway (or rather, it should have been deleted already)
		err := os.Remove(fullPath)
		if err != nil && !os.IsNotExist(err) {
			panic(err)
		}
		fmt.Printf("%s: done removing (err = %v)\n", fullPath, err)
	}
}

func addPaths(newPaths []string) {
	fmt.Println("Paths were added on the other side. Create them here")
	for _, newPathName := range newPaths {
		fmt.Printf("pathname is: %s\n", newPathName)
		err := os.Mkdir(newPathName, os.ModeDir+os.ModePerm)
		if err != nil && !os.IsExist(err) {
			panic(err)
		}
	}
}

func folderTreeHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		getListOfFolders(w)
	case "POST":
		decoder := json.NewDecoder(r.Body)

		var remoteTreePreTranslate DirTreeMap
		err := decoder.Decode(&remoteTreePreTranslate)
		if err != nil {
			panic("bad json body")
		}

		var remoteTree = make(DirTreeMap)
		for key, value := range remoteTreePreTranslate {
			key = fmt.Sprintf("%s/%s", globalSettings.Directory, key)
			remoteTree[key] = value
		}

		// todo get the node name in here
		fmt.Printf("Received a tree map from: %s\n%s\n", r.RemoteAddr, remoteTree)

		// let's compare to the current one we have.
		_, _, newPaths, deletedPaths, _ := checkForChanges(remoteTree, nil)

		// update the paths to be consistent. Remember, everything is backwards since this is comparing the other side to us instead of two successive states
		// deletion will show up as a new path.

		fmt.Printf("about to check for deletions: %s\n", newPaths)
		// delete folders that were deleted. We delete first, then add to make sure that the old ones will not be in the way
		if len(newPaths) > 0 {
			deletePaths(newPaths)
		}

		// add folders that were created
		fmt.Printf("about to check for new folders: %s\n", deletedPaths)
		// add folders that were added.
		if len(deletedPaths) > 0 {
			addPaths(deletedPaths)
		}
	}
}

func postFile(filename string, fullPath string, targetUrl string, credentials string) error {
	body := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(body)
	fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
	if err != nil {
		fmt.Println("error writing to buffer")
		return err
	}

	file, err := os.Open(fullPath)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = io.Copy(fileWriter, file)
	if err != nil {
		fmt.Println("error copying file")
		return err
	}

	myHash, err := fileMd5Hash(fullPath)
	if err != nil {
		fmt.Printf("failed to calculate MD5 Hash for %s\n", fullPath)
		return err
	}

	bodyWriter.WriteField("HASH", myHash)
	contentType := bodyWriter.FormDataContentType()
	err = bodyWriter.Close()
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", targetUrl, body)
	req.Header.Set("Content-Type", contentType)

	data := []byte(credentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	resp, err := client.Do(req)
	defer resp.Body.Close()

	if err != nil {
		return err
	}

	return nil
}

func fileMd5Hash(filePath string) (string, error) {
	var returnMD5String string
	file, err := os.Open(filePath)
	if err != nil {
		return returnMD5String, err
	}
	defer file.Close()

	hash := md5.New()
	_, err = io.Copy(hash, file)
	if err != nil {
		return returnMD5String, err
	}

	hashInBytes := hash.Sum(nil)[:16]

	returnMD5String = hex.EncodeToString(hashInBytes)

	return returnMD5String, nil

}
<commit_msg>moving break of sending events from nodeb higher up (don't even send to manager)<commit_after>// Copyright 2016 Jacob Taylor jacob@ablox.io
// License: Apache2 - http://www.apache.org/licenses/LICENSE-2.0
package main

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"sort"
	"time"
)

// Event stores the relevant information on events or updates to the storage layer.
type Event struct {
	Source      string
	Name        string
	Message     string
	Time        time.Time
	IsDirectory bool
}

var events = make([]Event, 0, 100)

// FileEvent stores the nodeid, string, and time of file related events
type FileEvent struct {
	NodeID int32
	Name   string
	Time   time.Time
}

// SendEvent gets events that have happened off to the peer servers so they can replicate the same change
func SendEvent(event Event, fullPath string) {
	if globalSettings.Name == "NodeB" {
		fmt.Printf("Skipping sending from NodeB for test")
		return
	}

	// sendEvent to manager
	sendEvent(&event, fullPath, globalSettings.ManagerAddress, globalSettings.ManagerCredentials)

	// SendEvent to all peers
	for k, v := range serverMap {
		fmt.Printf("Considering sending to: %s\n", k)
		if k != globalSettings.Name {
			fmt.Printf("sending to peer %s at %s\n", k, v.Address)
			sendEvent(&event, fullPath, v.Address, globalSettings.ManagerCredentials)
		}
	}
}

func sendEvent(event *Event, fullPath string, address string, credentials string) {
	url := "http://" + address + "/event/"
	fmt.Printf("target url: %s\n", url)

	// Set the event source  (server name)
	event.Source = globalSettings.Name

	jsonStr, _ := json.Marshal(event)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Set("Content-Type", "application/json")

	data := []byte(credentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
		return
	}

	defer resp.Body.Close()

	fmt.Println("response Status:", resp.Status)
	fmt.Println("response Headers:", resp.Header)
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("response Body:", string(body))

	if event.Name == "notify.Write" {
		fmt.Printf("source: notify.Write => %s\n", fullPath)
		url := "http://" + address + "/upload/"
		postFile(event.Message, fullPath, url, credentials)
	}
}

func eventHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		json.NewEncoder(w).Encode(events)
	case "POST":
		log.Println("Got POST: ", r.Body)
		decoder := json.NewDecoder(r.Body)
		var event Event
		err := decoder.Decode(&event)
		if err != nil {
			panic("bad json body")
		}

		log.Println(event.Name + ", path: " + event.Message)
		log.Printf("Event info: %v\n", event)

		pathName := globalSettings.Directory + "/" + event.Message
		relativePath := event.Message

		//todo remember these events and skip the logging on the other side. Possibly use nodeID?

		switch event.Name {
		case "notify.Create":
			fmt.Printf("notify.Create: %s\n", pathName)

			server, exists := serverMap[globalSettings.Name]
			if !exists {
				panic("Unable to find server definition")
			}

			fmt.Println("eventHandler->CreatePath")
			server.storage.CreatePath(relativePath, event.IsDirectory)
			fmt.Println("eventHandler->/CreatePath")
		case "notify.Remove":
			err = os.Remove(pathName)
			if err != nil && !os.IsNotExist(err) {
				panic(fmt.Sprintf("Error deleting folder %s: %v\n", pathName, err))
			}
			fmt.Printf("notify.Remove: %s\n", pathName)
		// todo fix this to handle the two rename events to be one event
		case "notify.Rename":
			fmt.Printf("notify.Rename: %s\n", pathName)

			server, exists := serverMap[globalSettings.Name]
			if !exists {
				panic("Unable to find server definition")
			}

			fmt.Println("eventHandler->CreatePath")
			server.storage.CreatePath(relativePath, event.IsDirectory)
			fmt.Println("eventHandler->/CreatePath")

		default:
			fmt.Printf("Unknown event found, doing nothing. Event: %v\n", event)
		}

		events = append([]Event{event}, events...)
	}
}

/*
Send the folder tree from this node to another node for comparison
*/
func sendFolderTree(initialTree DirTreeMap) {
	var tree = make(DirTreeMap)
	prefixLength := 1 // the slash prefix
	for key, value := range initialTree {
		if key == "" {
			continue
		}
		fmt.Printf("key is: '%s', length is: %d\n", key, prefixLength)
		key = key[prefixLength:]
		tree[key] = value
	}

	// package up the tree
	jsonStr, err := json.Marshal(tree)
	if err != nil {
		panic(err)
	}
	buffer := bytes.NewBuffer(jsonStr)
	client := &http.Client{}

	data := []byte(globalSettings.ManagerCredentials)
	authHash := base64.StdEncoding.EncodeToString(data)

	for k, v := range serverMap {
		// don't send an update to ourselves
		if k == globalSettings.Name {
			continue
		}

		url := "http://" + v.Address + "/tree/"
		fmt.Printf("Posting folder tree to node: %s at URL: %s\n", k, url)

		req, err := http.NewRequest("POST", url, buffer)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Basic "+authHash)

		resp, err := client.Do(req)
		if err != nil {
			fmt.Printf("we encountered an error!\n%s\n", err)
			//panic(err)
			continue
		}

		//fmt.Println("response Status:", resp.Status)
		//fmt.Println("response Headers:", resp.Header)
		_, _ = ioutil.ReadAll(resp.Body)
		//body, _ := ioutil.ReadAll(resp.Body)
		//fmt.Println("response Body:", string(body))
		resp.Body.Close()
	}
}

func getListOfFolders(w http.ResponseWriter) {
	listOfFileInfo, err := scanDirectoryContents()
	if err != nil {
		log.Fatal(err)
	}
	json.NewEncoder(w).Encode(listOfFileInfo)
	fmt.Printf("Sending tree of size %d back to client\n", len(listOfFileInfo))
}

func deletePaths(deletedPaths []string) {
	if globalSettings.Directory == "" {
		panic("globalSettings.Directory is not configured correctly. Aborting")
	}

	fmt.Println("Paths were deleted on the other side. Delete them here")

	fmt.Printf("Paths to delete\nBefore sort:\n%v\n", deletedPaths)
	// Reverse sort the paths so the most specific is first. This allows us to get away without a recursive delete
	sort.Sort(sort.Reverse(sort.StringSlice(deletedPaths)))
	fmt.Printf("Paths to delete after sort\nAfter sort:\n%v\n", deletedPaths)
	for _, relativePath := range deletedPaths {
		if relativePath == "" || relativePath == "/" {
			fmt.Printf("We had a request to delete the base path. Skipping: %s\n", relativePath)
			continue
		}
		fullPath := globalSettings.Directory + relativePath
		fmt.Printf("Full path is: %s\n", fullPath)

		fmt.Printf("%s: about to remove\n", fullPath)

		// stop on any error except for not exist. We are trying to delete it anyway (or rather, it should have been deleted already)
		err := os.Remove(fullPath)
		if err != nil && !os.IsNotExist(err) {
			panic(err)
		}
		fmt.Printf("%s: done removing (err = %v)\n", fullPath, err)
	}
}

func addPaths(newPaths []string) {
	fmt.Println("Paths were added on the other side. Create them here")
	for _, newPathName := range newPaths {
		fmt.Printf("pathname is: %s\n", newPathName)
		err := os.Mkdir(newPathName, os.ModeDir+os.ModePerm)
		if err != nil && !os.IsExist(err) {
			panic(err)
		}
	}
}

func folderTreeHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		getListOfFolders(w)
	case "POST":
		decoder := json.NewDecoder(r.Body)

		var remoteTreePreTranslate DirTreeMap
		err := decoder.Decode(&remoteTreePreTranslate)
		if err != nil {
			panic("bad json body")
		}

		var remoteTree = make(DirTreeMap)
		for key, value := range remoteTreePreTranslate {
			key = fmt.Sprintf("%s/%s", globalSettings.Directory, key)
			remoteTree[key] = value
		}

		// todo get the node name in here
		fmt.Printf("Received a tree map from: %s\n%s\n", r.RemoteAddr, remoteTree)

		// let's compare to the current one we have.
		_, _, newPaths, deletedPaths, _ := checkForChanges(remoteTree, nil)

		// update the paths to be consistent. Remember, everything is backwards since this is comparing the other side to us instead of two successive states
		// deletion will show up as a new path.

		fmt.Printf("about to check for deletions: %s\n", newPaths)
		// delete folders that were deleted. We delete first, then add to make sure that the old ones will not be in the way
		if len(newPaths) > 0 {
			deletePaths(newPaths)
		}

		// add folders that were created
		fmt.Printf("about to check for new folders: %s\n", deletedPaths)
		// add folders that were added.
		if len(deletedPaths) > 0 {
			addPaths(deletedPaths)
		}
	}
}

func postFile(filename string, fullPath string, targetUrl string, credentials string) error {
	body := &bytes.Buffer{}
	bodyWriter := multipart.NewWriter(body)
	fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
	if err != nil {
		fmt.Println("error writing to buffer")
		return err
	}

	file, err := os.Open(fullPath)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = io.Copy(fileWriter, file)
	if err != nil {
		fmt.Println("error copying file")
		return err
	}

	myHash, err := fileMd5Hash(fullPath)
	if err != nil {
		fmt.Printf("failed to calculate MD5 Hash for %s\n", fullPath)
		return err
	}

	bodyWriter.WriteField("HASH", myHash)
	contentType := bodyWriter.FormDataContentType()
	err = bodyWriter.Close()
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", targetUrl, body)
	req.Header.Set("Content-Type", contentType)

	data := []byte(credentials)
	authHash := base64.StdEncoding.EncodeToString(data)
	req.Header.Add("Authorization", "Basic "+authHash)

	client := &http.Client{}
	resp, err := client.Do(req)
	defer resp.Body.Close()

	if err != nil {
		return err
	}

	return nil
}

func fileMd5Hash(filePath string) (string, error) {
	var returnMD5String string
	file, err := os.Open(filePath)
	if err != nil {
		return returnMD5String, err
	}
	defer file.Close()

	hash := md5.New()
	_, err = io.Copy(hash, file)
	if err != nil {
		return returnMD5String, err
	}

	hashInBytes := hash.Sum(nil)[:16]

	returnMD5String = hex.EncodeToString(hashInBytes)

	return returnMD5String, nil

}
<|endoftext|>