def handler(self, name, data):
        if name == 'patch':
            Listener.apply_patch(data)
        elif name == 'get_buf':
            buf_id = data['id']
            buf = listener.BUFS.get(buf_id)
            if not buf:
                return msg.warn('no buf found: %s.  Hopefully you didn\'t need that' % data)
            timeout_id = buf.get('timeout_id')
            if timeout_id:
                utils.cancel_timeout(timeout_id)

            if data['encoding'] == 'base64':
                data['buf'] = base64.b64decode(data['buf'])
            # forced_patch doesn't exist in data, so this is equivalent to buf['forced_patch'] = False
            listener.BUFS[buf_id] = data
            view = listener.get_view(buf_id)
            if view:
                Listener.update_view(data, view)
            else:
                listener.save_buf(data)
        elif name == 'create_buf':
            if data['encoding'] == 'base64':
                data['buf'] = base64.b64decode(data['buf'])
            listener.BUFS[data['id']] = data
            listener.PATHS_TO_IDS[data['path']] = data['id']
            listener.save_buf(data)
            cb = listener.CREATE_BUF_CBS.get(data['path'])
            if cb:
                del listener.CREATE_BUF_CBS[data['path']]
                try:
                    cb(data['id'])
                except Exception as e:
                    print(e)
        elif name == 'rename_buf':
            del listener.PATHS_TO_IDS[data['old_path']]
            listener.PATHS_TO_IDS[data['path']] = data['id']
            new = utils.get_full_path(data['path'])
            old = utils.get_full_path(data['old_path'])
            new_dir = os.path.split(new)[0]
            if new_dir:
                utils.mkdir(new_dir)
            os.rename(old, new)
            view = listener.get_view(data['id'])
            if view:
                view.retarget(new)
            listener.BUFS[data['id']]['path'] = data['path']
        elif name == 'delete_buf':
            path = utils.get_full_path(data['path'])
            listener.delete_buf(data['id'])
            try:
                utils.rm(path)
            except Exception:
                pass
            user_id = data.get('user_id')
            username = self.get_username_by_id(user_id)
            msg.log('%s deleted %s' % (username, path))
        elif name == 'room_info':
            Listener.reset()
            G.JOINED_WORKSPACE = True
            # Success! Reset counter
            self.reconnect_delay = self.INITIAL_RECONNECT_DELAY
            self.retries = self.MAX_RETRIES

            self.workspace_info = data
            G.PERMS = data['perms']

            if 'patch' not in data['perms']:
                msg.log('No patch permission. Setting buffers to read-only')
                if sublime.ok_cancel_dialog('You don\'t have permission to edit this workspace. All files will be read-only.\n\nDo you want to request edit permission?'):
                    self.put({'name': 'request_perms', 'perms': ['edit_room']})

            project_json = {
                'folders': [
                    {'path': G.PROJECT_PATH}
                ]
            }

            utils.mkdir(G.PROJECT_PATH)
            with open(os.path.join(G.PROJECT_PATH, '.sublime-project'), 'wb') as project_fd:
                project_fd.write(json.dumps(project_json, indent=4, sort_keys=True).encode('utf-8'))

            floo_json = {
                'url': utils.to_workspace_url({
                    'host': self.host,
                    'owner': self.owner,
                    'port': self.port,
                    'workspace': self.workspace,
                    'secure': self.secure,
                })
            }
            with open(os.path.join(G.PROJECT_PATH, '.floo'), 'w') as floo_fd:
                floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

            for buf_id, buf in data['bufs'].items():
                buf_id = int(buf_id)  # json keys must be strings
                buf_path = utils.get_full_path(buf['path'])
                new_dir = os.path.dirname(buf_path)
                utils.mkdir(new_dir)
                listener.BUFS[buf_id] = buf
                listener.PATHS_TO_IDS[buf['path']] = buf_id
                # TODO: stupidly inefficient
                view = listener.get_view(buf_id)
                if view and not view.is_loading() and buf['encoding'] == 'utf8':
                    view_text = listener.get_text(view)
                    view_md5 = hashlib.md5(view_text.encode('utf-8')).hexdigest()
                    if view_md5 == buf['md5']:
                        msg.debug('md5 sum matches view. not getting buffer %s' % buf['path'])
                        buf['buf'] = view_text
                        G.VIEW_TO_HASH[view.buffer_id()] = view_md5
                    elif self.get_bufs:
                        Listener.get_buf(buf_id)
                    #TODO: maybe send patch here?
                else:
                    try:
                        buf_fd = open(buf_path, 'rb')
                        buf_buf = buf_fd.read()
                        md5 = hashlib.md5(buf_buf).hexdigest()
                        if md5 == buf['md5']:
                            msg.debug('md5 sum matches. not getting buffer %s' % buf['path'])
                            if buf['encoding'] == 'utf8':
                                buf_buf = buf_buf.decode('utf-8')
                            buf['buf'] = buf_buf
                        elif self.get_bufs:
                            Listener.get_buf(buf_id)
                    except Exception as e:
                        msg.debug('Error calculating md5:', e)
                        Listener.get_buf(buf_id)

            msg.log('Successfully joined workspace %s/%s' % (self.owner, self.workspace))

            temp_data = data.get('temp_data', {})
            hangout = temp_data.get('hangout', {})
            hangout_url = hangout.get('url')
            if hangout_url:
                self.prompt_join_hangout(hangout_url)

            if self.on_room_info:
                self.on_room_info()
                self.on_room_info = None
        elif name == 'user_info':
            user_id = str(data['user_id'])
            user_info = data['user_info']
            self.workspace_info['users'][user_id] = user_info
            if user_id == str(self.workspace_info['user_id']):
                G.PERMS = user_info['perms']
        elif name == 'join':
            msg.log('%s joined the workspace' % data['username'])
            user_id = str(data['user_id'])
            self.workspace_info['users'][user_id] = data
        elif name == 'part':
            msg.log('%s left the workspace' % data['username'])
            user_id = str(data['user_id'])
            try:
                del self.workspace_info['users'][user_id]
            except Exception as e:
                print('Unable to delete user %s from user list' % (data))
            region_key = 'floobits-highlight-%s' % (user_id)
            for window in sublime.windows():
                for view in window.views():
                    view.erase_regions(region_key)
        elif name == 'highlight':
            region_key = 'floobits-highlight-%s' % (data['user_id'])
            Listener.highlight(data['id'], region_key, data['username'], data['ranges'], data.get('ping', False))
        elif name == 'set_temp_data':
            hangout_data = data.get('data', {})
            hangout = hangout_data.get('hangout', {})
            hangout_url = hangout.get('url')
            if hangout_url:
                self.prompt_join_hangout(hangout_url)
        elif name == 'saved':
            try:
                buf = listener.BUFS[data['id']]
                username = self.get_username_by_id(data['user_id'])
                msg.log('%s saved buffer %s' % (username, buf['path']))
            except Exception as e:
                msg.error(str(e))
        elif name == 'request_perms':
            print(data)
            user_id = str(data.get('user_id'))
            username = self.get_username_by_id(user_id)
            if not username:
                return msg.debug('Unknown user for id %s. Not handling request_perms event.' % user_id)
            perm_mapping = {
                'edit_room': 'edit',
                'admin_room': 'admin',
            }
            perms = data.get('perms')
            perms_str = ''.join([perm_mapping.get(p) for p in perms])
            prompt = 'User %s is requesting %s permission for this room.' % (username, perms_str)
            message = data.get('message')
            if message:
                prompt += '\n\n%s says: %s' % (username, message)
            prompt += '\n\nDo you want to grant them permission?'
            confirm = bool(sublime.ok_cancel_dialog(prompt))
            if confirm:
                action = 'add'
            else:
                action = 'reject'
            self.put({
                'name': 'perms',
                'action': action,
                'user_id': user_id,
                'perms': perms
            })
        elif name == 'perms':
            action = data['action']
            user_id = str(data['user_id'])
            user = self.workspace_info['users'].get(user_id)
            if user is None:
                msg.log('No user for id %s. Not handling perms event' % user_id)
                return
            perms = set(user['perms'])
            if action == 'add':
                perms |= set(data['perms'])
            elif action == 'remove':
                perms -= set(data['perms'])
            else:
                return
            user['perms'] = list(perms)
            if user_id == self.workspace_info['user_id']:
                G.PERMS = perms
        elif name == 'msg':
            self.on_msg(data)
        else:
            msg.debug('unknown name!', name, 'data:', data)
