Beispiel #1
0
 def connect(self):
     self.empty_selects = 0
     self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     if self.secure:
         if ssl:
             cert_path = os.path.join(G.COLAB_DIR, 'startssl-ca.pem')
             with open(cert_path, 'wb') as cert_fd:
                 cert_fd.write(cert.CA_CERT.encode('utf-8'))
             self.sock = ssl.wrap_socket(self.sock,
                                         ca_certs=cert_path,
                                         cert_reqs=ssl.CERT_REQUIRED)
         else:
             msg.log(
                 'No SSL module found. Connection will not be encrypted.')
             if self.port == G.DEFAULT_PORT:
                 self.port = 3148  # plaintext port
     msg.log('Connecting to %s:%s' % (self.host, self.port))
     try:
         self.sock.settimeout(30)  # Seconds before timing out connecting
         self.sock.connect((self.host, self.port))
         if self.secure and ssl:
             self.sock.do_handshake()
     except socket.error as e:
         msg.error('Error connecting:', e)
         self.reconnect()
         return
     self.sock.setblocking(False)
     msg.log('Connected!')
     self.reconnect_delay = G.INITIAL_RECONNECT_DELAY
     utils.set_timeout(self.select, 0)
     self.auth()
 def update_view(buf, view=None):
     view = view or get_view(buf['id'])
     visible_region = view.visible_region()
     viewport_position = view.viewport_position()
     # deep copy
     selections = [x for x in view.sel()]
     MODIFIED_EVENTS.put(1)
     try:
         view.run_command('floo_view_replace_region', {
             'r': [0, view.size()],
             'data': buf['buf']
         })
     except Exception as e:
         msg.error('Exception updating view: %s' % e)
     utils.set_timeout(view.set_viewport_position, 0, viewport_position,
                       False)
     view.sel().clear()
     view.show(visible_region, False)
     for sel in selections:
         view.sel().add(sel)
     if 'patch' in G.PERMS:
         view.set_read_only(False)
     else:
         view.set_status(
             'Floobits',
             'You don\'t have write permission. Buffer is read-only.')
         view.set_read_only(True)
 def connect(self):
     self.empty_selects = 0
     self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     if self.secure:
         if ssl:
             cert_path = os.path.join(G.COLAB_DIR, 'startssl-ca.pem')
             with open(cert_path, 'wb') as cert_fd:
                 cert_fd.write(cert.CA_CERT.encode('utf-8'))
             self.sock = ssl.wrap_socket(self.sock, ca_certs=cert_path, cert_reqs=ssl.CERT_REQUIRED)
         else:
             msg.log('No SSL module found. Connection will not be encrypted.')
             if self.port == G.DEFAULT_PORT:
                 self.port = 3148  # plaintext port
     msg.log('Connecting to %s:%s' % (self.host, self.port))
     try:
         self.sock.settimeout(30)  # Seconds before timing out connecting
         self.sock.connect((self.host, self.port))
         if self.secure and ssl:
             self.sock.do_handshake()
     except socket.error as e:
         msg.error('Error connecting:', e)
         self.reconnect()
         return
     self.sock.setblocking(False)
     msg.log('Connected!')
     self.reconnect_delay = G.INITIAL_RECONNECT_DELAY
     utils.set_timeout(self.select, 0)
     self.auth()
    def create_buf(path, always_add=False):
        if not utils.is_shared(path):
            msg.error(
                'Skipping adding %s because it is not in shared path %s.' %
                (path, G.PROJECT_PATH))
            return
        if os.path.isdir(path):
            command = 'git ls-files %s' % path
            try:
                p = subprocess.Popen(command,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE,
                                     shell=True,
                                     cwd=path)
                stdoutdata, stderrdata = p.communicate()
                if p.returncode == 0:
                    for git_path in stdoutdata.split('\n'):
                        git_path = git_path.strip()
                        if not git_path:
                            continue
                        add_path = os.path.join(path, git_path)
                        msg.debug('adding %s' % add_path)
                        utils.set_timeout(Listener.create_buf, 0, add_path)
                    return
            except Exception as e:
                msg.debug("Couldn't run %s. This is probably OK. Error: %s" %
                          (command, str(e)))

            for dirpath, dirnames, filenames in os.walk(path):
                # Don't care about hidden stuff
                dirnames[:] = [d for d in dirnames if d[0] != '.']
                for f in filenames:
                    f_path = os.path.join(dirpath, f)
                    if f[0] == '.':
                        msg.log('Not creating buf for hidden file %s' % f_path)
                    else:
                        utils.set_timeout(Listener.create_buf, 0, f_path)
            return
        try:
            buf_fd = open(path, 'rb')
            buf = buf_fd.read().decode('utf-8')
            rel_path = utils.to_rel_path(path)
            msg.log('creating buffer ', rel_path)
            event = {
                'name': 'create_buf',
                'buf': buf,
                'path': rel_path,
            }
            Listener.agent.put(event)
        except (IOError, OSError):
            msg.error('Failed to open %s.' % path)
        except Exception as e:
            msg.error('Failed to create buffer %s: %s' % (path, str(e)))
