def stop(self):
     self.retries = -1
     utils.cancel_timeout(self.reconnect_timeout)
     self.reconnect_timeout = None
     self.cleanup()
     msg.log('Disconnected.')
     sublime.status_message('Disconnected.')
Example #2
0
 def disable_stalker_mode(self, timeout, agent):
     if G.STALKER_MODE is True:
         agent.temp_disable_stalk = True
         self.disable_stalker_mode_timeout = utils.set_timeout(self.reenable_stalker_mode, timeout)
     elif self.disable_stalker_mode_timeout:
         utils.cancel_timeout(self.disable_stalker_mode_timeout)
         self.disable_stalker_mode_timeout = utils.set_timeout(self.reenable_stalker_mode, timeout)
 def stop(self):
     self.retries = -1
     utils.cancel_timeout(self.reconnect_timeout)
     self.reconnect_timeout = None
     self.cleanup()
     msg.log('Disconnected.')
     sublime.status_message('Disconnected.')
Example #4
0
def disable_stalker_mode(timeout):
    global disable_stalker_mode_timeout, temp_disable_stalk
    if G.STALKER_MODE is True:
        temp_disable_stalk = True
        disable_stalker_mode_timeout = utils.set_timeout(reenable_stalker_mode, timeout)
    elif disable_stalker_mode_timeout:
        utils.cancel_timeout(disable_stalker_mode_timeout)
        disable_stalker_mode_timeout = utils.set_timeout(reenable_stalker_mode, timeout)
Example #5
0
def disable_stalker_mode(timeout):
    global disable_stalker_mode_timeout, temp_disable_stalk
    if G.STALKER_MODE is True:
        temp_disable_stalk = True
        disable_stalker_mode_timeout = utils.set_timeout(
            reenable_stalker_mode, timeout)
    elif disable_stalker_mode_timeout:
        utils.cancel_timeout(disable_stalker_mode_timeout)
        disable_stalker_mode_timeout = utils.set_timeout(
            reenable_stalker_mode, timeout)
Example #6
0
 def stop(self, log=True):
     if log:
         msg.log('Disconnecting from workspace %s/%s' % (self.owner, self.workspace))
     utils.cancel_timeout(self.reconnect_timeout)
     self.reconnect_timeout = None
     try:
         self.retries = -1
         self.sock.shutdown(2)
         self.sock.close()
     except Exception:
         return False
     if log:
         msg.log('Disconnected.')
     return True
Example #7
0
 def stop(self, log=True):
     if log:
         msg.log('Disconnecting from workspace %s/%s' %
                 (self.owner, self.workspace))
     utils.cancel_timeout(self.reconnect_timeout)
     self.reconnect_timeout = None
     try:
         self.retries = -1
         self.sock.shutdown(2)
         self.sock.close()
     except Exception:
         return False
     if log:
         msg.log('Disconnected.')
     return True
Example #8
0
    def apply_patches(self, buf, patches, username):
        regions = []
        commands = []
        for patch in patches[2]:
            offset = patch[0]
            length = patch[1]
            patch_text = patch[2]
            region = sublime.Region(offset, offset + length)
            regions.append(region)
            commands.append({'r': [offset, offset + length], 'data': patch_text})

        self.view.run_command('floo_view_replace_regions', {'commands': commands})
        region_key = 'floobits-patch-' + username
        self.view.add_regions(region_key, regions, 'floobits.patch', 'circle', sublime.DRAW_OUTLINED)
        utils.cancel_timeout(self.erase_regions_timeout)
        self.erase_regions_timeout = utils.set_timeout(self.view.erase_regions, 2000, region_key)
Example #9
0
    def apply_patches(self, buf, patches, username):
        regions = []
        commands = []
        for patch in patches[2]:
            offset = patch[0]
            length = patch[1]
            patch_text = patch[2]
            region = sublime.Region(offset, offset + length)
            regions.append(region)
            commands.append({"r": [offset, offset + length], "data": patch_text})

        self.view.run_command("floo_view_replace_regions", {"commands": commands})
        region_key = "floobits-patch-" + username
        self.view.add_regions(region_key, regions, "floobits.patch", "circle", sublime.DRAW_OUTLINED)
        utils.cancel_timeout(self.erase_regions_timeout)
        self.erase_regions_timeout = utils.set_timeout(self.view.erase_regions, 2000, region_key)
    def connect(self):
        utils.cancel_timeout(self.reconnect_timeout)
        self.reconnect_timeout = None
        self.cleanup()

        self.empty_selects = 0

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setblocking(False)
        if self.secure:
            if ssl:
                with open(self.cert_path, 'wb') as cert_fd:
                    cert_fd.write(cert.CA_CERT.encode('utf-8'))
            else:
                msg.log('No SSL module found. Connection will not be encrypted.')
                self.secure = False
                if self.port == G.DEFAULT_PORT:
                    self.port = 3148  # plaintext port
        msg.log('Connecting to %s:%s' % (self.host, self.port))
        self._connect()
