Exemple #1
0
    def _on_create_workspace(self,
                             data,
                             workspace_name,
                             dir_to_share,
                             owner=None,
                             perms=None):
        owner = owner or G.USERNAME
        workspace_name = data.get('response', workspace_name)
        prompt = 'workspace %s already exists. Choose another name: ' % workspace_name
        try:
            api_args = {
                'name': workspace_name,
                'owner': owner,
            }
            if perms:
                api_args['perms'] = perms
            api.create_workspace(api_args)
            workspace_url = utils.to_workspace_url({
                'secure': True,
                'owner': owner,
                'workspace': workspace_name
            })
            msg.debug('Created workspace %s' % workspace_url)
        except HTTPError as e:
            err_body = e.read()
            msg.error('Unable to create workspace: %s %s' %
                      (unicode(e), err_body))
            if e.code not in [400, 402, 409]:
                return msg.error('Unable to create workspace: %s' % str(e))
            if e.code == 400:
                workspace_name = re.sub('[^A-Za-z0-9_\-]', '-', workspace_name)
                prompt = 'Invalid name. Workspace names must match the regex [A-Za-z0-9_\-]. Choose another name:'
            elif e.code == 402:
                try:
                    err_body = json.loads(err_body)
                    err_body = err_body['detail']
                except Exception:
                    pass
                return editor.error_message('%s' % err_body)
            else:
                prompt = 'Workspace %s/%s already exists. Choose another name:' % (
                    owner, workspace_name)

            return self.get_input(prompt, workspace_name,
                                  self._on_create_workspace, workspace_name,
                                  dir_to_share, owner, perms)
        except Exception as e:
            return msg.error('Unable to create workspace: %s' % str(e))

        G.PROJECT_PATH = dir_to_share
        agent = self.remote_connect(owner, workspace_name, False)
        agent.once("room_info", lambda: agent.upload(dir_to_share))