Beispiel #5
0
    def select(self):
        if not self.sock:
            msg.error('select(): No socket.')
            return self.reconnect()

        try:
            # this blocks until the socket is readable or writeable
            _in, _out, _except = select.select([self.sock], [self.sock],
                                               [self.sock])
        except (select.error, socket.error, Exception) as e:
            msg.error('Error in select(): %s' % str(e))
            return self.reconnect()

        if _except:
            msg.error('Socket error')
            return self.reconnect()

        if _in:
            buf = ''.encode('utf-8')
            while True:
                try:
                    d = self.sock.recv(4096)
                    if not d:
                        break
                    buf += d
                except (socket.error, TypeError):
                    break

            if buf:
                self.empty_selects = 0
                self.protocol(buf)
            else:
                self.empty_selects += 1
                if self.empty_selects > 10:
                    msg.error('No data from sock.recv() {0} times.'.format(
                        self.empty_selects))
                    return self.reconnect()

        if _out:
            for p in self.get_patches():
                if p is None:
                    SOCKET_Q.task_done()
                    continue
                try:
                    msg.debug('writing patch: %s' % p)
                    self.sock.sendall(p.encode('utf-8'))
                    SOCKET_Q.task_done()
                except Exception as e:
                    msg.error('Couldn\'t write to socket: %s' % str(e))
                    return self.reconnect()

        utils.set_timeout(self.select, 100)
    def push():
        reported = set()
        while Listener.views_changed:
            view, buf = Listener.views_changed.pop()
            if view.is_loading():
                msg.debug(
                    'View for buf %s is not ready. Ignoring change event' %
                    buf['id'])
                continue
            if 'patch' not in G.PERMS:
                continue
            vb_id = view.buffer_id()
            if vb_id in reported:
                continue
            if 'buf' not in buf:
                msg.debug('No data for buf %s %s yet. Skipping sending patch' %
                          (buf['id'], buf['path']))
                continue

            reported.add(vb_id)
            patch = FlooPatch(view, buf)
            # Update the current copy of the buffer
            buf['buf'] = patch.current
            buf['md5'] = hashlib.md5(patch.current.encode('utf-8')).hexdigest()
            if Listener.agent:
                Listener.agent.put(patch.to_json())
            else:
                msg.debug('Not connected. Discarding view change.')

        while Listener.selection_changed:
            view, buf, ping = Listener.selection_changed.pop()
            # consume highlight events to avoid leak
            if 'highlight' not in G.PERMS:
                continue
            vb_id = view.buffer_id()
            if vb_id in reported:
                continue

            reported.add(vb_id)
            sel = view.sel()
            highlight_json = {
                'id': buf['id'],
                'name': 'highlight',
                'ranges': [[x.a, x.b] for x in sel],
                'ping': ping,
            }
            if Listener.agent:
                Listener.agent.put(highlight_json)
            else:
                msg.debug('Not connected. Discarding selection change.')

        utils.set_timeout(Listener.push, 100)
    def select(self):
        if not self.sock:
            msg.error('select(): No socket.')
            return self.reconnect()

        try:
            # this blocks until the socket is readable or writeable
            _in, _out, _except = select.select([self.sock], [self.sock], [self.sock])
        except (select.error, socket.error, Exception) as e:
            msg.error('Error in select(): %s' % str(e))
            return self.reconnect()

        if _except:
            msg.error('Socket error')
            return self.reconnect()

        if _in:
            buf = ''.encode('utf-8')
            while True:
                try:
                    d = self.sock.recv(4096)
                    if not d:
                        break
                    buf += d
                except (socket.error, TypeError):
                    break

            if buf:
                self.empty_selects = 0
                self.protocol(buf)
            else:
                self.empty_selects += 1
                if self.empty_selects > 10:
                    msg.error('No data from sock.recv() {0} times.'.format(self.empty_selects))
                    return self.reconnect()

        if _out:
            for p in self.get_patches():
                if p is None:
                    SOCKET_Q.task_done()
                    continue
                try:
                    msg.debug('writing patch: %s' % p)
                    self.sock.sendall(p.encode('utf-8'))
                    SOCKET_Q.task_done()
                except Exception as e:
                    msg.error('Couldn\'t write to socket: %s' % str(e))
                    return self.reconnect()

        utils.set_timeout(self.select, 100)
    def push():
        reported = set()
        while Listener.views_changed:
            view, buf = Listener.views_changed.pop()
            if view.is_loading():
                msg.debug('View for buf %s is not ready. Ignoring change event' % buf['id'])
                continue
            if 'patch' not in G.PERMS:
                continue
            vb_id = view.buffer_id()
            if vb_id in reported:
                continue
            if 'buf' not in buf:
                msg.debug('No data for buf %s %s yet. Skipping sending patch' % (buf['id'], buf['path']))
                continue

            reported.add(vb_id)
            patch = FlooPatch(view, buf)
            # Update the current copy of the buffer
            buf['buf'] = patch.current
            buf['md5'] = hashlib.md5(patch.current.encode('utf-8')).hexdigest()
            if Listener.agent:
                Listener.agent.put(patch.to_json())
            else:
                msg.debug('Not connected. Discarding view change.')

        while Listener.selection_changed:
            view, buf, ping = Listener.selection_changed.pop()
            # consume highlight events to avoid leak
            if 'highlight' not in G.PERMS:
                continue
            vb_id = view.buffer_id()
            if vb_id in reported:
                continue

            reported.add(vb_id)
            sel = view.sel()
            highlight_json = {
                'id': buf['id'],
                'name': 'highlight',
                'ranges': [[x.a, x.b] for x in sel],
                'ping': ping,
            }
            if Listener.agent:
                Listener.agent.put(highlight_json)
            else:
                msg.debug('Not connected. Discarding selection change.')

        utils.set_timeout(Listener.push, 100)
    def create_buf(path, always_add=False):
        if not utils.is_shared(path):
            msg.error('Skipping adding %s because it is not in shared path %s.' % (path, G.PROJECT_PATH))
            return
        if os.path.isdir(path):
            command = 'git ls-files %s' % path
            try:
                p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=path)
                stdoutdata, stderrdata = p.communicate()
                if p.returncode == 0:
                    for git_path in stdoutdata.split('\n'):
                        git_path = git_path.strip()
                        if not git_path:
                            continue
                        add_path = os.path.join(path, git_path)
                        msg.debug('adding %s' % add_path)
                        utils.set_timeout(Listener.create_buf, 0, add_path)
                    return
            except Exception as e:
                msg.debug("Couldn't run %s. This is probably OK. Error: %s" % (command, str(e)))

            for dirpath, dirnames, filenames in os.walk(path):
                # Don't care about hidden stuff
                dirnames[:] = [d for d in dirnames if d[0] != '.']
                for f in filenames:
                    f_path = os.path.join(dirpath, f)
                    if f[0] == '.':
                        msg.log('Not creating buf for hidden file %s' % f_path)
                    else:
                        utils.set_timeout(Listener.create_buf, 0, f_path)
            return
        try:
            buf_fd = open(path, 'rb')
            buf = buf_fd.read().decode('utf-8')
            rel_path = utils.to_rel_path(path)
            msg.log('creating buffer ', rel_path)
            event = {
                'name': 'create_buf',
                'buf': buf,
                'path': rel_path,
            }
            Listener.agent.put(event)
        except (IOError, OSError):
            msg.error('Failed to open %s.' % path)
        except Exception as e:
            msg.error('Failed to create buffer %s: %s' % (path, str(e)))