Example #11
0
    def connect(self):
        utils.cancel_timeout(self.reconnect_timeout)
        self.reconnect_timeout = None
        self.cleanup()

        self.empty_selects = 0

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setblocking(False)
        if self.secure:
            if ssl:
                with open(self.cert_path, 'wb') as cert_fd:
                    cert_fd.write(cert.CA_CERT.encode('utf-8'))
            else:
                msg.log(
                    'No SSL module found. Connection will not be encrypted.')
                self.secure = False
                if self.port == G.DEFAULT_PORT:
                    self.port = 3148  # plaintext port
        msg.log('Connecting to %s:%s' % (self.host, self.port))
        self._connect()
    def connect(self):
        utils.cancel_timeout(self.reconnect_timeout)
        self.reconnect_timeout = None
        self.cleanup()

        self.empty_selects = 0

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setblocking(False)
        if self.secure:
            if ssl:
                with open(self.cert_path, "wb") as cert_fd:
                    cert_fd.write(cert.CA_CERT.encode("utf-8"))
            else:
                msg.log("No SSL module found. Connection will not be encrypted.")
                self.secure = False
                if self.port == G.DEFAULT_PORT:
                    self.port = 3148  # plaintext port
        conn_msg = "Connecting to %s:%s" % (self.host, self.port)
        msg.log(conn_msg)
        sublime.status_message(conn_msg)
        self._connect()
    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)
Example #15
0
    def apply_patch(patch_data):
        if not G.AGENT:
            msg.debug('Not connected. Discarding view change.')
            return
        buf_id = patch_data['id']
        buf = BUFS[buf_id]
        if 'buf' not in buf:
            msg.debug('buf %s not populated yet. not patching' % buf['path'])
            return
        if buf['encoding'] == 'base64':
            # TODO apply binary patches
            return Listener.get_buf(buf_id, None)

        view = get_view(buf_id)
        if len(patch_data['patch']) == 0:
            msg.error('wtf? no patches to apply. server is being stupid')
            return
        msg.debug('patch is', patch_data['patch'])
        dmp_patches = DMP.patch_fromText(patch_data['patch'])
        # TODO: run this in a separate thread
        old_text = buf['buf']

        if view and not view.is_loading():
            view_text = get_text(view)
            if old_text == view_text:
                buf['forced_patch'] = False
            elif not buf.get('forced_patch'):
                patch = utils.FlooPatch(get_text(view), buf)
                # Update the current copy of the buffer
                buf['buf'] = patch.current
                buf['md5'] = hashlib.md5(patch.current.encode('utf-8')).hexdigest()
                buf['forced_patch'] = True
                msg.debug('forcing patch for %s' % buf['path'])
                G.AGENT.put(patch.to_json())
                old_text = view_text
            else:
                msg.debug('forced patch is true. not sending another patch for buf %s' % buf['path'])
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != patch_data['md5_before']:
            msg.warn('starting md5s don\'t match for %s. this is dangerous!' % buf['path'])

        t = DMP.patch_apply(dmp_patches, old_text)

        clean_patch = True
        for applied_patch in t[1]:
            if not applied_patch:
                clean_patch = False
                break

        if G.DEBUG:
            if len(t[0]) == 0:
                try:
                    msg.debug('OMG EMPTY!')
                    msg.debug('Starting data:', buf['buf'])
                    msg.debug('Patch:', patch_data['patch'])
                except Exception as e:
                    print(e)

            if '\x01' in t[0]:
                msg.debug('FOUND CRAZY BYTE IN BUFFER')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', patch_data['patch'])

        timeout_id = buf.get('timeout_id')
        if timeout_id:
            utils.cancel_timeout(timeout_id)

        if not clean_patch:
            msg.log('Couldn\'t patch %s cleanly.' % buf['path'])
            return Listener.get_buf(buf_id, view)

        cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest()
        if cur_hash != patch_data['md5_after']:
            buf['timeout_id'] = utils.set_timeout(Listener.get_buf, 2000, buf_id, view)

        buf['buf'] = t[0]
        buf['md5'] = cur_hash

        if not view:
            save_buf(buf)
            return

        regions = []
        commands = []
        for patch in t[2]:
            offset = patch[0]
            length = patch[1]
            patch_text = patch[2]
            region = sublime.Region(offset, offset + length)
            regions.append(region)
            commands.append({'r': [offset, offset + length], 'data': patch_text})

        view.run_command('floo_view_replace_regions', {'commands': commands})
        region_key = 'floobits-patch-' + patch_data['username']
        view.add_regions(region_key, regions, 'floobits.patch', 'circle', sublime.DRAW_OUTLINED)
        utils.set_timeout(view.erase_regions, 2000, region_key)

        view.set_status('Floobits', 'Changed by %s at %s' % (patch_data['username'], datetime.now().strftime('%H:%M')))