Beispiel #2
0
    def protocol(self, req):
        self.buf += req.decode('utf-8')
        msg.debug('buf: %s' % self.buf)
        while True:
            before, sep, after = self.buf.partition('\n')
            if not sep:
                break
            try:
                data = json.loads(before)
            except Exception as e:
                msg.error('Unable to parse json: %s' % str(e))
                msg.error('Data: %s' % before)
                raise e
            name = data.get('name')
            if name == 'patch':
                # TODO: we should do this in a separate thread
                Listener.apply_patch(data)
            elif name == 'get_buf':
                buf_id = data['id']
                listener.BUFS[buf_id] = data
                view = listener.get_view(buf_id)
                if view:
                    Listener.update_view(data, view)
                else:
                    listener.save_buf(data)
            elif name == 'create_buf':
                listener.BUFS[data['id']] = data
                listener.save_buf(data)
            elif name == 'rename_buf':
                new = utils.get_full_path(data['path'])
                old = utils.get_full_path(data['old_path'])
                new_dir = os.path.split(new)[0]
                if new_dir:
                    utils.mkdir(new_dir)
                os.rename(old, new)
                view = listener.get_view(data['id'])
                if view:
                    view.retarget(new)
            elif name == 'delete_buf':
                path = utils.get_full_path(data['path'])
                try:
                    utils.rm(path)
                except Exception:
                    pass
                listener.delete_buf(data['id'])
            elif name == 'room_info':
                # Success! Reset counter
                self.retries = G.MAX_RETRIES
                self.room_info = data
                G.PERMS = data['perms']

                if 'patch' not in data['perms']:
                    msg.log(
                        'We don\'t have patch permission. Setting buffers to read-only'
                    )

                project_json = {'folders': [{'path': G.PROJECT_PATH}]}

                utils.mkdir(G.PROJECT_PATH)
                with open(os.path.join(G.PROJECT_PATH, '.sublime-project'),
                          'wb') as project_fd:
                    project_fd.write(
                        json.dumps(project_json, indent=4,
                                   sort_keys=True).encode('utf-8'))

                floo_json = {
                    'url':
                    utils.to_room_url({
                        'host': self.host,
                        'owner': self.owner,
                        'port': self.port,
                        'room': self.room,
                        'secure': self.secure,
                    })
                }
                with open(os.path.join(G.PROJECT_PATH, '.floo'),
                          'w') as floo_fd:
                    floo_fd.write(
                        json.dumps(floo_json, indent=4, sort_keys=True))

                for buf_id, buf in data['bufs'].items():
                    buf_id = int(buf_id)  # json keys must be strings
                    buf_path = utils.get_full_path(buf['path'])
                    new_dir = os.path.dirname(buf_path)
                    utils.mkdir(new_dir)
                    listener.BUFS[buf_id] = buf
                    try:
                        buf_fd = open(buf_path, 'rb')
                        buf_buf = buf_fd.read().decode('utf-8')
                        md5 = hashlib.md5(buf_buf.encode('utf-8')).hexdigest()
                        if md5 == buf['md5']:
                            msg.debug('md5 sums match. not getting buffer')
                            buf['buf'] = buf_buf
                        else:
                            msg.debug(
                                'md5 for %s should be %s but is %s. getting buffer'
                                % (buf['path'], buf['md5'], md5))
                            raise Exception('different md5')
                    except Exception as e:
                        msg.debug('Error calculating md5:', e)
                        Listener.get_buf(buf_id)

                self.authed = True
                G.CONNECTED = True
                msg.log('Successfully joined room %s/%s' %
                        (self.owner, self.room))
                if self.on_connect:
                    self.on_connect(self)
                    self.on_connect = None
            elif name == 'join':
                msg.log('%s joined the room' % data['username'])
                self.room_info['users'][data['user_id']] = data['username']
            elif name == 'part':
                msg.log('%s left the room' % data['username'])
                try:
                    del self.room_info['users'][data['user_id']]
                except Exception as e:
                    print('Unable to delete user %s from user list' % (data))
                region_key = 'floobits-highlight-%s' % (data['user_id'])
                for window in sublime.windows():
                    for view in window.views():
                        view.erase_regions(region_key)
            elif name == 'highlight':
                region_key = 'floobits-highlight-%s' % (data['user_id'])
                Listener.highlight(data['id'], region_key, data['username'],
                                   data['ranges'], data.get('ping', False))
            elif name == 'error':
                message = 'Floobits: Error! Message: %s' % str(data.get('msg'))
                msg.error(message)
            elif name == 'disconnect':
                message = 'Floobits: Disconnected! Reason: %s' % str(
                    data.get('reason'))
                msg.error(message)
                sublime.error_message(message)
                self.stop()
            elif name == 'msg':
                self.on_msg(data)
            else:
                msg.debug('unknown name!', name, 'data:', data)
            self.buf = after
    def handler(self, name, data):
        if name == "patch":
            Listener.apply_patch(data)
        elif name == "get_buf":
            buf_id = data["id"]
            buf = listener.BUFS.get(buf_id)
            if not buf:
                return msg.warn("no buf found: %s.  Hopefully you didn't need that" % data)
            timeout_id = buf.get("timeout_id")
            if timeout_id:
                utils.cancel_timeout(timeout_id)

            if data["encoding"] == "base64":
                data["buf"] = base64.b64decode(data["buf"])
            # forced_patch doesn't exist in data, so this is equivalent to buf['forced_patch'] = False
            listener.BUFS[buf_id] = data
            view = listener.get_view(buf_id)
            if view:
                Listener.update_view(data, view)
            else:
                listener.save_buf(data)
        elif name == "create_buf":
            if data["encoding"] == "base64":
                data["buf"] = base64.b64decode(data["buf"])
            listener.BUFS[data["id"]] = data
            listener.PATHS_TO_IDS[data["path"]] = data["id"]
            listener.save_buf(data)
            cb = listener.CREATE_BUF_CBS.get(data["path"])
            if cb:
                del listener.CREATE_BUF_CBS[data["path"]]
                try:
                    cb(data["id"])
                except Exception as e:
                    print(e)
        elif name == "rename_buf":
            del listener.PATHS_TO_IDS[data["old_path"]]
            listener.PATHS_TO_IDS[data["path"]] = data["id"]
            new = utils.get_full_path(data["path"])
            old = utils.get_full_path(data["old_path"])
            new_dir = os.path.split(new)[0]
            if new_dir:
                utils.mkdir(new_dir)
            os.rename(old, new)
            view = listener.get_view(data["id"])
            if view:
                view.retarget(new)
            listener.BUFS[data["id"]]["path"] = data["path"]
        elif name == "delete_buf":
            path = utils.get_full_path(data["path"])
            listener.delete_buf(data["id"])
            try:
                utils.rm(path)
            except Exception:
                pass
            user_id = data.get("user_id")
            username = self.get_username_by_id(user_id)
            msg.log("%s deleted %s" % (username, path))
        elif name == "room_info":
            Listener.reset()
            G.JOINED_WORKSPACE = True
            # Success! Reset counter
            self.reconnect_delay = self.INITIAL_RECONNECT_DELAY
            self.retries = self.MAX_RETRIES

            self.workspace_info = data
            G.PERMS = data["perms"]

            if "patch" not in data["perms"]:
                msg.log("No patch permission. Setting buffers to read-only")
                if sublime.ok_cancel_dialog(
                    "You don't have permission to edit this workspace. All files will be read-only.\n\nDo you want to request edit permission?"
                ):
                    self.put({"name": "request_perms", "perms": ["edit_room"]})

            project_json = {"folders": [{"path": G.PROJECT_PATH}]}

            utils.mkdir(G.PROJECT_PATH)
            with open(os.path.join(G.PROJECT_PATH, ".sublime-project"), "wb") as project_fd:
                project_fd.write(json.dumps(project_json, indent=4, sort_keys=True).encode("utf-8"))

            floo_json = {
                "url": utils.to_workspace_url(
                    {
                        "host": self.host,
                        "owner": self.owner,
                        "port": self.port,
                        "workspace": self.workspace,
                        "secure": self.secure,
                    }
                )
            }
            with open(os.path.join(G.PROJECT_PATH, ".floo"), "w") as floo_fd:
                floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

            for buf_id, buf in data["bufs"].items():
                buf_id = int(buf_id)  # json keys must be strings
                buf_path = utils.get_full_path(buf["path"])
                new_dir = os.path.dirname(buf_path)
                utils.mkdir(new_dir)
                listener.BUFS[buf_id] = buf
                listener.PATHS_TO_IDS[buf["path"]] = buf_id
                # TODO: stupidly inefficient
                view = listener.get_view(buf_id)
                if view and not view.is_loading() and buf["encoding"] == "utf8":
                    view_text = listener.get_text(view)
                    view_md5 = hashlib.md5(view_text.encode("utf-8")).hexdigest()
                    if view_md5 == buf["md5"]:
                        msg.debug("md5 sum matches view. not getting buffer %s" % buf["path"])
                        buf["buf"] = view_text
                        G.VIEW_TO_HASH[view.buffer_id()] = view_md5
                    elif self.get_bufs:
                        Listener.get_buf(buf_id)
                    # TODO: maybe send patch here?
                else:
                    try:
                        buf_fd = open(buf_path, "rb")
                        buf_buf = buf_fd.read()
                        md5 = hashlib.md5(buf_buf).hexdigest()
                        if md5 == buf["md5"]:
                            msg.debug("md5 sum matches. not getting buffer %s" % buf["path"])
                            if buf["encoding"] == "utf8":
                                buf_buf = buf_buf.decode("utf-8")
                            buf["buf"] = buf_buf
                        elif self.get_bufs:
                            Listener.get_buf(buf_id)
                    except Exception as e:
                        msg.debug("Error calculating md5:", e)
                        Listener.get_buf(buf_id)

            msg.log("Successfully joined workspace %s/%s" % (self.owner, self.workspace))

            temp_data = data.get("temp_data", {})
            hangout = temp_data.get("hangout", {})
            hangout_url = hangout.get("url")
            if hangout_url:
                self.prompt_join_hangout(hangout_url)

            if self.on_room_info:
                self.on_room_info()
                self.on_room_info = None
        elif name == "user_info":
            user_id = str(data["user_id"])
            user_info = data["user_info"]
            self.workspace_info["users"][user_id] = user_info
            if user_id == str(self.workspace_info["user_id"]):
                G.PERMS = user_info["perms"]
        elif name == "join":
            msg.log("%s joined the workspace" % data["username"])
            user_id = str(data["user_id"])
            self.workspace_info["users"][user_id] = data
        elif name == "part":
            msg.log("%s left the workspace" % data["username"])
            user_id = str(data["user_id"])
            try:
                del self.workspace_info["users"][user_id]
            except Exception as e:
                print("Unable to delete user %s from user list" % (data))
            region_key = "floobits-highlight-%s" % (user_id)
            for window in sublime.windows():
                for view in window.views():
                    view.erase_regions(region_key)
        elif name == "highlight":
            region_key = "floobits-highlight-%s" % (data["user_id"])
            Listener.highlight(data["id"], region_key, data["username"], data["ranges"], data.get("ping", False))
        elif name == "set_temp_data":
            hangout_data = data.get("data", {})
            hangout = hangout_data.get("hangout", {})
            hangout_url = hangout.get("url")
            if hangout_url:
                self.prompt_join_hangout(hangout_url)
        elif name == "saved":
            try:
                buf = listener.BUFS[data["id"]]
                username = self.get_username_by_id(data["user_id"])
                msg.log("%s saved buffer %s" % (username, buf["path"]))
            except Exception as e:
                msg.error(str(e))
        elif name == "request_perms":
            print(data)
            user_id = str(data.get("user_id"))
            username = self.get_username_by_id(user_id)
            if not username:
                return msg.debug("Unknown user for id %s. Not handling request_perms event." % user_id)
            perm_mapping = {"edit_room": "edit", "admin_room": "admin"}
            perms = data.get("perms")
            perms_str = "".join([perm_mapping.get(p) for p in perms])
            prompt = "User %s is requesting %s permission for this room." % (username, perms_str)
            message = data.get("message")
            if message:
                prompt += "\n\n%s says: %s" % (username, message)
            prompt += "\n\nDo you want to grant them permission?"
            confirm = bool(sublime.ok_cancel_dialog(prompt))
            if confirm:
                action = "add"
            else:
                action = "reject"
            self.put({"name": "perms", "action": action, "user_id": user_id, "perms": perms})
        elif name == "perms":
            action = data["action"]
            user_id = str(data["user_id"])
            user = self.workspace_info["users"].get(user_id)
            if user is None:
                msg.log("No user for id %s. Not handling perms event" % user_id)
                return
            perms = set(user["perms"])
            if action == "add":
                perms |= set(data["perms"])
            elif action == "remove":
                perms -= set(data["perms"])
            else:
                return
            user["perms"] = list(perms)
            if user_id == self.workspace_info["user_id"]:
                G.PERMS = perms
        elif name == "msg":
            self.on_msg(data)
        else:
            msg.debug("unknown name!", name, "data:", data)
    def protocol(self, req):
        self.buf += req
        while True:
            before, sep, after = self.buf.partition('\n')
            if not sep:
                break
            try:
                data = json.loads(before)
            except Exception as e:
                print('Unable to parse json:', e)
                print('Data:', before)
                raise e
            name = data.get('name')
            if name == 'patch':
                # TODO: we should do this in a separate thread
                Listener.apply_patch(data)
            elif name == 'get_buf':
                buf_id = data['id']
                listener.BUFS[buf_id] = data
                view = listener.get_view(buf_id)
                if view:
                    Listener.update_view(data, view)
                else:
                    listener.save_buf(data)
            elif name == 'create_buf':
                listener.BUFS[data['id']] = data
                listener.save_buf(data)
            elif name == 'rename_buf':
                new = utils.get_full_path(data['path'])
                old = utils.get_full_path(data['old_path'])
                new_dir = os.path.split(new)[0]
                if new_dir:
                    utils.mkdir(new_dir)
                os.rename(old, new)
                view = listener.get_view(data['id'])
                if view:
                    view.retarget(new)
            elif name == 'delete_buf':
                path = utils.get_full_path(data['path'])
                utils.rm(path)
                listener.delete_buf(data['id'])
            elif name == 'room_info':
                # Success! Reset counter
                self.retries = G.MAX_RETRIES
                self.room_info = data
                G.PERMS = data['perms']

                if 'patch' not in data['perms']:
                    msg.log('We don\'t have patch permission. Setting buffers to read-only')

                project_json = {
                    'folders': [
                        {'path': G.PROJECT_PATH}
                    ]
                }

                utils.mkdir(G.PROJECT_PATH)
                with open(os.path.join(G.PROJECT_PATH, '.sublime-project'), 'w') as project_fd:
                    project_fd.write(json.dumps(project_json, indent=4, sort_keys=True))

                floo_json = {
                    'url': utils.to_room_url({
                        'host': self.host,
                        'owner': self.owner,
                        'port': self.port,
                        'room': self.room,
                        'secure': self.secure,
                    })
                }
                with open(os.path.join(G.PROJECT_PATH, '.floo'), 'w') as floo_fd:
                    floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

                for buf_id, buf in data['bufs'].iteritems():
                    buf_id = int(buf_id)  # json keys must be strings
                    buf_path = utils.get_full_path(buf['path'])
                    new_dir = os.path.dirname(buf_path)
                    utils.mkdir(new_dir)
                    listener.BUFS[buf_id] = buf
                    try:
                        buf_fd = open(buf_path, 'r')
                        buf_buf = buf_fd.read().decode('utf-8')
                        md5 = hashlib.md5(buf_buf.encode('utf-8')).hexdigest()
                        if md5 == buf['md5']:
                            msg.debug('md5 sums match. not getting buffer')
                            buf['buf'] = buf_buf
                        else:
                            msg.debug('md5 for %s should be %s but is %s. getting buffer' % (buf['path'], buf['md5'], md5))
                            raise Exception('different md5')
                    except Exception as e:
                        msg.debug('Error calculating md5:', e)
                        Listener.get_buf(buf_id)

                self.authed = True
                G.CONNECTED = True
                msg.log('Successfully joined room %s/%s' % (self.owner, self.room))
                if self.on_connect:
                    self.on_connect(self)
                    self.on_connect = None
            elif name == 'join':
                msg.log('%s joined the room' % data['username'])
                self.room_info['users'][data['user_id']] = data['username']
            elif name == 'part':
                msg.log('%s left the room' % data['username'])
                try:
                    del self.room_info['users'][data['user_id']]
                except Exception as e:
                    print('Unable to delete user %s from user list' % (data))
                region_key = 'floobits-highlight-%s' % (data['user_id'])
                for window in sublime.windows():
                    for view in window.views():
                        view.erase_regions(region_key)
            elif name == 'highlight':
                region_key = 'floobits-highlight-%s' % (data['user_id'])
                Listener.highlight(data['id'], region_key, data['username'], data['ranges'], data.get('ping', False))
            elif name == 'error':
                message = 'Floobits: Error! Message: %s' % str(data.get('msg'))
                msg.error(message)
            elif name == 'disconnect':
                message = 'Floobits: Disconnected! Reason: %s' % str(data.get('reason'))
                msg.error(message)
                sublime.error_message(message)
                self.stop()
            elif name == 'msg':
                self.on_msg(data)
            else:
                msg.error('unknown name!', name, 'data:', data)
            self.buf = after
    def handler(self, name, data):
        if name == 'patch':
            Listener.apply_patch(data)
        elif name == 'get_buf':
            buf_id = data['id']
            buf = listener.BUFS.get(buf_id)
            if not buf:
                return msg.warn(
                    'no buf found: %s.  Hopefully you didn\'t need that' %
                    data)
            timeout_id = buf.get('timeout_id')
            if timeout_id:
                utils.cancel_timeout(timeout_id)

            if data['encoding'] == 'base64':
                data['buf'] = base64.b64decode(data['buf'])
            # forced_patch doesn't exist in data, so this is equivalent to buf['forced_patch'] = False
            listener.BUFS[buf_id] = data
            view = listener.get_view(buf_id)
            if view:
                Listener.update_view(data, view)
            else:
                listener.save_buf(data)
        elif name == 'create_buf':
            if data['encoding'] == 'base64':
                data['buf'] = base64.b64decode(data['buf'])
            listener.BUFS[data['id']] = data
            listener.PATHS_TO_IDS[data['path']] = data['id']
            listener.save_buf(data)
            cb = listener.CREATE_BUF_CBS.get(data['path'])
            if cb:
                del listener.CREATE_BUF_CBS[data['path']]
                try:
                    cb(data['id'])
                except Exception as e:
                    print(e)
        elif name == 'rename_buf':
            del listener.PATHS_TO_IDS[data['old_path']]
            listener.PATHS_TO_IDS[data['path']] = data['id']
            new = utils.get_full_path(data['path'])
            old = utils.get_full_path(data['old_path'])
            new_dir = os.path.split(new)[0]
            if new_dir:
                utils.mkdir(new_dir)
            os.rename(old, new)
            view = listener.get_view(data['id'])
            if view:
                view.retarget(new)
            listener.BUFS[data['id']]['path'] = data['path']
        elif name == 'delete_buf':
            path = utils.get_full_path(data['path'])
            listener.delete_buf(data['id'])
            try:
                utils.rm(path)
            except Exception:
                pass
            user_id = data.get('user_id')
            username = self.get_username_by_id(user_id)
            msg.log('%s deleted %s' % (username, path))
        elif name == 'room_info':
            Listener.reset()
            G.JOINED_WORKSPACE = True
            # Success! Reset counter
            self.reconnect_delay = self.INITIAL_RECONNECT_DELAY
            self.retries = self.MAX_RETRIES

            self.workspace_info = data
            G.PERMS = data['perms']

            if 'patch' not in data['perms']:
                msg.log('No patch permission. Setting buffers to read-only')
                if sublime.ok_cancel_dialog(
                        'You don\'t have permission to edit this workspace. All files will be read-only.\n\nDo you want to request edit permission?'
                ):
                    self.put({'name': 'request_perms', 'perms': ['edit_room']})

            project_json = {'folders': [{'path': G.PROJECT_PATH}]}

            utils.mkdir(G.PROJECT_PATH)
            with open(os.path.join(G.PROJECT_PATH, '.sublime-project'),
                      'wb') as project_fd:
                project_fd.write(
                    json.dumps(project_json, indent=4,
                               sort_keys=True).encode('utf-8'))

            floo_json = {
                'url':
                utils.to_workspace_url({
                    'host': self.host,
                    'owner': self.owner,
                    'port': self.port,
                    'workspace': self.workspace,
                    'secure': self.secure,
                })
            }
            with open(os.path.join(G.PROJECT_PATH, '.floo'), 'w') as floo_fd:
                floo_fd.write(json.dumps(floo_json, indent=4, sort_keys=True))

            for buf_id, buf in data['bufs'].items():
                buf_id = int(buf_id)  # json keys must be strings
                buf_path = utils.get_full_path(buf['path'])
                new_dir = os.path.dirname(buf_path)
                utils.mkdir(new_dir)
                listener.BUFS[buf_id] = buf
                listener.PATHS_TO_IDS[buf['path']] = buf_id
                # TODO: stupidly inefficient
                view = listener.get_view(buf_id)
                if view and not view.is_loading(
                ) and buf['encoding'] == 'utf8':
                    view_text = listener.get_text(view)
                    view_md5 = hashlib.md5(
                        view_text.encode('utf-8')).hexdigest()
                    if view_md5 == buf['md5']:
                        msg.debug(
                            'md5 sum matches view. not getting buffer %s' %
                            buf['path'])
                        buf['buf'] = view_text
                        G.VIEW_TO_HASH[view.buffer_id()] = view_md5
                    elif self.get_bufs:
                        Listener.get_buf(buf_id)
                    #TODO: maybe send patch here?
                else:
                    try:
                        buf_fd = open(buf_path, 'rb')
                        buf_buf = buf_fd.read()
                        md5 = hashlib.md5(buf_buf).hexdigest()
                        if md5 == buf['md5']:
                            msg.debug(
                                'md5 sum matches. not getting buffer %s' %
                                buf['path'])
                            if buf['encoding'] == 'utf8':
                                buf_buf = buf_buf.decode('utf-8')
                            buf['buf'] = buf_buf
                        elif self.get_bufs:
                            Listener.get_buf(buf_id)
                    except Exception as e:
                        msg.debug('Error calculating md5:', e)
                        Listener.get_buf(buf_id)

            msg.log('Successfully joined workspace %s/%s' %
                    (self.owner, self.workspace))

            temp_data = data.get('temp_data', {})
            hangout = temp_data.get('hangout', {})
            hangout_url = hangout.get('url')
            if hangout_url:
                self.prompt_join_hangout(hangout_url)

            if self.on_room_info:
                self.on_room_info()
                self.on_room_info = None
        elif name == 'user_info':
            user_id = str(data['user_id'])
            user_info = data['user_info']
            self.workspace_info['users'][user_id] = user_info
            if user_id == str(self.workspace_info['user_id']):
                G.PERMS = user_info['perms']
        elif name == 'join':
            msg.log('%s joined the workspace' % data['username'])
            user_id = str(data['user_id'])
            self.workspace_info['users'][user_id] = data
        elif name == 'part':
            msg.log('%s left the workspace' % data['username'])
            user_id = str(data['user_id'])
            try:
                del self.workspace_info['users'][user_id]
            except Exception as e:
                print('Unable to delete user %s from user list' % (data))
            region_key = 'floobits-highlight-%s' % (user_id)
            for window in sublime.windows():
                for view in window.views():
                    view.erase_regions(region_key)
        elif name == 'highlight':
            region_key = 'floobits-highlight-%s' % (data['user_id'])
            Listener.highlight(data['id'], region_key, data['username'],
                               data['ranges'], data.get('ping', False))
        elif name == 'set_temp_data':
            hangout_data = data.get('data', {})
            hangout = hangout_data.get('hangout', {})
            hangout_url = hangout.get('url')
            if hangout_url:
                self.prompt_join_hangout(hangout_url)
        elif name == 'saved':
            try:
                buf = listener.BUFS[data['id']]
                username = self.get_username_by_id(data['user_id'])
                msg.log('%s saved buffer %s' % (username, buf['path']))
            except Exception as e:
                msg.error(str(e))
        elif name == 'request_perms':
            print(data)
            user_id = str(data.get('user_id'))
            username = self.get_username_by_id(user_id)
            if not username:
                return msg.debug(
                    'Unknown user for id %s. Not handling request_perms event.'
                    % user_id)
            perm_mapping = {
                'edit_room': 'edit',
                'admin_room': 'admin',
            }
            perms = data.get('perms')
            perms_str = ''.join([perm_mapping.get(p) for p in perms])
            prompt = 'User %s is requesting %s permission for this room.' % (
                username, perms_str)
            message = data.get('message')
            if message:
                prompt += '\n\n%s says: %s' % (username, message)
            prompt += '\n\nDo you want to grant them permission?'
            confirm = bool(sublime.ok_cancel_dialog(prompt))
            if confirm:
                action = 'add'
            else:
                action = 'reject'
            self.put({
                'name': 'perms',
                'action': action,
                'user_id': user_id,
                'perms': perms
            })
        elif name == 'perms':
            action = data['action']
            user_id = str(data['user_id'])
            user = self.workspace_info['users'].get(user_id)
            if user is None:
                msg.log('No user for id %s. Not handling perms event' %
                        user_id)
                return
            perms = set(user['perms'])
            if action == 'add':
                perms |= set(data['perms'])
            elif action == 'remove':
                perms -= set(data['perms'])
            else:
                return
            user['perms'] = list(perms)
            if user_id == self.workspace_info['user_id']:
                G.PERMS = perms
        elif name == 'msg':
            self.on_msg(data)
        else:
            msg.debug('unknown name!', name, 'data:', data)