Beispiel #10
0
def get_or_create_chat(cb=None):
    def return_view():
        global LOG_LEVEL
        G.CHAT_VIEW_PATH = G.CHAT_VIEW.file_name()
        G.CHAT_VIEW.set_read_only(True)
        if G.DEBUG:
            LOG_LEVEL = LOG_LEVELS['DEBUG']
        if cb:
            return cb(G.CHAT_VIEW)

    def open_view():
        if not G.CHAT_VIEW:
            p = os.path.join(G.COLAB_DIR, 'msgs.floobits.log')
            G.CHAT_VIEW = G.ROOM_WINDOW.open_file(p)
        utils.set_timeout(return_view, 0)

    # Can't call open_file outside main thread
    utils.set_timeout(open_view, 0)
 def reconnect(self):
     try:
         self.sock.close()
     except Exception:
         pass
     G.CONNECTED = False
     self.room_info = {}
     self.buf = ''
     self.sock = None
     self.authed = False
     self.reconnect_delay *= 1.5
     if self.reconnect_delay > 10000:
         self.reconnect_delay = 10000
     if self.retries > 0:
         msg.log('Floobits: Reconnecting in %sms' % self.reconnect_delay)
         utils.set_timeout(self.connect, int(self.reconnect_delay))
     elif self.retries == 0:
         sublime.error_message('Floobits Error! Too many reconnect failures. Giving up.')
     self.retries -= 1
