Exemple #1
0
func NewHandler(config util.JsMap, logger *util.HekaLogger, store *storage.Storage) *Handler {
	return &Handler{config: config,
		logger: logger,
		logCat: "handler",
		hawk:   &Hawk{logger: logger, config: config},
		metrics: util.NewMetrics(util.MzGet(config,
			"metrics.prefix",
			"WMF")),
		store: store}
}
Exemple #2
0
// verify a Persona assertion using the config values
// part of Handler for config & logging reasons
func (self *Handler) verifyAssertion(assertion string) (userid, email string, err error) {
	var ok bool
	if util.MzGetFlag(self.config, "auth.disabled") {
		self.logger.Info(self.logCat, "### Skipping validation...", nil)
		return "user1", "*****@*****.**", nil
	}
	ver_url := util.MzGet(self.config, "persona.validater_url", "https://verifier.login.persona.org/verify")
	audience := util.MzGet(self.config, "persona.audience",
		"http://localhost:8080")
	res, err := http.PostForm(ver_url, url.Values{
		"assertion": {assertion},
		"audience":  {audience}})
	if err != nil {
		self.logger.Error(self.logCat, "Persona verification failed",
			util.Fields{"error": err.Error()})
		return "", "", AuthorizationErr
	}
	buffer, err := parseBody(res.Body)
	if isOk, ok := buffer["status"]; !ok || isOk != "okay" {
		var errStr string
		if err != nil {
			errStr = err.Error()
		} else if _, ok = buffer["reason"]; ok {
			errStr = buffer["reason"].(string)
		}
		self.logger.Error(self.logCat, "Persona Auth Failed",
			util.Fields{"error": errStr,
				"buffer": fmt.Sprintf("%+v", buffer)})
		return "", "", AuthorizationErr
	}
	if email, ok = buffer["email"].(string); !ok {
		self.logger.Error(self.logCat, "No email found in assertion",
			util.Fields{"assertion": fmt.Sprintf("%+v", buffer)})
		return "", "", AuthorizationErr
	}
	if userid, ok = buffer["userid"].(string); !ok {
		hasher := sha256.New()
		hasher.Write([]byte(email))
		userid = hex.EncodeToString(hasher.Sum(nil))
	}
	return userid, email, nil
}
Exemple #3
0
func main() {
	flag.Parse()

	// Configuration
	config := util.MzGetConfig(*configFile)
	config["VERSION"] = VERSION
	runtime.GOMAXPROCS(runtime.NumCPU())
	logger := util.NewHekaLogger(config)
	store := storage.New(config, logger)
	handlers := moztradamus.NewHandler(config, store, logger)

	// Signal handler
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGHUP, SIGUSR1)

	// Rest Config
	errChan := make(chan error)
	host := util.MzGet(config, "host", "localhost")
	port := util.MzGet(config, "port", "8080")
	var RESTMux *http.ServeMux = http.DefaultServeMux
	var verRoot = strings.SplitN(VERSION, ".", 2)[0]
	RESTMux.HandleFunc(fmt.Sprintf("/%s/ping/", verRoot), handlers.PingHandler)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/poll/", verRoot), handlers.PollHandler)
	RESTMux.HandleFunc("/status/", handlers.StatusHandler)

	logger.Info("main", "startup...", nil)

	go func() {
		errChan <- http.ListenAndServe(host+":"+port, nil)
	}()

	select {
	case err := <-errChan:
		if err != nil {
			panic("ListenAndServe: " + err.Error())
		}
	case <-sigChan:
		logger.Info("main", "Shutting down...", nil)
	}

}
Exemple #4
0
// -- main
func main() {

	var configFile string

	flag.StringVar(&configFile, "config", "config.ini", "Configuration File")
	flag.Parse()
	log.Printf("Using config %s", configFile)
	config := util.MzGetConfig(configFile)

	// Convert the token_key from base64 (if present)
	if k, ok := config["token_key"]; ok {
		key, _ := base64.URLEncoding.DecodeString(k.(string))
		config["token_key"] = key
	}

	logger = util.NewHekaLogger(config)

	simplepush.Clients = make(map[string]*simplepush.Client)

	// Initialize the common server.
	simplepush.InitServer(config, logger)

	// Register the handlers
	// each websocket gets it's own handler.
	http.HandleFunc("/update/", makeHandler(simplepush.UpdateHandler))
	http.HandleFunc("/status/", makeHandler(simplepush.StatusHandler))
	http.Handle("/", websocket.Handler(simplepush.PushSocketHandler))

	// Config the server
	host := util.MzGet(config, "host", "localhost")
	port := util.MzGet(config, "port", "8080")

	// Hoist the main sail
	logger.Info("main",
		fmt.Sprintf("listening on %s:%s", host, port), nil)
	err := http.ListenAndServe(fmt.Sprintf("%s:%s", host, port), nil)
	if err != nil {
		panic("ListenAndServe: " + err.Error())
	}
}
Exemple #5
0
// Open the database.
func Open(config util.JsMap, logger *util.HekaLogger) (store *Storage, err error) {
	dsn := fmt.Sprintf("user=%s password=%s host=%s dbname=%s sslmode=%s",
		util.MzGet(config, "db.user", "user"),
		util.MzGet(config, "db.password", "password"),
		util.MzGet(config, "db.host", "localhost"),
		util.MzGet(config, "db.db", "wmf"),
		util.MzGet(config, "db.sslmode", "disable"))
	logCat := "storage"
	defExpry, err := strconv.ParseInt(util.MzGet(config, "db.default_expry", "1500"), 0, 64)
	if err != nil {
		defExpry = 1500
	}
	db, err := sql.Open("postgres", dsn)
	if err != nil {
		return nil, err
	}
	db.SetMaxIdleConns(100)
	if err = db.Ping(); err != nil {
		return nil, err
	}
	store = &Storage{
		config:   config,
		logger:   logger,
		logCat:   logCat,
		defExpry: defExpry,
		dsn:      dsn,
		db:       db}
	if err = store.Init(); err != nil {
		return nil, err
	}
	return store, nil
}
Exemple #6
0
func main() {
	flags.ParseArgs(&opts, os.Args)

	// Configuration
	// defaults don't appear to work.
	if opts.ConfigFile == "" {
		opts.ConfigFile = "config.ini"
	}
	config := util.MzGetConfig(opts.ConfigFile)
	config["VERSION"] = VERSION
	if util.MzGetFlag(config, "aws.get_hostname") {
		if hostname, err := util.GetAWSPublicHostname(); err == nil {
			config["ws_hostname"] = hostname
		}
	}

	//TODO: Build out the partner cert pool if need be.
	// certpoo

	if opts.Profile != "" {
		log.Printf("Creating profile %s...\n", opts.Profile)
		f, err := os.Create(opts.Profile)
		if err != nil {
			log.Fatal("Profile creation failed:\n%s\n", err.Error())
			return
		}
		defer func() {
			log.Printf("Closing profile...\n")
			pprof.StopCPUProfile()
		}()
		pprof.StartCPUProfile(f)
	}
	if opts.MemProfile != "" {
		defer func() {
			profFile, err := os.Create(opts.MemProfile)
			if err != nil {
				log.Fatal("Memory Profile creation failed:\n%s\n", err.Error())
				return
			}
			pprof.WriteHeapProfile(profFile)
			profFile.Close()
		}()
	}

	runtime.GOMAXPROCS(runtime.NumCPU())
	logger := util.NewHekaLogger(config)
	store, err := storage.Open(config, logger)
	if err != nil {
		logger.Error("main", "FAIL", nil)
		return
	}
	handlers := wmf.NewHandler(config, logger, store)

	// Signal handler
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGHUP, SIGUSR1)

	// Rest Config
	errChan := make(chan error)
	host := util.MzGet(config, "host", "localhost")
	port := util.MzGet(config, "port", "8080")

	var RESTMux *http.ServeMux = http.DefaultServeMux
	var WSMux *http.ServeMux = http.DefaultServeMux
	var verRoot = strings.SplitN(VERSION, ".", 2)[0]

	// REST calls
	// Device calls.
	RESTMux.HandleFunc(fmt.Sprintf("/%s/register/", verRoot),
		handlers.Register)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/cmd/", verRoot),
		handlers.Cmd)
	// Web UI calls
	RESTMux.HandleFunc(fmt.Sprintf("/%s/queue/", verRoot),
		handlers.Queue)
	RESTMux.HandleFunc(fmt.Sprintf("/%s/state/", verRoot),
		handlers.State)
	RESTMux.HandleFunc("/static/",
		handlers.Static)
	RESTMux.HandleFunc("/metrics/",
		handlers.Metrics)
	// Operations call
	RESTMux.HandleFunc("/status/",
		handlers.Status)
	WSMux.Handle(fmt.Sprintf("/%s/ws/", verRoot),
		websocket.Handler(handlers.WSSocketHandler))
	// Handle root calls as webUI
	RESTMux.HandleFunc("/",
		handlers.Index)

	logger.Info("main", "startup...",
		util.Fields{"host": host, "port": port})

	go func() {
		errChan <- http.ListenAndServe(host+":"+port, nil)
	}()

	select {
	case err := <-errChan:
		if err != nil {
			panic("ListenAndServe: " + err.Error())
		}
	case <-sigChan:
		logger.Info("main", "Shutting down...", nil)
	}

}
Exemple #7
0
// -- main
func main() {
	var certFile string
	var keyFile string

	flag.Parse()
	config := util.MzGetConfig(*configFile)
	// The config file requires some customization and normalization
	config = simplepush.FixConfig(config)
	config["VERSION"] = VERSION
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Report what the app believes the current host to be, and what version.
	log.Printf("CurrentHost: %s, Version: %s",
		config["shard.current_host"], VERSION)

	// Metrics reporting
	if mprefix, ok := config["metrics.prefix"]; ok {
		simplepush.MetricsPrefix(mprefix.(string))
	}
	if mstatsd, ok := config["metrics.statsd_target"]; ok {
		err := simplepush.MetricsStatsdTarget(mstatsd.(string))
		if err != nil {
			log.Fatal("Couldn't create statsd client: ", err)
		}
	}

	// Only create profiles if requested. To view the application profiles,
	// see http://blog.golang.org/profiling-go-programs
	if *profile != "" {
		log.Printf("Creating profile...")
		f, err := os.Create(*profile)
		if err != nil {
			log.Fatal(err)
		}
		defer func() {
			log.Printf("Closing profile...")
			pprof.StopCPUProfile()
		}()
		pprof.StartCPUProfile(f)
	}
	if *memProfile != "" {
		defer func() {
			profFile, err := os.Create(*memProfile)
			if err != nil {
				log.Fatalln(err)
			}
			pprof.WriteHeapProfile(profFile)
			profFile.Close()
		}()
	}

	// Logging can be CPU intensive (note: variable reflection is VERY
	// CPU intensive. Avoid things like log.Printf("%v", someStruct) )
	// If logging is specified as a command line flag, it overrides the
	// value specified in the config file. This allows short term logging
	// for operations.
	if *logging > 0 {
		config["logger.enable"] = "1"
		config["logger.filter"] = strconv.FormatInt(int64(*logging), 10)
	}
	if v, ok := config["logger.enable"]; ok {
		if v, _ := strconv.ParseBool(v.(string)); v {
			logger = util.NewHekaLogger(config)
			logger.Info("main", "Enabling full logger", nil)
		}
	}

	// Routing allows stand-alone instances to send updates between themselves.
	// Websock does not allow for native redirects in some browsers. Routing
	// allows websocket connections and updates to be handled by any server,
	// with the approriate notification sent.
	//
	// Note: While this is fairly primative, it works. There are more efficient
	// models and systems that could be used for this (e.g. 0mq, rabbit, etc.)
	// however those also add additional complexity to the server system.
	// Since this is mostly point-to-point (we know the host location to send
	// to), there wasn't much justification to add that complexity.
	// Obviously, this can and will change over time.
	route = &router.Router{
		Port:   util.MzGet(config, "shard.port", "3000"),
		Logger: logger,
	}
	defer func() {
		if route != nil {
			route.CloseAll()
		}
	}()

	// Currently, we're opting for a memcache "storage" mechanism, however
	// and key/value store would suffice. (bonus points if the records are
	// self expiring.)
	store = storage.New(config, logger)

	// Initialize the common server.
	simplepush.InitServer(config, logger)
	handlers := simplepush.NewHandler(config, logger, store, route)

	// Config the server
	var wsport string
	var wshost string
	var WSMux *http.ServeMux = http.DefaultServeMux
	var RESTMux *http.ServeMux = http.DefaultServeMux
	host := util.MzGet(config, "host", "localhost")
	port := util.MzGet(config, "port", "8080")

	// Register the handlers
	// each websocket gets it's own handler.
	if util.MzGet(config, "wsport", port) != port {
		wsport = util.MzGet(config, "wsport", port)
		wshost = util.MzGet(config, "wshost", host)
		WSMux = http.NewServeMux()
	}

	RESTMux.HandleFunc("/update/", handlers.UpdateHandler)
	RESTMux.HandleFunc("/status/", handlers.StatusHandler)
	RESTMux.HandleFunc("/realstatus/", handlers.RealStatusHandler)
	RESTMux.HandleFunc("/metrics/", handlers.MetricsHandler)
	WSMux.Handle("/", websocket.Handler(handlers.PushSocketHandler))

	// Hoist the main sail.
	if logger != nil {
		logger.Info("main",
			fmt.Sprintf("listening on %s:%s", host, port), nil)
	}

	// Get the (optional) SSL certs
	if name, ok := config["ssl.certfile"]; ok {
		certFile = name.(string)
	}
	if name, ok := config["ssl.keyfile"]; ok {
		keyFile = name.(string)
	}
	wscertFile := util.MzGet(config, "ssl.ws.certfile", certFile)
	wskeyFile := util.MzGet(config, "ssl.ws.keyfile", keyFile)

	// wait for sigint
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGHUP, SIGUSR1)
	errChan := make(chan error)

	// Weigh the anchor!
	go func() {
		addr := host + ":" + port
		if len(certFile) > 0 && len(keyFile) > 0 {
			if logger != nil {
				logger.Info("main", "Using TLS", nil)
			}
			errChan <- http.ListenAndServeTLS(addr, certFile, keyFile, nil)
		} else {
			errChan <- http.ListenAndServe(addr, nil)
		}
	}()
	// Oh, we have a different context for WebSockets. Weigh that anchor too!
	if WSMux != RESTMux {
		if logger != nil {
			logger.Info("main", "Starting separate context for WS", nil)
			logger.Info("main",
				fmt.Sprintf("ws listen on %s:%s", wshost, wsport), nil)
		}
		go func() {
			wsaddr := wshost + ":" + wsport
			if len(wscertFile) > 0 && len(wskeyFile) > 0 {
				errChan <- http.ListenAndServeTLS(wsaddr, wscertFile, wskeyFile, WSMux)
			} else {
				errChan <- http.ListenAndServe(wsaddr, WSMux)
			}
		}()
	}

	// And we're underway!
	go route.HandleUpdates(updater)

	select {
	case err := <-errChan:
		if err != nil {
			panic("ListenAndServe: " + err.Error())
		}
	case <-sigChan:
		if logger != nil {
			logger.Info("main", "Recieved signal, shutting down.", nil)
		}
		route.CloseAll()
		route = nil
	}
}
Exemple #8
0
func New(opts util.JsMap, logger *util.HekaLogger) *Storage {
	config = opts
	var ok bool
	var err error

	if configEndpoint, ok := config["elasticache.config_endpoint"]; ok {
		memcacheEndpoint, err := getElastiCacheEndpointsTimeout(configEndpoint.(string), 2)
		if err == nil {
			config["memcache.server"] = memcacheEndpoint
		} else {
			fmt.Println(err)
			if logger != nil {
				logger.Error("storage", "Elastisearch error.",
					util.Fields{"error": err.Error()})
			}
		}
	}

	if _, ok = config["memcache.server"]; !ok {
		config["memcache.server"] = "127.0.0.1:11211"
	}

	timeout, err := time.ParseDuration(util.MzGet(config, "db.handle_timeout", "5s"))
	if err != nil {
		if logger != nil {
			logger.Error("storage", "Could not parse db.handle_timeout",
				util.Fields{"err": err.Error()})
		}
		timeout = 10 * time.Second
	}
	if _, ok = config["memcache.pool_size"]; !ok {
		config["memcache.pool_size"] = "100"
	}
	if config["memcache.pool_size"], err =
		strconv.ParseInt(config["memcache.pool_size"].(string), 0, 0); err != nil {
		config["memcache.pool_size"] = 100
	}
	poolSize := int(config["memcache.pool_size"].(int64))
	if _, ok = config["db.timeout_live"]; !ok {
		config["db.timeout_live"] = "259200"
	}

	if _, ok = config["db.timeout_reg"]; !ok {
		config["db.timeout_reg"] = "10800"
	}

	if _, ok = config["db.timeout_del"]; !ok {
		config["db.timeout_del"] = "86400"
	}
	if _, ok = config["shard.default_host"]; !ok {
		config["shard.default_host"] = "localhost"
	}
	if _, ok = config["shard.current_host"]; !ok {
		config["shard.current_host"] = config["shard.default_host"]
	}
	if _, ok = config["shard.prefix"]; !ok {
		config["shard.prefix"] = "_h-"
	}
	/*
		i, err := strconv.ParseInt(util.MzGet(config,
			"memcache.max_pool_size", "400"), 0, 10)
		if err != nil {
			if logger != nil {
				logger.Error("storage", "Could not parse memcache.max_pool_size",
					util.Fields{"error": err.Error()})
			}
			mcsMaxPoolSize = 400
		}
		mcsMaxPoolSize = int32(i)
	*/

	if logger != nil {
		logger.Info("storage", "Creating new gomc handler", nil)
	}
	// do NOT include any spaces
	servers := strings.Split(
		no_whitespace.Replace(config["memcache.server"].(string)),
		",")

	mcs := make(chan gomc.Client, poolSize)
	for i := 0; i < poolSize; i++ {
		mcs <- newMC(servers, config, logger)
	}

	return &Storage{
		mcs:        mcs,
		config:     config,
		logger:     logger,
		mc_timeout: timeout,
		servers:    servers,
	}
}
Exemple #9
0
// -- REST
func (self *Handler) UpdateHandler(resp http.ResponseWriter, req *http.Request) {
	// Handle the version updates.
	var err error
	var port string
	var vers int64

	if ClientCount() > self.config["max_connections"].(int) {
		if self.logger != nil {
			if toomany == 0 {
				atomic.StoreInt32(&toomany, 1)
				self.logger.Error("handler", "Socket Count Exceeded", nil)
			}
		}
		MetricIncrement("too many connections")
		http.Error(resp, "{\"error\": \"Server unavailable\"}",
			http.StatusServiceUnavailable)
		return
	}
	if toomany != 0 {
		atomic.StoreInt32(&toomany, 0)
	}

	timer := time.Now()
	filter := regexp.MustCompile("[^\\w-\\.\\=]")
	if self.logger != nil {
		self.logger.Debug("update", "Handling Update",
			util.Fields{"path": req.URL.Path})
	}
	if self.logger != nil {
		self.logger.Debug("update", "=========== UPDATE ====", nil)
	}

	defer func() {
		if self.logger != nil {
			self.logger.Debug("update", "+++++++++++++ DONE +++", nil)
		}
	}()
	if req.Method != "PUT" {
		http.Error(resp, "", http.StatusMethodNotAllowed)
		return
	}

	svers := req.FormValue("version")
	if svers != "" {
		vers, err = strconv.ParseInt(svers, 10, 64)
		if err != nil || vers < 0 {
			http.Error(resp, "\"Invalid Version\"", http.StatusBadRequest)
			return
		}
	} else {
		vers = time.Now().UTC().Unix()
	}

	elements := strings.Split(req.URL.Path, "/")
	pk := elements[len(elements)-1]
	if len(pk) == 0 {
		if self.logger != nil {
			self.logger.Error("update", "No token, rejecting request",
				util.Fields{"remoteAddr": req.RemoteAddr,
					"path": req.URL.Path})
		}
		http.Error(resp, "Token not found", http.StatusNotFound)
		return
	}

	if token, ok := self.config["token_key"]; ok && len(token.([]uint8)) > 0 {
		if self.logger != nil {
			// Note: dumping the []uint8 keys can produce terminal glitches
			self.logger.Debug("main", "Decoding...", nil)
		}
		var err error
		bpk, err := Decode(token.([]byte),
			pk)
		if err != nil {
			if self.logger != nil {
				self.logger.Error("update",
					"Could not decode token",
					util.Fields{"primarykey": pk,
						"remoteAddr": req.RemoteAddr,
						"path":       req.URL.Path,
						"error":      ErrStr(err)})
			}
			http.Error(resp, "", http.StatusNotFound)
			return
		}

		pk = strings.TrimSpace(string(bpk))
	}

	if filter.Find([]byte(pk)) != nil {
		if self.logger != nil {
			self.logger.Error("update",
				"Invalid token for update",
				util.Fields{"token": pk,
					"path": req.URL.Path})
		}
		http.Error(resp, "Invalid Token", http.StatusNotFound)
		return
	}

	uaid, chid, err := storage.ResolvePK(pk)
	if err != nil {
		if self.logger != nil {
			self.logger.Error("update",
				"Could not resolve PK",
				util.Fields{"primaryKey": pk,
					"path":  req.URL.Path,
					"error": ErrStr(err)})
		}
		return
	}

	if chid == "" {
		if self.logger != nil {
			self.logger.Error("update",
				"Incomplete primary key",
				util.Fields{"uaid": uaid,
					"channelID":  chid,
					"remoteAddr": req.RemoteAddr})
		}
		return
	}

	//log.Printf("<< %s.%s = %d", uaid, chid, vers)

	if iport, ok := self.config["port"]; ok {
		port = iport.(string)
	}
	if port == "80" {
		port = ""
	}
	if port != "" {
		port = ":" + port
	}
	currentHost := util.MzGet(self.config, "shard.current_host", "localhost")
	host, err := self.store.GetUAIDHost(uaid)
	if err != nil {
		if self.logger != nil {
			self.logger.Error("update",
				"Could not discover host for UAID",
				util.Fields{"uaid": uaid,
					"error": err.Error()})
		}
		host = util.MzGet(self.config, "shard.defaultHost", "localhost")
	}
	if util.MzGetFlag(self.config, "shard.do_proxy") {
		if host != currentHost && host != "localhost" {
			if self.logger != nil {
				self.logger.Info("update",
					"Proxying request for UAID",
					util.Fields{"uaid": uaid,
						"destination": host + port})
			}
			// Use tcp routing.
			if util.MzGetFlag(self.config, "shard.router") {
				// If there was an error routing the update, don't
				// tell the AppServer. Chances are it's temporary, and
				// the client will get the update on next refresh/reconnect
				self.router.SendUpdate(host, uaid, chid, vers, timer)
			} else {
				proto := "http"
				if len(util.MzGet(self.config, "ssl.certfile", "")) > 0 {
					proto = "https"
				}

				err = proxyNotification(proto, host+port, req.URL.Path, vers)
				if err != nil && self.logger != nil {
					self.logger.Error("update",
						"Proxy failed", util.Fields{
							"uaid":        uaid,
							"destination": host + port,
							"error":       err.Error()})
				}
			}
			MetricIncrement("routing update: out")
			if err != nil {
				http.Error(resp, err.Error(), 500)
			} else {
				resp.Write([]byte("Ok"))
			}
			return
		}
	}

	if self.logger != nil {
		defer func(uaid, chid, path string, timer time.Time) {
			self.logger.Info("timer", "Client Update complete",
				util.Fields{
					"uaid":      uaid,
					"path":      req.URL.Path,
					"channelID": chid,
					"duration":  strconv.FormatInt(time.Now().Sub(timer).Nanoseconds(), 10)})
		}(uaid, chid, req.URL.Path, timer)

		self.logger.Info("update",
			"setting version for ChannelID",
			util.Fields{"uaid": uaid, "channelID": chid,
				"version": strconv.FormatInt(vers, 10)})
	}
	err = self.store.UpdateChannel(pk, vers)

	if err != nil {
		if self.logger != nil {
			self.logger.Error("update", "Cound not update channel",
				util.Fields{"UAID": uaid,
					"channelID": chid,
					"version":   strconv.FormatInt(vers, 10),
					"error":     err.Error()})
		}
		status, _ := sperrors.ErrToStatus(err)
		http.Error(resp, "Could not update channel version", status)
		return
	}
	resp.Header().Set("Content-Type", "application/json")
	resp.Write([]byte("{}"))
	// Ping the appropriate server
	if client, ok := Clients[uaid]; ok {
		Flush(client, chid, int64(vers))
	}
	MetricIncrement("update channel")
	return
}
Exemple #10
0
// user login functions
func (self *Handler) Index(resp http.ResponseWriter, req *http.Request) {
	/* Handle a user login to the web UI
	 */

	// This should be handled by an nginx rule.
	if strings.Contains(req.URL.Path, "/static/") {
		if strings.Contains(req.URL.Path, "..") {
			return
		}
		body, err := ioutil.ReadFile("." + req.URL.Path)
		if err == nil {
			resp.Write(body)
		}
		return
	}
	self.logCat = "handler:Index"
	var data struct {
		ProductName string
		UserId      string
		MapKey      string
		DeviceList  []storage.DeviceList
		Device      *storage.Device
		Host        map[string]string
	}

	var err error

	// Get this from the config file?
	data.ProductName = util.MzGet(self.config, "productname", "Where's My Fox")

	data.MapKey = util.MzGet(self.config, "mapbox.key", "")

	// host information (for websocket callback)
	data.Host = make(map[string]string)
	data.Host["Hostname"] = util.MzGet(self.config,
		"ws_hostname", "localhost")

	// get the cached session info (if present)
	// will also resolve assertions and other bits to get user and dev info.
	sessionInfo, err := self.getSessionInfo(req)
	if err == nil {
		// we have user info, use it.
		data.UserId = sessionInfo.UserId
		if sessionInfo.DeviceId == "" {
			data.DeviceList, err = self.store.GetDevicesForUser(data.UserId)
			if err != nil {
				self.logger.Error(self.logCat, "Could not get user devices",
					util.Fields{"error": err.Error(),
						"user": data.UserId})
			}
			if len(data.DeviceList) == 1 {
				sessionInfo.DeviceId = (data.DeviceList[0]).ID
				data.DeviceList = nil
			}
		}
		if sessionInfo.DeviceId != "" {
			data.Device, err = self.store.GetDeviceInfo(sessionInfo.DeviceId)
			if err != nil {
				self.logger.Error(self.logCat, "Could not get device info",
					util.Fields{"error": err.Error(),
						"user": data.UserId})
				if file, err := ioutil.ReadFile("static/error.html"); err == nil {
					resp.Write(file)
				}
				return
			}
			data.Device.PreviousPositions, err = self.store.GetPositions(sessionInfo.DeviceId)
			if err != nil {
				self.logger.Error(self.logCat,
					"Could not get device's position information",
					util.Fields{"error": err.Error(),
						"user":   data.UserId,
						"device": sessionInfo.DeviceId})
				return
			}
		}
	}

	// render the page from the template
	tmpl, err := template.New("index.html").ParseFiles("static/index.html")
	if err != nil {
		// TODO: display error
		self.logger.Error(self.logCat, "Could not display index page",
			util.Fields{"error": err.Error(),
				"user": data.UserId})
		if file, err := ioutil.ReadFile("static/error.html"); err == nil {
			resp.Write(file)
		}
		return
	}
	setSessionInfo(resp, sessionInfo)
	err = tmpl.Execute(resp, data)
	if err != nil {
		self.logger.Error(self.logCat, "Could not execute template",
			util.Fields{"error": err.Error()})
	}
	self.metrics.Increment("page.index")
	return
}
Exemple #11
0
func (self *Handler) Queue(resp http.ResponseWriter, req *http.Request) {
	/* Queue commands for the device.
	 */
	var err error
	var lbody int

	rep := make(reply_t)

	self.logCat = "handler:Queue"
	resp.Header().Set("Content-Type", "application/json")
	deviceId := getDevFromUrl(req)
	if deviceId == "" {
		self.logger.Error(self.logCat, "Invalid call (No device id)", nil)
		http.Error(resp, "Unauthorized", 401)
		return
	}
	userId, _, err := self.getUser(req)
	if err != nil {
		self.logger.Error(self.logCat, "No userid", nil)
		http.Error(resp, "Unauthorized", 401)
		return
	}
	if userId == "" {
		http.Error(resp, "Unauthorized", 401)
		return
	}

	devRec, err := self.store.GetDeviceInfo(deviceId)
	if devRec.User != userId {
		http.Error(resp, "Unauthorized", 401)
		return
	}
	if devRec == nil {
		self.logger.Error(self.logCat,
			"Queue:User requested unknown device",
			util.Fields{
				"deviceId": deviceId,
				"userId":   userId})
		http.Error(resp, "Unauthorized", 401)
		return
	}
	if err != nil {
		switch err {
		default:
			self.logger.Error(self.logCat,
				"Cmd:Unhandled Error",
				util.Fields{
					"error":    err.Error(),
					"deviceId": deviceId})
			http.Error(resp, "Unauthorized", 401)
		}
		return
	}

	//decode the body
	var body = make([]byte, req.ContentLength)
	lbody, err = req.Body.Read(body)
	if err != nil {
		self.logger.Error(self.logCat, "Could not read body",
			util.Fields{"error": err.Error()})
		http.Error(resp, "Invalid", 400)
		return
	}
	self.logger.Info(self.logCat, "Handling cmd",
		util.Fields{
			"cmd":   string(body),
			"lbody": fmt.Sprintf("%d", lbody),
		})
	if lbody > 0 {
		reply := make(reply_t)
		merr := json.Unmarshal(body, &reply)
		//	merr := json.Unmarshal(body, &reply)
		if merr != nil {
			self.logger.Error(self.logCat, "Could not unmarshal data",
				util.Fields{
					"error": merr.Error(),
					"body":  string(body)})
			http.Error(resp, "Server Error", 500)
			return
		}

		for cmd, args := range reply {
			// sanitize values.
			var v interface{}
			var ok bool
			c := strings.ToLower(string(cmd[0]))
			if !strings.Contains(devRec.Accepts, c) {
				// skip unacceptable command
				self.logger.Warn(self.logCat, "Agent does not accept command",
					util.Fields{"unacceptable": c,
						"acceptable": devRec.Accepts})
				rep["error"] = 422
				rep["cmd"] = cmd
				continue
			}
			rargs := args.(map[string]interface{})
			switch c {
			case "l":
				if v, ok = rargs["c"]; ok {
					max, err := strconv.ParseInt(util.MzGet(self.config,
						"cmd.c.max",
						"9999"), 10, 64)
					if err != nil {
						max = 9999
					}
					vs := v.(string)
					rargs["c"] = strings.Map(digitsOnly,
						vs[:minInt(4, len(vs))])
					rargs["c"] = self.rangeCheck(rargs["c"].(string),
						0,
						max)
				}
				if v, ok = rargs["m"]; ok {
					vs := v.(string)
					rargs["m"] = strings.Map(asciiOnly,
						vs[:minInt(100, len(vs))])
				}
			case "r", "t":
				if v, ok = rargs["d"]; ok {
					max, err := strconv.ParseInt(
						util.MzGet(self.config,
							"cmd."+c+".max",
							"10500"), 10, 64)
					if err != nil {
						max = 10500
					}
					vs := v.(string)
					rargs["d"] = strings.Map(digitsOnly, vs)
					rargs["d"] = self.rangeCheck(rargs["d"].(string),
						0, max)
				}
			case "e":
				rargs = storage.Unstructured{}
			default:
				http.Error(resp, "Invalid Command", 400)
				return
			}
			fixed, err := json.Marshal(storage.Unstructured{c: rargs})
			if err != nil {
				// Log the error
				self.logger.Error(self.logCat, "Error handling command",
					util.Fields{"error": err.Error(),
						"command": string(cmd),
						"device":  deviceId,
						"args":    fmt.Sprintf("%v", rargs)})
				http.Error(resp, "Server Error", http.StatusServiceUnavailable)
			}
			err = self.store.StoreCommand(deviceId, string(fixed))
			if err != nil {
				// Log the error
				self.logger.Error(self.logCat, "Error storing command",
					util.Fields{"error": err.Error(),
						"command": string(cmd),
						"device":  deviceId,
						"args":    fmt.Sprintf("%v", args)})
				http.Error(resp, "Server Error", http.StatusServiceUnavailable)
			}
			// trigger the push
			self.metrics.Increment("cmd.store." + c)
			self.metrics.Increment("push.send")
			err = SendPush(devRec, &self.config)
			if err != nil {
				self.logger.Error(self.logCat, "Could not send Push",
					util.Fields{"error": err.Error(),
						"pushUrl": devRec.PushUrl})
				http.Error(resp, "Server Error", http.StatusServiceUnavailable)
			}
		}
	}
	repl, _ := json.Marshal(rep)
	resp.Write(repl)
}