def connect(self, cb=None): self.stop(False) 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.debug('Connecting to %s:%s' % (self.host, self.port)) try: 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(0) msg.debug('Connected!') self.reconnect_delay = G.INITIAL_RECONNECT_DELAY self.send_auth() if cb: cb()
def redraw(self) : if self.sem_obj != None: self.thread_lock.acquire() try : try : self.sem_obj.P() try : shm_obj = self.shm_obj size = struct.unpack_from("i", shm_obj.read(4,4*0))[0] x = struct.unpack_from("i", shm_obj.read(4,4*1))[0] y = struct.unpack_from("i", shm_obj.read(4,4*2))[0] width = struct.unpack_from("i", shm_obj.read(4,4*3))[0] height = struct.unpack_from("i", shm_obj.read(4,4*4))[0] pixbufloader = gtk.gdk.PixbufLoader() pixbufloader.write(shm_obj.read(size,4*5)) pixbufloader.close() pixbuf = pixbufloader.get_pixbuf() finally : self.sem_obj.V() pass pixbuf.copy_area(0, 0, pixbuf.get_width(), pixbuf.get_height(), self.pixbuf, x, y) self.rectangle = (x,y,width,height) self.win.queue_draw_area(x,y, pixbuf.get_width(), pixbuf.get_height()) except TypeError: msg.log("unexpected error:" + str(sys.exc_info()[0])) pass except : msg.log("unexpected general error:" + str(sys.exc_info())) pass finally: self.thread_lock.release() pass
def get_info(workspace_url, project_dir): repo_type = detect_type(project_dir) if not repo_type: return msg.debug('Detected ', repo_type, ' repo in ', project_dir) data = { 'type': repo_type, } cmd = REPO_MAPPING[repo_type]['cmd'] try: p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=project_dir) result = p.communicate() repo_url = result[0].decode('utf-8').strip() if repo_type == 'svn': repo_url = parse_svn_xml(repo_url) msg.log(repo_type, ' url is ', repo_url) if not repo_url: msg.error('Error getting ', repo_type, ' url:', result[1]) return except Exception as e: msg.error('Error getting ', repo_type, ' url:', str_e(e)) return data['url'] = repo_url return data
def send_error(description=None, exception=None): G.ERROR_COUNT += 1 if G.ERRORS_SENT > G.MAX_ERROR_REPORTS: msg.warn('Already sent %s errors this session. Not sending any more.' % G.ERRORS_SENT) return data = { 'jsondump': { 'error_count': G.ERROR_COUNT }, 'username': G.USERNAME, 'dir': G.COLAB_DIR, } if G.AGENT: data['owner'] = G.AGENT.owner data['workspace'] = G.AGENT.workspace if description: data['description'] = description if exception: data['message'] = { 'msg': str(exception), 'stack': traceback.format_exc(exception) } msg.log('Floobits plugin error! Sending exception report: %s' % data['message']) try: api_url = 'https://%s/api/log' % (G.DEFAULT_HOST) r = api_request(api_url, data) G.ERRORS_SENT += 1 return r except Exception as e: print(e)
def on_auth(self): self.authed = True self.retries = G.MAX_RETRIES msg.log("Successfully joined room %s/%s" % (self.owner, self.room)) if self._on_auth: self._on_auth(self) self._on_auth = None
def save_floorc_json(s): floorc_json = {} for k, v in s.items(): floorc_json[k.lower()] = v msg.log('Writing ', floorc_json) with open(G.FLOORC_JSON_PATH, 'w') as fd: fd.write(json.dumps(floorc_json, indent=4, sort_keys=True))
def save_floorc_json(s): floorc_json = {} for k, v in s.items(): floorc_json[k.lower()] = v msg.log('Writing %s' % floorc_json) with open(G.FLOORC_JSON_PATH, 'w') as fd: fd.write(json.dumps(floorc_json, indent=4, sort_keys=True))
def __init__(self, parent, path): self.parent = parent self.size = 0 self.children = [] self.files = [] self.ignores = { '/TOO_BIG/': [] } self.path = utils.unfuck_path(path) try: paths = os.listdir(self.path) except OSError as e: if e.errno != errno.ENOTDIR: raise self.path = os.path.dirname(self.path) self.add_file(os.path.basename(path)) return except Exception as e: msg.error('Error listing path %s: %s' % (path, unicode(e))) return msg.log('Initializing ignores for %s' % path) for ignore_file in IGNORE_FILES: try: self.load(ignore_file) except: pass for p in paths: self.add_file(p)
def on_auth(self): self.authed = True self.retries = G.MAX_RETRIES msg.log('Successfully joined room %s/%s' % (self.owner, self.room)) if self._on_auth: self._on_auth(self) self._on_auth = None
def save_floorc_json(s): floorc_json = {} for k, v in s.items(): floorc_json[k.lower()] = v msg.log("Writing ", floorc_json) with open(G.FLOORC_JSON_PATH, "w") as fd: fd.write(json.dumps(floorc_json, indent=4, sort_keys=True, separators=(",", ": ")))
def send_error(description=None, exception=None): G.ERROR_COUNT += 1 if G.ERRORS_SENT >= G.MAX_ERROR_REPORTS: msg.warn('Already sent %s errors this session. Not sending any more.' % G.ERRORS_SENT) return data = { 'jsondump': { 'error_count': G.ERROR_COUNT }, 'message': {}, 'dir': G.COLAB_DIR, } if G.AGENT: data['owner'] = G.AGENT.owner data['username'] = G.AGENT.username data['workspace'] = G.AGENT.workspace if exception: data['message'] = { 'description': str(exception), 'stack': traceback.format_exc(exception) } msg.log('Floobits plugin error! Sending exception report: %s' % data['message']) if description: data['message']['description'] = description try: # TODO: use G.AGENT.proto.host? api_url = 'https://%s/api/log' % (G.DEFAULT_HOST) r = api_request(G.DEFAULT_HOST, api_url, data) G.ERRORS_SENT += 1 return r except Exception as e: print(e)
def on_highlight(self, data): # floobits.highlight(data['id'], region_key, data['username'], data['ranges'], data.get('ping', False)) # buf_id, region_key, username, ranges, ping=False): ping = data.get("ping", False) if self.follow_mode: ping = True buf = self.FLOO_BUFS[data["id"]] view = self.get_view(data["id"]) if not view: if not ping: return view = self.create_view(buf) if not view: return if ping: try: offset = data["ranges"][0][0] except IndexError as e: msg.debug("could not get offset from range %s" % e) else: msg.log("You have been summoned by %s" % (data.get("username", "an unknown user"))) view.focus() view.set_cursor_position(offset) if G.SHOW_HIGHLIGHTS: view.highlight(data["ranges"], data["user_id"])
def send_error(description=None, exception=None): G.ERROR_COUNT += 1 if G.ERRORS_SENT >= G.MAX_ERROR_REPORTS: msg.warn('Already sent ', G.ERRORS_SENT, ' errors this session. Not sending any more.\n', description, exception) return data = { 'jsondump': { 'error_count': G.ERROR_COUNT }, 'message': {}, 'dir': G.COLAB_DIR, } if G.AGENT: data['owner'] = getattr(G.AGENT, "owner", None) data['username'] = getattr(G.AGENT, "username", None) data['workspace'] = getattr(G.AGENT, "workspace", None) if exception: data['message'] = { 'description': str(exception), 'stack': traceback.format_exc(exception) } msg.log('Floobits plugin error! Sending exception report: ', data['message']) if description: data['message']['description'] = description try: # TODO: use G.AGENT.proto.host? api_url = 'https://%s/api/log' % (G.DEFAULT_HOST) r = api_request(G.DEFAULT_HOST, api_url, data) G.ERRORS_SENT += 1 return r except Exception as e: print(e)
def delete_buf(path): if not utils.is_shared(path): msg.error('Skipping deleting %s because it is not in shared path %s.' % (path, G.PROJECT_PATH)) return if os.path.isdir(path): for dirpath, dirnames, filenames in os.walk(path): # TODO: rexamine this assumption # 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 deleting buf for hidden file %s' % f_path) else: Listener.delete_buf(f_path) return buf_to_delete = None rel_path = utils.to_rel_path(path) for buf_id, buf in BUFS.items(): if rel_path == buf['path']: buf_to_delete = buf break if buf_to_delete is None: msg.error('%s is not in this room' % path) return msg.log('deleting buffer ', rel_path) event = { 'name': 'delete_buf', 'id': buf_to_delete['id'], } Listener.agent.put(event)
def on_highlight(self, data): # floobits.highlight(data['id'], region_key, data['username'], data['ranges'], data.get('ping', False)) #buf_id, region_key, username, ranges, ping=False): ping = data.get('ping', False) if self.follow_mode: ping = True buf = self.FLOO_BUFS[data['id']] view = self.get_view(data['id']) if not view: if not ping: return view = self.create_view(buf) if not view: return if ping: try: offset = data['ranges'][0][0] except IndexError as e: msg.debug('could not get offset from range %s' % e) else: msg.log('You have been summoned by %s' % (data.get('username', 'an unknown user'))) view.focus() view.set_cursor_position(offset) if G.SHOW_HIGHLIGHTS: view.highlight(data['ranges'], data['user_id'])
def create_buf(path): # >>> (lambda x: lambda: x)(2)() # TODO: check if functools can do this in st2 # really_create_buf = lambda x: (lambda: Listener.create_buf(x)) def really_create_buf(x): return (lambda: Listener.create_buf(x)) 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): 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: sublime.set_timeout(really_create_buf(f_path), 0) 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)))
def delete_buf(self, path): """deletes a path""" if not path: return path = utils.get_full_path(path) if not self.is_shared(path): msg.error("Skipping deleting %s because it is not in shared path %s." % (path, G.PROJECT_PATH)) return if os.path.isdir(path): 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 deleting buf for hidden file %s" % f_path) else: self.delete_buf(f_path) return buf_to_delete = None rel_path = utils.to_rel_path(path) for buf_id, buf in self.FLOO_BUFS.items(): if rel_path == buf["path"]: buf_to_delete = buf break if buf_to_delete is None: msg.error("%s is not in this room" % path) return msg.log("deleting buffer ", rel_path) event = {"name": "delete_buf", "id": buf_to_delete["id"]} self.agent.put(event)
def update_view(self, buf, view=None): msg.debug('updating view for buf %s' % buf['id']) view = view or self.get_view(buf['id']) if not view: msg.log('view for buf %s not found. not updating' % buf['id']) return self.MODIFIED_EVENTS.put(1) view.set_text(buf['buf'])
def stop(self): for _conn in self._protos: _conn.stop() self._protos = [] self._handlers = [] msg.log("Disconnected.") editor.status_message("Disconnected.")
def stop(self): for _conn in self._protos: _conn.stop() self._protos = [] self._handlers = [] msg.log('Disconnected.') editor.status_message('Disconnected.')
def update_view(self, buf, view=None): msg.debug("updating view for buf %s" % buf["id"]) view = view or self.get_view(buf["id"]) if not view: msg.log("view for buf %s not found. not updating" % buf["id"]) return self.MODIFIED_EVENTS.put(1) view.set_text(buf["buf"])
def on_delete_buf(self, data): # TODO: somehow tell the user about this. maybe delete on disk too? del self.FLOO_BUFS[data['id']] path = utils.get_full_path(data['path']) if not G.DELETE_LOCAL_FILES: msg.log('Not deleting %s because delete_local_files is disabled' % path) return utils.rm(path) msg.warn('deleted %s because %s told me to.' % (path, data.get('username', 'the internet')))
def stop(self): msg.log('Disconnecting from room %s/%s' % (self.owner, self.room)) try: self.retries = -1 self.sock.shutdown(2) self.sock.close() except Exception: pass msg.log('Disconnected.')
def on_delete_buf(self, data): # TODO: somehow tell the user about this. maybe delete on disk too? del self.FLOO_BUFS[data["id"]] path = utils.get_full_path(data["path"]) if not G.DELETE_LOCAL_FILES: msg.log("Not deleting %s because delete_local_files is disabled" % path) return utils.rm(path) msg.warn("deleted %s because %s told me to." % (path, data.get("username", "the internet")))
def recurse(self, root): try: paths = os.listdir(self.path) except OSError as e: if e.errno != errno.ENOTDIR: msg.error('Error listing path ', self.path, ': ', str_e(e)) return except Exception as e: msg.error('Error listing path ', self.path, ': ', str_e(e)) return msg.debug('Initializing ignores for ', self.path) for ignore_file in IGNORE_FILES: try: self.load(ignore_file) except Exception: pass for p in paths: if p == '.' or p == '..': continue if p in BLACKLIST: msg.log('Ignoring blacklisted file ', p) continue p_path = os.path.join(self.path, p) try: s = os.stat(p_path) except Exception as e: msg.error('Error stat()ing path ', p_path, ': ', str_e(e)) continue if stat.S_ISREG(s.st_mode) and p in HIDDEN_WHITELIST: # Don't count these whitelisted files in size self.files.append(p_path) continue is_dir = stat.S_ISDIR(s.st_mode) if root.is_ignored(p_path, is_dir, True): continue if is_dir: ig = Ignore(p_path, self) self.children[p] = ig ig.recurse(root) self.total_size += ig.total_size continue if stat.S_ISREG(s.st_mode): if s.st_size > (MAX_FILE_SIZE): self.ignores['/TOO_BIG/'].append(p) msg.log( self.is_ignored_message(p_path, p, '/TOO_BIG/', False)) else: self.size += s.st_size self.total_size += s.st_size self.files.append(p_path)
def stop(self): msg.log("Disconnecting from room %s/%s" % (self.owner, self.room)) try: self.retries = -1 self.sock.shutdown(2) self.sock.close() except Exception: return False msg.log("Disconnected.") return True
def recurse(self, root): try: paths = os.listdir(self.path) except OSError as e: if e.errno != errno.ENOTDIR: msg.error('Error listing path ', self.path, ': ', str_e(e)) return except Exception as e: msg.error('Error listing path ', self.path, ': ', str_e(e)) return msg.debug('Initializing ignores for ', self.path) for ignore_file in IGNORE_FILES: try: self.load(ignore_file) except Exception: pass for p in paths: if p == '.' or p == '..': continue if p in BLACKLIST: msg.log('Ignoring blacklisted file ', p) continue p_path = os.path.join(self.path, p) try: s = os.stat(p_path) except Exception as e: msg.error('Error stat()ing path ', p_path, ': ', str_e(e)) continue if stat.S_ISREG(s.st_mode) and p in HIDDEN_WHITELIST: # Don't count these whitelisted files in size self.files.append(p_path) continue is_dir = stat.S_ISDIR(s.st_mode) if root.is_ignored(p_path, is_dir, True): continue if is_dir: ig = Ignore(p_path, self) self.children[p] = ig ig.recurse(root) self.total_size += ig.total_size continue if stat.S_ISREG(s.st_mode): if s.st_size > (MAX_FILE_SIZE): self.ignores['/TOO_BIG/'].append(p) msg.log(self.is_ignored_message(p_path, p, '/TOO_BIG/', False)) else: self.size += s.st_size self.total_size += s.st_size self.files.append(p_path)
def on_emacs_delete_buf(self, req): buf = self.get_buf_by_path(req['path']) if not buf: msg.debug('No buffer for path %s' % req['path']) return msg.log('deleting buffer ', buf['path']) event = { 'name': 'delete_buf', 'id': buf['id'], } self.agent.put(event)
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)))
def get_git_excludesfile(): global_ignore = None try: p = subprocess.Popen(['git', 'config -z --get core.excludesfile'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) result = p.communicate() global_ignore = result[0] if not global_ignore: return global_ignore = os.path.realpath(os.path.expanduser(str(global_ignore))) msg.log('git core.excludesfile is ', global_ignore) except Exception as e: msg.error('Error getting git core.excludesfile:', str_e(e)) return global_ignore
def get_git_excludesfile(): global_ignore = None try: p = subprocess.Popen(['git', 'config -z --get core.excludesfile'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) result = p.communicate() global_ignore = result[0] if not global_ignore: return global_ignore = os.path.realpath(os.path.expanduser(global_ignore.decode('utf-8'))) msg.log('git core.excludesfile is ', global_ignore) except Exception as e: msg.error('Error getting git core.excludesfile:', str_e(e)) return global_ignore
def msg_log(self, _type, _dir="-->", data=None, err=""): if self.is_debug_msg(_type): data = {} if data is None else data msg.log( self.addr, _type, _dir, name=self.name, data=data, err=err, pg=self.pg, tag=self._conn_tag )
def _is_ignored(self, rel_path, is_dir, log): base_path, file_name = os.path.split(rel_path) if not is_dir and file_name in HIDDEN_WHITELIST: return False for ignore_file, patterns in self.ignores.items(): for pattern in patterns: orig_pattern = pattern negate = False match = False if pattern[0] in NEGATE_PREFIXES: negate = True pattern = pattern[1:] if not pattern: continue if pattern[0] == '/': match = fnmatch.fnmatch(rel_path, pattern[1:]) else: if len(pattern) > 0 and pattern[-1] == '/': if is_dir: pattern = pattern[:-1] if file_name == pattern: match = True elif base_path == pattern or (pattern[-1] == '/' and base_path == pattern[:-1]): match = True elif fnmatch.fnmatch(file_name, pattern): match = True elif fnmatch.fnmatch(rel_path, pattern): match = True if match: if log: msg.log( self.is_ignored_message(rel_path, orig_pattern, ignore_file, negate)) if negate: return False return True split = rel_path.split("/", 1) if len(split) != 2: return False name, new_path = split ig = self.children.get(name) if ig: return ig._is_ignored(new_path, is_dir, log) return False
def stop(self, log=True): if log: msg.log('Disconnecting from room %s/%s' % (self.owner, self.room)) sublime.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
def on_room_info(self, data): # Success! Reset counter self.room_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_room_url( { "host": self.agent.host, "owner": self.agent.owner, "port": self.agent.port, "room": self.agent.room, "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()
def proxy_api_request(url, data, method): args = ['python', '-m', 'floo.proxy', '--url', url] if data: args += ["--data", json.dumps(data)] if method: args += ["--method", method] msg.log('Running %s (%s)' % (' '.join(args), G.PLUGIN_PATH)) proc = subprocess.Popen(args, cwd=G.PLUGIN_PATH, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (stdout, stderr) = proc.communicate() if stderr: raise IOError(stderr) if proc.poll() != 0: raise IOError(stdout) r = APIResponse(stdout) return r
def proxy_api_request(host, url, data, method): args = ["python", "-m", "floo.proxy", "--host", host, "--url", url] if data: args += ["--data", json.dumps(data)] if method: args += ["--method", method] msg.log("Running ", " ".join(args), " (", G.PLUGIN_PATH, ")") proc = subprocess.Popen(args, cwd=G.PLUGIN_PATH, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (stdout, stderr) = proc.communicate() if stderr: raise IOError(stderr) if proc.poll() != 0: raise IOError(stdout) r = APIResponse(stdout) return r
def proxy_api_request(host, url, data, method): args = ['python', '-m', 'floo.proxy', '--host', host, '--url', url] if data: args += ["--data", json.dumps(data)] if method: args += ["--method", method] msg.log('Running ', ' '.join(args), ' (', G.PLUGIN_PATH, ')') proc = subprocess.Popen(args, cwd=G.PLUGIN_PATH, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (stdout, stderr) = proc.communicate() if stderr: raise IOError(stderr) if proc.poll() != 0: raise IOError(stdout) r = APIResponse(stdout) return r
def on_room_info(self, data): # Success! Reset counter self.room_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_room_url({ 'host': self.agent.host, 'owner': self.agent.owner, 'port': self.agent.port, 'room': self.agent.room, '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()
def _is_ignored(self, rel_path, is_dir, log): base_path, file_name = os.path.split(rel_path) if not is_dir and file_name in HIDDEN_WHITELIST: return False for ignore_file, patterns in self.ignores.items(): for pattern in patterns: orig_pattern = pattern negate = False match = False if pattern[0] in NEGATE_PREFIXES: negate = True pattern = pattern[1:] if not pattern: continue if pattern[0] == '/': match = fnmatch.fnmatch(rel_path, pattern[1:]) else: if len(pattern) > 0 and pattern[-1] == '/': if is_dir: pattern = pattern[:-1] if file_name == pattern: match = True elif base_path == pattern or (pattern[-1] == '/' and base_path == pattern[:-1]): match = True elif fnmatch.fnmatch(file_name, pattern): match = True elif fnmatch.fnmatch(rel_path, pattern): match = True if match: if log: msg.log(self.is_ignored_message(rel_path, orig_pattern, ignore_file, negate)) if negate: return False return True split = rel_path.split("/", 1) if len(split) != 2: return False name, new_path = split ig = self.children.get(name) if ig: return ig._is_ignored(new_path, is_dir, log) return False
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)))
def send_error(description=None, exception=None): G.ERROR_COUNT += 1 data = { 'jsondump': { 'error_count': G.ERROR_COUNT }, 'message': {}, 'dir': G.COLAB_DIR, } stack = '' if G.AGENT: data['owner'] = getattr(G.AGENT, "owner", None) data['username'] = getattr(G.AGENT, "username", None) data['workspace'] = getattr(G.AGENT, "workspace", None) if exception: exc_info = sys.exc_info() try: stack = traceback.format_exception(*exc_info) except Exception: if exc_info[0] is None: stack = 'No sys.exc_info()' else: stack = "Python is rtardd" try: description = str(exception) except Exception: description = "Python is rtadd" data['message'] = { 'description': description, 'stack': stack } msg.log('Floobits plugin error! Sending exception report: ', data['message']) if description: data['message']['description'] = description if G.ERRORS_SENT >= G.MAX_ERROR_REPORTS: msg.warn('Already sent ', G.ERRORS_SENT, ' errors this session. Not sending any more.\n', description, exception, stack) return try: # TODO: use G.AGENT.proto.host? api_url = 'https://%s/api/log' % (G.DEFAULT_HOST) r = api_request(G.DEFAULT_HOST, api_url, data) G.ERRORS_SENT += 1 return r except Exception as e: print(e)
def reconnect(self): try: self.sock.close() except Exception: pass self.room_info = {} self.net_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) sublime.set_timeout(self.connect, int(self.reconnect_delay)) elif self.retries == 0: msg.error( 'Floobits Error! Too many reconnect failures. Giving up.') self.retries -= 1
def __init__(self, current, buf): self.buf = buf self.current = current self.previous = buf['buf'] if buf['encoding'] == 'base64': self.md5_before = hashlib.md5(self.previous).hexdigest() self.md5_after = hashlib.md5(self.current).hexdigest() else: try: self.md5_before = hashlib.md5(self.previous.encode('utf-8')).hexdigest() except Exception as e: # Horrible fallback if for some reason encoding doesn't agree with actual object self.md5_before = hashlib.md5(self.previous).hexdigest() msg.log('Error calculating md5_before for ', str(self), ': ', str_e(e)) try: self.md5_after = hashlib.md5(self.current.encode('utf-8')).hexdigest() except Exception as e: # Horrible fallback if for some reason encoding doesn't agree with actual object self.md5_after = hashlib.md5(self.current).hexdigest() msg.log('Error calculating md5_after for ', str(self), ': ', str_e(e))
def create_buf(self, path): if 'create_buf' not in self.perms: msg.error("Skipping %s. You don't have permission to create buffers in this room." % path) return if not self.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): 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) if f in self.ignored_names: # TODO: prompt instead of being lame msg.log('Not creating buf for ignored file %s' % f_path) else: sublime.set_timeout(self.create_buf, 0, f_path) return if self.get_buf_by_path(path): msg.debug('Buf %s already exists in room. Skipping adding.' % path) return try: buf_fd = open(path, 'rb') buf = buf_fd.read().decode('utf-8') rel_path = utils.to_rel_path(path) msg.debug('creating buffer ', rel_path) event = { 'name': 'create_buf', 'buf': buf, 'path': rel_path, } self.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)))
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 add_file(self, p): p_path = os.path.join(self.path, p) if p[0] == '.' and p not in HIDDEN_WHITELIST: msg.log('Ignoring hidden path %s' % p_path) return is_ignored = self.is_ignored(p_path) if is_ignored: msg.log(is_ignored) return try: s = os.stat(p_path) except Exception as e: msg.error('Error stat()ing path %s: %s' % (p_path, unicode(e))) return if stat.S_ISDIR(s.st_mode): ig = Ignore(self, p_path) self.children.append(ig) self.size += ig.size return elif stat.S_ISREG(s.st_mode): if s.st_size > (MAX_FILE_SIZE): self.ignores['/TOO_BIG/'].append(p) msg.log(self.is_ignored_message(p_path, p, '/TOO_BIG/')) else: self.size += s.st_size self.files.append(p)
def got_msg(sock, client_obj, byte_data): message = msg.extract_message(sock, client_obj, byte_data); if (message != None): mid = message.mid; params = message.params; np = len(params); msg.log(message); callback.process_message(message); return; if (verify_params(mid, _MID.RECV_CLIENT_REGISTER_USER_PASS, np)): print("username: %s, password: %s" % (params[0], params[1])); db.add_user_account(params[0], params[1]); elif (verify_params(mid, _MID.RELAY_TEST, np)): msg.send(sock, client_obj, msg.build(_MID.RELAY_TEST, client_obj.id, client_obj.ip, client_obj.c_tcp_port, client_obj.c_udp_port)); elif (verify_params(mid, _MID.SEND_CLIENT_ID, np)): pass; elif (verify_params(mid, _MID.RECV_CLIENT_BINDED_UDP_PORT, np)): client_obj.c_udp_port = params[0]; elif (verify_params(mid, _MID.RECV_UDP_PEER_BIND_PORT_SUCCESS, np)): client_obj.joined_game.received_udp_bind_port(client_obj.game_client, params[0], params[1], params[2]); elif (verify_params(mid, _MID.RECV_UDP_PEER_BIND_PORT_FAILED, np)): client_obj.joined_game.received_udp_bind_port(client_obj.game_client, params[0], params[1], -1); elif (verify_params(mid, _MID.RECV_PEER_CONNECT_SUCCESS, np)): client_obj.joined_game.received_connect_success(client_obj.game_client, params[0], params[1]); elif (verify_params(mid, _MID.BEGIN_RELAY_TEST, np)): msg.send(sock, client_obj, msg.build(_MID.RELAY_TEST, client_obj.id, client_obj.ip, client_obj.c_tcp_port, client_obj.c_udp_port)); else: print("received msg (raw: %s, len: %d) has an unknown MID" % (byte_data, byte_data.__len__()));
def _is_ignored(self, rel_path, is_dir, log): base_path, file_name = os.path.split(rel_path) for ignore_file, patterns in self.ignores.items(): for pattern in patterns: orig_pattern = pattern exclude = False match = False if pattern[0] == "!": exclude = True pattern = pattern[1:] if pattern[0] == '/': match = fnmatch.fnmatch(rel_path, pattern[1:]) else: if len(pattern) > 0 and pattern[-1] == '/': if is_dir: pattern = pattern[:-1] if fnmatch.fnmatch(file_name, pattern): match = True elif fnmatch.fnmatch(rel_path, pattern): match = True if match: if log: msg.log( self.is_ignored_message(rel_path, orig_pattern, ignore_file, exclude)) if exclude: return False return True split = rel_path.split("/", 1) if len(split) != 2: return False name, new_path = split ig = self.children.get(name) if ig: return ig._is_ignored(new_path, is_dir, log) return False
def on_post_save(self, view): def cleanup(): del self.between_save_events[view.buffer_id()] if view == G.CHAT_VIEW or view.file_name() == G.CHAT_VIEW_PATH: return cleanup() else: print(G.CHAT_VIEW_PATH, "not", view.file_name()) event = None buf = get_buf(view) name = utils.to_rel_path(view.file_name()) old_name = self.between_save_events[view.buffer_id()] if buf is None: if utils.is_shared(view.file_name()): msg.log('new buffer ', name, view.file_name()) event = { 'name': 'create_buf', 'buf': get_text(view), 'path': name } elif name != old_name: if utils.is_shared(view.file_name()): msg.log('renamed buffer {0} to {1}'.format(old_name, name)) event = { 'name': 'rename_buf', 'id': buf['id'], 'path': name } else: msg.log('deleting buffer from shared: {0}'.format(name)) event = { 'name': 'delete_buf', 'id': buf['id'], } if event and Listener.agent: Listener.agent.put(event) cleanup()
def on_connect(self): msg.log('Connection established.') self.proto.proxy = self.proxy
def on_connect(self): msg.log('Connection established.') reactor.reactor.connect(FlooConn(self), G.DEFAULT_HOST, G.DEFAULT_PORT, True)
def protocol(self, req): self.buf += req.decode('utf-8') msg.debug('buf: %s' % self.buf) while True: before, sep, after = self.buf.partition('\n') if not sep: break try: data = json.loads(before) except Exception as e: msg.error('Unable to parse json: %s' % str(e)) msg.error('Data: %s' % before) raise e name = data.get('name') if name == 'patch': # TODO: we should do this in a separate thread Listener.apply_patch(data) elif name == 'get_buf': buf_id = data['id'] listener.BUFS[buf_id] = data view = listener.get_view(buf_id) if view: Listener.update_view(data, view) else: listener.save_buf(data) elif name == 'create_buf': listener.BUFS[data['id']] = data listener.save_buf(data) elif name == 'rename_buf': new = utils.get_full_path(data['path']) old = utils.get_full_path(data['old_path']) new_dir = os.path.split(new)[0] if new_dir: utils.mkdir(new_dir) os.rename(old, new) view = listener.get_view(data['id']) if view: view.retarget(new) elif name == 'delete_buf': path = utils.get_full_path(data['path']) try: utils.rm(path) except Exception: pass listener.delete_buf(data['id']) elif name == 'room_info': # Success! Reset counter self.retries = G.MAX_RETRIES self.room_info = data G.PERMS = data['perms'] if 'patch' not in data['perms']: msg.log( 'We don\'t have patch permission. Setting buffers to read-only' ) project_json = {'folders': [{'path': G.PROJECT_PATH}]} utils.mkdir(G.PROJECT_PATH) with open(os.path.join(G.PROJECT_PATH, '.sublime-project'), 'wb') as project_fd: project_fd.write( json.dumps(project_json, indent=4, sort_keys=True).encode('utf-8')) floo_json = { 'url': utils.to_room_url({ 'host': self.host, 'owner': self.owner, 'port': self.port, 'room': self.room, 'secure': self.secure, }) } with open(os.path.join(G.PROJECT_PATH, '.floo'), 'w') as floo_fd: floo_fd.write( json.dumps(floo_json, indent=4, sort_keys=True)) for buf_id, buf in data['bufs'].items(): buf_id = int(buf_id) # json keys must be strings buf_path = utils.get_full_path(buf['path']) new_dir = os.path.dirname(buf_path) utils.mkdir(new_dir) listener.BUFS[buf_id] = buf try: buf_fd = open(buf_path, 'rb') buf_buf = buf_fd.read().decode('utf-8') md5 = hashlib.md5(buf_buf.encode('utf-8')).hexdigest() if md5 == buf['md5']: msg.debug('md5 sums match. not getting buffer') buf['buf'] = buf_buf else: msg.debug( 'md5 for %s should be %s but is %s. getting buffer' % (buf['path'], buf['md5'], md5)) raise Exception('different md5') except Exception as e: msg.debug('Error calculating md5:', e) Listener.get_buf(buf_id) self.authed = True G.CONNECTED = True msg.log('Successfully joined room %s/%s' % (self.owner, self.room)) if self.on_connect: self.on_connect(self) self.on_connect = None elif name == 'join': msg.log('%s joined the room' % data['username']) self.room_info['users'][data['user_id']] = data['username'] elif name == 'part': msg.log('%s left the room' % data['username']) try: del self.room_info['users'][data['user_id']] except Exception as e: print('Unable to delete user %s from user list' % (data)) region_key = 'floobits-highlight-%s' % (data['user_id']) for window in sublime.windows(): for view in window.views(): view.erase_regions(region_key) elif name == 'highlight': region_key = 'floobits-highlight-%s' % (data['user_id']) Listener.highlight(data['id'], region_key, data['username'], data['ranges'], data.get('ping', False)) elif name == 'error': message = 'Floobits: Error! Message: %s' % str(data.get('msg')) msg.error(message) elif name == 'disconnect': message = 'Floobits: Disconnected! Reason: %s' % str( data.get('reason')) msg.error(message) sublime.error_message(message) self.stop() elif name == 'msg': self.on_msg(data) else: msg.debug('unknown name!', name, 'data:', data) self.buf = after