Example #16
0
    def apply_patch(patch_data):
        if not G.AGENT:
            msg.debug('Not connected. Discarding view change.')
            return
        buf_id = patch_data['id']
        buf = BUFS[buf_id]
        if 'buf' not in buf:
            msg.debug('buf %s not populated yet. not patching' % buf['path'])
            return
        if buf['encoding'] == 'base64':
            # TODO apply binary patches
            return Listener.get_buf(buf_id, None)

        view = get_view(buf_id)
        if len(patch_data['patch']) == 0:
            msg.error('wtf? no patches to apply. server is being stupid')
            return
        msg.debug('patch is', patch_data['patch'])
        dmp_patches = DMP.patch_fromText(patch_data['patch'])
        # TODO: run this in a separate thread
        old_text = buf['buf']

        if view and not view.is_loading():
            view_text = get_text(view)
            if old_text == view_text:
                buf['forced_patch'] = False
            elif not buf.get('forced_patch'):
                patch = utils.FlooPatch(get_text(view), buf)
                # Update the current copy of the buffer
                buf['buf'] = patch.current
                buf['md5'] = hashlib.md5(
                    patch.current.encode('utf-8')).hexdigest()
                buf['forced_patch'] = True
                msg.debug('forcing patch for %s' % buf['path'])
                G.AGENT.put(patch.to_json())
                old_text = view_text
            else:
                msg.debug(
                    'forced patch is true. not sending another patch for buf %s'
                    % buf['path'])
        md5_before = hashlib.md5(old_text.encode('utf-8')).hexdigest()
        if md5_before != patch_data['md5_before']:
            msg.warn('starting md5s don\'t match for %s. this is dangerous!' %
                     buf['path'])

        t = DMP.patch_apply(dmp_patches, old_text)

        clean_patch = True
        for applied_patch in t[1]:
            if not applied_patch:
                clean_patch = False
                break

        if G.DEBUG:
            if len(t[0]) == 0:
                try:
                    msg.debug('OMG EMPTY!')
                    msg.debug('Starting data:', buf['buf'])
                    msg.debug('Patch:', patch_data['patch'])
                except Exception as e:
                    print(e)

            if '\x01' in t[0]:
                msg.debug('FOUND CRAZY BYTE IN BUFFER')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', patch_data['patch'])

        timeout_id = buf.get('timeout_id')
        if timeout_id:
            utils.cancel_timeout(timeout_id)

        if not clean_patch:
            msg.log('Couldn\'t patch %s cleanly.' % buf['path'])
            return Listener.get_buf(buf_id, view)

        cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest()
        if cur_hash != patch_data['md5_after']:
            buf['timeout_id'] = utils.set_timeout(Listener.get_buf, 2000,
                                                  buf_id, view)

        buf['buf'] = t[0]
        buf['md5'] = cur_hash

        if not view:
            save_buf(buf)
            return

        regions = []
        commands = []
        for patch in t[2]:
            offset = patch[0]
            length = patch[1]
            patch_text = patch[2]
            region = sublime.Region(offset, offset + length)
            regions.append(region)
            commands.append({
                'r': [offset, offset + length],
                'data': patch_text
            })

        view.run_command('floo_view_replace_regions', {'commands': commands})
        region_key = 'floobits-patch-' + patch_data['username']
        view.add_regions(region_key, regions, 'floobits.patch', 'circle',
                         sublime.DRAW_OUTLINED)
        utils.set_timeout(view.erase_regions, 2000, region_key)

        view.set_status(
            'Floobits', 'Changed by %s at %s' %
            (patch_data['username'], datetime.now().strftime('%H:%M')))
Example #17
0
 def disable_follow_mode(self, timeout, agent):
     if G.FOLLOW_MODE is True:
         agent.temp_disable_follow = True
     utils.cancel_timeout(self.disable_follow_mode_timeout)
     self.disable_follow_mode_timeout = utils.set_timeout(self.reenable_follow_mode, timeout)
    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)