Exemple #2
0
    def on_room_info(self, data):
        # Success! Reset counter
        self.workspace_info = data
        self.perms = data['perms']

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

        utils.mkdir(G.PROJECT_PATH)

        floo_json = {
            'url': utils.to_workspace_url({
                'host': self.agent.host,
                'owner': self.agent.owner,
                'port': self.agent.port,
                'workspace': self.agent.workspace,
                'secure': self.agent.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)
            self.FLOO_BUFS[buf_id] = buf
            self.FLOO_PATHS_TO_BUFS[buf_path] = buf_id
            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:
                    raise Exception('different md5')
            except Exception:
                try:
                    open(buf_path, "a").close()
                except Exception as e:
                    msg.debug("couldn't touch file: %s becuase %s" % (buf_path, e))
                self.agent.send_get_buf(buf_id)

        msg.debug(G.PROJECT_PATH)

        self.agent.on_auth()
Exemple #3
0
    def on_room_info(self, data):
        # Success! Reset counter
        self.workspace_info = data
        self.perms = data['perms']

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

        utils.mkdir(G.PROJECT_PATH)

        floo_json = {
            'url': utils.to_workspace_url({
                'host': self.agent.host,
                'owner': self.agent.owner,
                'port': self.agent.port,
                'workspace': self.agent.workspace,
                'secure': self.agent.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)
            self.FLOO_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:
                    raise Exception('different md5')
            except Exception:
                try:
                    open(buf_path, "a").close()
                except Exception as e:
                    msg.debug("couldn't touch file: %s becuase %s" % (buf_path, e))
                self.agent.send_get_buf(buf_id)

        msg.debug(G.PROJECT_PATH)

        self.agent.on_auth()
Exemple #4
0
    def _on_create_workspace(self, data, workspace_name, dir_to_share, owner=None, perms=None):
        owner = owner or G.USERNAME
        workspace_name = data.get("response", workspace_name)
        prompt = "workspace %s already exists. Choose another name: " % workspace_name
        try:
            api_args = {"name": workspace_name, "owner": owner}
            if perms:
                api_args["perms"] = perms
            api.create_workspace(api_args)
            workspace_url = utils.to_workspace_url({"secure": True, "owner": owner, "workspace": workspace_name})
            msg.debug("Created workspace %s" % workspace_url)
        except HTTPError as e:
            err_body = e.read()
            msg.error("Unable to create workspace: %s %s" % (unicode(e), err_body))
            if e.code not in [400, 402, 409]:
                return msg.error("Unable to create workspace: %s" % str(e))
            if e.code == 400:
                workspace_name = re.sub("[^A-Za-z0-9_\-]", "-", workspace_name)
                prompt = "Invalid name. Workspace names must match the regex [A-Za-z0-9_\-]. Choose another name:"
            elif e.code == 402:
                try:
                    err_body = json.loads(err_body)
                    err_body = err_body["detail"]
                except Exception:
                    pass
                return editor.error_message("%s" % err_body)
            else:
                prompt = "Workspace %s/%s already exists. Choose another name:" % (owner, workspace_name)

            return self.get_input(
                prompt, workspace_name, self._on_create_workspace, workspace_name, dir_to_share, owner, perms
            )
        except Exception as e:
            return msg.error("Unable to create workspace: %s" % str(e))

        G.PROJECT_PATH = dir_to_share
        agent = self.remote_connect(owner, workspace_name, False)
        agent.once("room_info", lambda: agent.upload(dir_to_share))
    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 on_room_info(self, data):
        # Success! Reset counter
        self.workspace_info = data
        self.perms = data['perms']

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

        utils.mkdir(G.PROJECT_PATH)
        msg.debug('mkdir -p for project path %s' % G.PROJECT_PATH)

        floo_json = {
            'url': utils.to_workspace_url({
                'host': self.agent.host,
                'owner': self.agent.owner,
                'port': self.agent.port,
                'workspace': self.agent.workspace,
                'secure': self.agent.secure,
            })
        }
        utils.update_floo_file(os.path.join(G.PROJECT_PATH, '.floo'), floo_json)

        G.JOINED_WORKSPACE = True

        bufs_to_get = []
        new_dirs = set()
        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)
            if new_dir not in new_dirs:
                utils.mkdir(new_dir)
                new_dirs.add(new_dir)
            self.FLOO_BUFS[buf_id] = buf
            self.FLOO_PATHS_TO_BUFS[buf['path']] = buf_id

            try:
                buf_fd = open(buf_path, 'rb')
            except Exception as e:
                msg.debug("Couldn't read %s: %s" % (buf_path, e))
                bufs_to_get.append(buf_id)
                continue
            else:
                buf_buf = buf_fd.read()
            md5 = hashlib.md5(buf_buf).hexdigest()
            if md5 == buf['md5']:
                msg.debug('md5 sums match- not fetching buffer %s' % buf_path)
                if buf['encoding'] == 'utf8':
                    buf['buf'] = buf_buf.decode('utf-8')
                continue
            bufs_to_get.append(buf_id)

        def finish_room_info():
            success_msg = 'Successfully joined workspace %s/%s' % (self.agent.owner, self.agent.workspace)
            msg.log(success_msg)
            self.agent.on_auth()

        if bufs_to_get and self.agent.get_bufs:
            if len(bufs_to_get) > 4:
                prompt = '%s local files are different from the workspace. Overwrite your local files?' % len(bufs_to_get)
            else:
                prompt = 'Overwrite the following local files: %s ? ' % " ".join([self.FLOO_BUFS[buf_id]['path']
                         for buf_id in bufs_to_get])

            def stomp_local(data):
                d = data['response']
                for buf_id in bufs_to_get:
                    if d:
                        self.agent.send_get_buf(buf_id)
                    else:
                        buf = self.FLOO_BUFS[buf_id]
                        # TODO: this is inefficient. we just read the file 20 lines ago
                        self.create_buf(utils.get_full_path(buf['path']))
                finish_room_info()
            self.agent.conn.get_input(prompt, '', stomp_local, y_or_n=True)
        else:
            finish_room_info()
    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 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)