Beispiel #12
0
 def reconnect(self):
     try:
         self.sock.close()
     except Exception:
         pass
     G.CONNECTED = False
     self.room_info = {}
     self.buf = ''
     self.sock = None
     self.authed = False
     self.reconnect_delay *= 1.5
     if self.reconnect_delay > 10000:
         self.reconnect_delay = 10000
     if self.retries > 0:
         msg.log('Floobits: Reconnecting in %sms' % self.reconnect_delay)
         utils.set_timeout(self.connect, int(self.reconnect_delay))
     elif self.retries == 0:
         sublime.error_message(
             'Floobits Error! Too many reconnect failures. Giving up.')
     self.retries -= 1
Beispiel #13
0
 def update_view(buf, view=None):
     view = view or get_view(buf['id'])
     visible_region = view.visible_region()
     viewport_position = view.viewport_position()
     # deep copy
     selections = [x for x in view.sel()]
     MODIFIED_EVENTS.put(1)
     try:
         view.run_command('floo_view_replace_region', {'r': [0, view.size()], 'data': buf['buf']})
     except Exception as e:
         msg.error('Exception updating view: %s' % e)
     utils.set_timeout(view.set_viewport_position, 0, viewport_position, False)
     view.sel().clear()
     view.show(visible_region, False)
     for sel in selections:
         view.sel().add(sel)
     if 'patch' in G.PERMS:
         view.set_read_only(False)
     else:
         view.set_status('Floobits', 'You don\'t have write permission. Buffer is read-only.')
         view.set_read_only(True)
Beispiel #14
0
    def reconnect(self):
        if self.reconnect_timeout:
            return

        new_buf_out = collections.deque()
        total_len = 0
        while True:
            try:
                item = self.buf_out.popleft()
            except IndexError:
                break

            if item['name'] == 'term_stdout':
                total_len += len(item['data'])
                if total_len > MAX_BYTES_TO_BUFFER:
                    continue
                new_buf_out.appendleft(item)

        self.buf_out = new_buf_out

        if self.sock:
            self.remove_fd(self.sock.fileno())
            try:
                self.sock.shutdown(2)
            except Exception:
                pass
            try:
                self.sock.close()
            except Exception:
                pass
            self.sock = None
        self.authed = False
        self.reconnect_delay *= 1.5
        if self.reconnect_delay > 10000:
            self.reconnect_delay = 10000
        self.reconnect_timeout = utils.set_timeout(self.connect_to_internet, self.reconnect_delay)
