func (self *Storage) Status() (success bool, err error) { defer func() { if recv := recover(); recv != nil { success = false err = recv.(error) return } }() fake_id, _ := util.GenUUID4() key := "status_" + fake_id mc, err := self.getMC() defer self.returnMC(mc) if err != nil { return false, err } err = mc.Set("status_"+fake_id, "test", 6*time.Second) if err != nil { return false, err } var val string err = mc.Get(key, &val) if err != nil || val != "test" { return false, errors.New("Invalid value returned") } mc.Delete(key, time.Duration(0)) return true, nil }
// A client connects! func (self *Serv) Hello(worker *Worker, cmd PushCommand, sock *PushWS) (result int, arguments util.JsMap) { var uaid string args := cmd.Arguments.(util.JsMap) if self.logger != nil { chidss := "" if chids, ok := args["channelIDs"]; ok { chidss = "[" + strings.Join(chids.([]string), ", ") + "]" } self.logger.Info("server", "handling 'hello'", util.Fields{"uaid": args["uaid"].(string), "channelIDs": chidss}) } // TODO: If the client needs to connect to a different server, // Look up the appropriate server (based on UAID) // return a response that looks like: // { uaid: UAIDValue, status: 302, redirect: NewWS_URL } // New connects overwrite previous connections. // Raw client if args["uaid"] == "" { uaid, _ = util.GenUUID4() if self.logger != nil { self.logger.Debug("server", "Generating new UAID", util.Fields{"uaid": uaid}) } } else { uaid = args["uaid"].(string) if self.logger != nil { self.logger.Debug("server", "Using existing UAID", util.Fields{"uaid": uaid}) } delete(args, "uaid") } prop := self.Set_proprietary_info(args) if self.logger != nil { self.logger.Debug("server", "Proprietary Info", util.Fields{"ip": prop.Ip, "port": prop.Port}) } // Create a new, live client entry for this record. // See Bye for discussion of potential longer term storage of this info sock.Uaid = uaid client := &Client{ Worker: worker, PushWS: *sock, UAID: uaid, Prop: prop, } MuClient.Lock() Clients[uaid] = client MuClient.Unlock() MetricIncrement("updates.client.connect") // We don't register the list of known ChannelIDs since we echo // back any ChannelIDs sent on behalf of this UAID. args["uaid"] = uaid arguments = args result = 200 return result, arguments }
// Associate the UAID for this socket connection (and flush any data that // may be pending for the connection) func (self *Worker) Hello(sock *PushWS, buffer interface{}) (err error) { // register the UAID defer func() { if r := recover(); r != nil { debug.PrintStack() if self.logger != nil { self.logger.Error("worker", "Unhandled error", util.Fields{"cmd": "hello", "error": r.(error).Error()}) } err = sperrors.InvalidDataError } }() //Force the client to re-register all it's clients. // This is done by returning a new UAID. forceReset := false var suggestedUAID string data := buffer.(util.JsMap) if _, ok := data["uaid"]; !ok { // Must include "uaid" (even if blank) data["uaid"] = "" } if redir, ok := self.config["db.redirect"]; ok { resp := util.JsMap{ "messageType": data["messageType"], "status": 302, "redirect": redir, "uaid": sock.Uaid} if self.logger != nil { self.logger.Debug("worker", "sending redirect", util.Fields{"messageType": data["messageType"].(string), "status": strconv.FormatInt(data["status"].(int64), 10), "redirect": data["redirect"].(string), "uaid": data["uaid"].(string)}) } websocket.JSON.Send(sock.Socket, resp) return nil } suggestedUAID = data["uaid"].(string) if data["channelIDs"] == nil { // Must include "channelIDs" (even if empty) if self.logger != nil { self.logger.Debug("worker", "Missing ChannelIDs", nil) } return sperrors.MissingDataError } if len(sock.Uaid) > 0 && len(data["uaid"].(string)) > 0 && sock.Uaid != suggestedUAID { // if there's already a Uaid for this channel, don't accept a new one if self.logger != nil { self.logger.Debug("worker", "Conflicting UAIDs", nil) } return sperrors.InvalidChannelError } if self.filter.Find([]byte(strings.ToLower(suggestedUAID))) != nil { if self.logger != nil { self.logger.Debug("worker", "Invalid character in UAID", nil) } return sperrors.InvalidChannelError } if len(sock.Uaid) == 0 { // if there's no UAID for the socket, accept or create a new one. sock.Uaid = suggestedUAID if len(sock.Uaid) > UAID_MAX_LEN { if self.logger != nil { self.logger.Debug("worker", "UAID is too long", nil) } return sperrors.InvalidDataError } if len(sock.Uaid) == 0 { forceReset = forceReset || true } if ClientCollision(sock.Uaid) { forceReset = true } if num := len(data["channelIDs"].([]interface{})); num > 0 { // are there a suspicious number of channels? if int64(num) > self.maxChannels { forceReset = forceReset || true } if !sock.Store.IsKnownUaid(sock.Uaid) { forceReset = forceReset || true } } } if forceReset { if self.logger != nil { self.logger.Warn("worker", "Resetting UAID for device", util.Fields{"uaid": sock.Uaid}) } if len(sock.Uaid) > 0 { sock.Store.PurgeUAID(sock.Uaid) } sock.Uaid, _ = util.GenUUID4() } // register the sockets (NOOP) // register any proprietary connection requirements // alert the master of the new UAID. cmd := PushCommand{ Command: HELLO, Arguments: util.JsMap{ "worker": self, "uaid": sock.Uaid, "chids": data["channelIDs"], }, } // blocking call back to the boss. raw_result, args := HandleServerCommand(cmd, sock) result := PushCommand{raw_result, args} if err = sock.Store.SetUAIDHost(sock.Uaid, ""); err != nil { return err } if self.logger != nil { self.logger.Debug("worker", "sending response", util.Fields{"cmd": "hello", "error": ErrStr(err), "uaid": sock.Uaid}) } // websocket.JSON.Send(sock.Socket, util.JsMap{ // "messageType": data["messageType"], // "status": result.Command, // "uaid": sock.Uaid}) msg := []byte("{\"messageType\":\"" + data["messageType"].(string) + "\",\"status\":" + strconv.FormatInt(int64(result.Command), 10) + ",\"uaid\":\"" + sock.Uaid + "\"}") _, err = sock.Socket.Write(msg) self.state = ACTIVE if err == nil { // Get the lastAccessed time from wherever return self.Flush(sock, 0, "", 0) } return err }
// Register a new device to a given userID. func (self *Storage) RegisterDevice(userid string, dev Device) (devId string, err error) { // value check? statement := "insert into deviceInfo (deviceId, lockable, loggedin, lastExchange, hawkSecret, accepts, pushUrl) values ($1, $2, $3, $4, $5, $6, $7);" if dev.ID == "" { dev.ID, _ = util.GenUUID4() } dbh := self.db if err != nil { self.logger.Error(self.logCat, "Could not insert device", util.Fields{"error": err.Error()}) return "", err } if _, err = dbh.Exec(statement, string(dev.ID), dev.Lockable, dev.LoggedIn, dbNow(), dev.Secret, dev.Accepts, dev.PushUrl); err != nil { if strings.Contains(err.Error(), "duplicate key value") { fmt.Printf("#### Updating... \n") statement = "update deviceinfo set lockable=$2, accepts=$3, pushUrl=$4, hawkSecret=$5 where deviceId=$1" if _, err = dbh.Exec(statement, string(dev.ID), dev.Lockable, dev.Accepts, dev.PushUrl, dev.Secret, ); err != nil { self.logger.Error(self.logCat, "Could not update device", util.Fields{"error": err.Error(), "device": fmt.Sprintf("%+v", dev)}) return "", err } statement = "update usertodevicemap set name = $1 where deviceId=$2 and userId=$3" if _, err = dbh.Exec(statement, string(dev.Name), string(dev.ID), userid, ); err != nil { self.logger.Error(self.logCat, "Could not update device name", util.Fields{"error": err.Error(), "device": fmt.Sprintf("%+v", dev), "userid": userid}) return "", err } } else { self.logger.Error(self.logCat, "Could not create device", util.Fields{"error": err.Error(), "device": fmt.Sprintf("%+v", dev)}) return "", err } } else { if _, err = dbh.Exec("insert into userToDeviceMap (userId, deviceId, name) values ($1, $2, $3);", userid, dev.ID, dev.Name); err != nil { switch { default: self.logger.Error(self.logCat, "Could not map device to user", util.Fields{ "uid": userid, "deviceId": dev.ID, "name": dev.Name, "error": err.Error()}) return "", err } } } return dev.ID, nil }
func (self *Handler) Register(resp http.ResponseWriter, req *http.Request) { /*register a new device */ var buffer util.JsMap = util.JsMap{} var userid string var user string var pushUrl string var deviceid string var secret string var accepts string var lockable bool var ok bool self.logCat = "handler:Register" buffer, err := parseBody(req.Body) if err != nil { http.Error(resp, "No body", http.StatusBadRequest) } else { if assertion, ok := buffer["assert"].(string); !ok { self.logger.Error(self.logCat, "Missing assertion", nil) http.Error(resp, "Unauthorized", 401) return } else { userid, user, err = self.verifyAssertion(assertion) if err != nil { http.Error(resp, "Unauthorized", 401) } self.logger.Info(self.logCat, "### Got user "+userid, nil) user = strings.SplitN(user, "@", 2)[0] } if _, ok = buffer["pushurl"]; !ok { self.logger.Error(self.logCat, "Missing SimplePush url", nil) http.Error(resp, "Bad Data", 400) return } else { pushUrl = buffer["pushurl"].(string) } //ALWAYS generate a new secret on registration! secret = GenNonce(16) if _, ok = buffer["deviceid"]; !ok { deviceid, err = util.GenUUID4() } else { deviceid = buffer["deviceid"].(string) } if _, ok = buffer["has_passcode"]; !ok { lockable = true } else { lockable, err = strconv.ParseBool(buffer["has_passcode"].(string)) if err != nil { lockable = false } } if k, ok := buffer["accepts"]; ok { // collapse the array to a string if l := len(k.([]interface{})); l > 0 { acc := make([]byte, l) for n, ke := range k.([]interface{}) { acc[n] = ke.(string)[0] } accepts = strings.ToLower(string(acc)) } } if len(accepts) == 0 { accepts = "elrth" } // create the new device record var devId string if devId, err = self.store.RegisterDevice( userid, storage.Device{ ID: deviceid, Name: user, Secret: secret, PushUrl: pushUrl, Lockable: lockable, Accepts: accepts, }); err != nil { self.logger.Error(self.logCat, "Error Registering device", nil) http.Error(resp, "Bad Request", 400) return } else { if devId != deviceid { self.logger.Error(self.logCat, "Different deviceID returned", util.Fields{"original": deviceid, "new": devId}) http.Error(resp, "Server error", 500) return } self.devId = deviceid } } self.metrics.Increment("registration") resp.Write([]byte(fmt.Sprintf("{\"deviceid\":\"%s\", \"secret\":\"%s\"}", self.devId, secret))) return }