Beispiel #15
0
 def set_timer(self, sec):
     self.remove_timer()
     self.timer = set_timeout(self.autoplay,sec)
    def apply_patch(patch_data):
        buf_id = patch_data['id']
        buf = BUFS[buf_id]
        view = get_view(buf_id)
        DMP = dmp.diff_match_patch()
        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
        if view:
            old_text = get_text(view)
        else:
            old_text = buf.get('buf', '')
        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:
                msg.debug('OMG EMPTY!')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', patch_data['patch'])
            if '\x01' in t[0]:
                msg.debug('FOUND CRAZY BYTE IN BUFFER')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', patch_data['patch'])

        if not clean_patch:
            msg.error('failed to patch %s cleanly. re-fetching buffer' % buf['path'])
            return Listener.get_buf(buf_id)

        cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest()
        if cur_hash != patch_data['md5_after']:
            msg.warn(
                '%s new hash %s != expected %s. re-fetching buffer...' %
                (buf['path'], cur_hash, patch_data['md5_after'])
            )
            return Listener.get_buf(buf_id)

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

        if not view:
            save_buf(buf)
            return

        selections = [x for x in view.sel()]  # deep copy
        regions = []
        for patch in t[2]:
            offset = patch[0]
            length = patch[1]
            patch_text = patch[2]
            region = sublime.Region(offset, offset + length)
            regions.append(region)
            MODIFIED_EVENTS.put(1)
            view.run_command('floo_view_replace_region', {'r': [offset, offset + length], 'data': patch_text})
            new_sels = []
            for sel in selections:
                a = sel.a
                b = sel.b
                new_offset = len(patch_text) - length
                if sel.a > offset:
                    a += new_offset
                if sel.b > offset:
                    b += new_offset
                new_sels.append(sublime.Region(a, b))
            selections = [x for x in new_sels]

        SELECTED_EVENTS.put(1)
        view.sel().clear()
        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, 1000, region_key)
        for sel in selections:
            SELECTED_EVENTS.put(1)
            view.sel().add(sel)

        now = datetime.now()
        view.set_status('Floobits', 'Changed by %s at %s' % (patch_data['username'], now.strftime('%H:%M')))
Beispiel #17
0
    def apply_patch(patch_data):
        buf_id = patch_data['id']
        buf = BUFS[buf_id]
        view = get_view(buf_id)
        DMP = dmp.diff_match_patch()
        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
        if view:
            old_text = get_text(view)
        else:
            old_text = buf.get('buf', '')
        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:
                msg.debug('OMG EMPTY!')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', patch_data['patch'])
            if '\x01' in t[0]:
                msg.debug('FOUND CRAZY BYTE IN BUFFER')
                msg.debug('Starting data:', buf['buf'])
                msg.debug('Patch:', patch_data['patch'])

        if not clean_patch:
            msg.error('failed to patch %s cleanly. re-fetching buffer' % buf['path'])
            return Listener.get_buf(buf_id)

        cur_hash = hashlib.md5(t[0].encode('utf-8')).hexdigest()
        if cur_hash != patch_data['md5_after']:
            msg.warn(
                '%s new hash %s != expected %s. re-fetching buffer...' %
                (buf['path'], cur_hash, patch_data['md5_after'])
            )
            return Listener.get_buf(buf_id)

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

        if not view:
            save_buf(buf)
            return

        selections = [x for x in view.sel()]  # deep copy
        regions = []
        for patch in t[2]:
            offset = patch[0]
            length = patch[1]
            patch_text = patch[2]
            region = sublime.Region(offset, offset + length)
            regions.append(region)
            MODIFIED_EVENTS.put(1)
            view.run_command('floo_view_replace_region', {'r': [offset, offset + length], 'data': patch_text})
            new_sels = []
            for sel in selections:
                a = sel.a
                b = sel.b
                new_offset = len(patch_text) - length
                if sel.a > offset:
                    a += new_offset
                if sel.b > offset:
                    b += new_offset
                new_sels.append(sublime.Region(a, b))
            selections = [x for x in new_sels]

        SELECTED_EVENTS.put(1)
        view.sel().clear()
        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, 1000, region_key)
        for sel in selections:
            SELECTED_EVENTS.put(1)
            view.sel().add(sel)

        now = datetime.now()
        view.set_status('Floobits', 'Changed by %s at %s' % (patch_data['username'], now.strftime('%H:%M')))
Beispiel #18
0
 def open_view():
     if not G.CHAT_VIEW:
         p = os.path.join(G.COLAB_DIR, 'msgs.floobits.log')
         G.CHAT_VIEW = G.ROOM_WINDOW.open_file(p)
     utils.set_timeout(return_view, 0)
Beispiel #19
0
 def set_timer(self, sec):
     self.remove_timer()
     self.timer = set_timeout(self.autoplay, sec)