def refresh_workspace(self): ig = ignore.create_ignore_tree(G.PROJECT_PATH) G.IGNORE = ig read_only = 'patch' not in self.workspace_info['perms'] changed_bufs, missing_bufs, new_files = self._scan_dir( self.bufs, G.IGNORE, read_only) ignored = [] for p, buf_id in self.paths_to_ids.items(): if p not in new_files: ignored.append(p) new_files.discard(p) if changed_bufs or missing_bufs or new_files: stomp_local = yield self.stomp_prompt, changed_bufs, missing_bufs, list( new_files), ignored if stomp_local not in [0, 1]: return if stomp_local: for buf in changed_bufs: self.get_buf(buf['id'], buf.get('view')) self.save_on_get_bufs.add(buf['id']) for buf in missing_bufs: self.get_buf(buf['id'], buf.get('view')) self.save_on_get_bufs.add(buf['id']) else: yield self._initial_upload, G.IGNORE, missing_bufs, changed_bufs else: editor.error_message('No files differ')
def _handle(self, data): self._buf += data while True: before, sep, after = self._buf.partition(self.NEWLINE) if not sep: return try: # Node.js sends invalid utf8 even though we're calling write(string, "utf8") # Python 2 can figure it out, but python 3 hates it and will die here with some byte sequences # Instead of crashing the plugin, we drop the data. Yes, this is horrible. before = before.decode('utf-8', 'ignore') data = json.loads(before) except Exception as e: msg.error('Unable to parse json: %s' % str(e)) msg.error('Data: %s' % before) # XXXX: THIS LOSES DATA self._buf = after continue name = data.get('name') try: msg.debug("got data " + name) self.emit("data", name, data) except Exception as e: print(traceback.format_exc()) msg.error('Error handling %s event (%s).' % (name, str(e))) if name == 'room_info': editor.error_message('Error joining workspace: %s' % str(e)) self.stop() self._buf = after
def refresh_workspace(self): ig = ignore.create_ignore_tree(G.PROJECT_PATH) G.IGNORE = ig read_only = 'patch' not in self.workspace_info['perms'] changed_bufs, missing_bufs, new_files = self._scan_dir(self.bufs, G.IGNORE, read_only) ignored = [] for p, buf_id in self.paths_to_ids.items(): if p not in new_files: ignored.append(p) new_files.discard(p) if changed_bufs or missing_bufs or new_files: stomp_local = yield self.stomp_prompt, changed_bufs, missing_bufs, list(new_files), ignored if stomp_local not in [0, 1]: return if stomp_local: for buf in changed_bufs: self.get_buf(buf['id'], buf.get('view')) self.save_on_get_bufs.add(buf['id']) for buf in missing_bufs: self.get_buf(buf['id'], buf.get('view')) self.save_on_get_bufs.add(buf['id']) else: yield self._initial_upload, G.IGNORE, missing_bufs, changed_bufs else: editor.error_message('No files differ')
def _on_create_user(self, data): try: del data['name'] floorc_json = {'auth': {}} floorc_json['auth'][G.DEFAULT_HOST] = data utils.save_floorc_json(floorc_json) utils.reload_settings() if utils.can_auth(): p = os.path.join(G.BASE_DIR, 'welcome.md') with open(p, 'w') as fd: username = G.AUTH.get(self.proto.host, {}).get('username') text = editor.NEW_ACCOUNT_TXT.format(username=username, host=self.proto.host) fd.write(text) d = utils.get_persistent_data() d['auto_generated_account'] = True utils.update_persistent_data(d) G.AUTO_GENERATED_ACCOUNT = True editor.open_file(p) else: editor.error_message( 'Something went wrong. You will need to sign up for an account to use Floobits.' ) api.send_error('No username or secret') except Exception as e: msg.debug(traceback.format_exc()) msg.error(str_e(e)) finally: try: d = utils.get_persistent_data() d['disable_account_creation'] = True utils.update_persistent_data(d) finally: self.stop()
def prompt_ignore(self, ig, path, cb): ignore.create_flooignore(ig.path) dirs = ig.get_children() dirs.append(ig) dirs = sorted(dirs, key=attrgetter('size')) size = starting_size = reduce(lambda x, c: x + c.size, dirs, 0) too_big = [] while size > MAX_WORKSPACE_SIZE and dirs: cd = dirs.pop() size -= cd.size too_big.append(cd) if size > MAX_WORKSPACE_SIZE: editor.error_message( 'Maximum workspace size is %.2fMB.\n\n%s is too big (%.2fMB) to upload. Consider adding stuff to the .flooignore file.' % (MAX_WORKSPACE_SIZE / 1000000.0, path, ig.size / 1000000.0)) cb([set(), 0]) return if too_big: txt = TOO_BIG_TEXT % (MAX_WORKSPACE_SIZE / 1000000.0, path, starting_size / 1000000.0, "\n".join( set([x.path for x in too_big]))) upload = yield self.ok_cancel_dialog, txt if not upload: cb([set(), 0]) return files = set() for ig in dirs: files = files.union(set([utils.to_rel_path(x) for x in ig.files])) cb([files, size])
def _on_create_user(self, data): try: del data['name'] floorc_json = { 'auth': {} } floorc_json['auth'][G.DEFAULT_HOST] = data utils.save_floorc_json(floorc_json) utils.reload_settings() if utils.can_auth(): p = os.path.join(G.BASE_DIR, 'welcome.md') with open(p, 'w') as fd: username = G.AUTH.get(self.proto.host, {}).get('username') text = editor.NEW_ACCOUNT_TXT.format(username=username, host=self.proto.host) fd.write(text) d = utils.get_persistent_data() d['auto_generated_account'] = True utils.update_persistent_data(d) G.AUTO_GENERATED_ACCOUNT = True editor.open_file(p) else: editor.error_message('Something went wrong. You will need to sign up for an account to use Floobits.') api.send_error('No username or secret') except Exception as e: msg.debug(traceback.format_exc()) msg.error(str_e(e)) finally: try: d = utils.get_persistent_data() d['disable_account_creation'] = True utils.update_persistent_data(d) finally: self.stop()
def upload(self, path): if not utils.is_shared(path): editor.error_message( 'Cannot share %s because is not in shared path %s.\n\nPlease move it there and try again.' % (path, G.PROJECT_PATH)) return ig = ignore.create_ignore_tree(G.PROJECT_PATH) G.IGNORE = ig is_dir = os.path.isdir(path) if ig.is_ignored(path, is_dir, True): editor.error_message( 'Cannot share %s because it is ignored.\n\nAdd an exclude rule (!%s) to your .flooignore file.' % (path, path)) return rel_path = utils.to_rel_path(path) if not is_dir: self._upload_file_by_path(rel_path) return for p in rel_path.split('/'): child = ig.children.get(p) if not child: break ig = child if ig.path != path: msg.warn(ig.path, ' is not the same as ', path) self._rate_limited_upload(ig.list_paths(), ig.total_size, upload_func=self._upload_file_by_path)
def _handle(self, data): self._buf += data while True: before, sep, after = self._buf.partition(b"\n") if not sep: return try: # Node.js sends invalid utf8 even though we're calling write(string, "utf8") # Python 2 can figure it out, but python 3 hates it and will die here with some byte sequences # Instead of crashing the plugin, we drop the data. Yes, this is horrible. before = before.decode("utf-8", "ignore") data = json.loads(before) except Exception as e: msg.error("Unable to parse json: %s" % str(e)) msg.error("Data: %s" % before) # XXXX: THIS LOSES DATA self._buf = after continue name = data.get("name") try: msg.debug("got data " + (name or "no name")) self.emit("data", name, data) except Exception as e: print(traceback.format_exc()) msg.error("Error handling %s event (%s)." % (name, str(e))) if name == "room_info": editor.error_message("Error joining workspace: %s" % str(e)) self.stop() self._buf = after
def prompt_ignore(self, ig, path, cb): ignore.create_flooignore(ig.path) dirs = ig.get_children() dirs.append(ig) dirs = sorted(dirs, key=attrgetter('size')) size = starting_size = reduce(lambda x, c: x + c.size, dirs, 0) too_big = [] while size > MAX_WORKSPACE_SIZE and dirs: cd = dirs.pop() size -= cd.size too_big.append(cd) if size > MAX_WORKSPACE_SIZE: editor.error_message( 'Maximum workspace size is %.2fMB.\n\n%s is too big (%.2fMB) to upload. Consider adding stuff to the .flooignore file.' % (MAX_WORKSPACE_SIZE / 1000000.0, path, ig.size / 1000000.0)) cb([set(), 0]) return if too_big: txt = TOO_BIG_TEXT % (MAX_WORKSPACE_SIZE / 1000000.0, path, starting_size / 1000000.0, "\n".join(set([x.path for x in too_big]))) upload = yield self.ok_cancel_dialog, txt if not upload: cb([set(), 0]) return files = set() for ig in dirs: files = files.union(set([utils.to_rel_path(x) for x in ig.files])) cb([files, size])
def on_data(self, name, data): if name == 'create_user': del data['name'] try: floorc = self.BASE_FLOORC + '\n'.join(['%s %s' % (k, v) for k, v in data.items()]) + '\n' with open(G.FLOORC_PATH, 'w') as floorc_fd: floorc_fd.write(floorc) utils.reload_settings() if False in [bool(x) for x in (G.USERNAME, G.API_KEY, G.SECRET)]: editor.error_message('Something went wrong. You will need to sign up for an account to use Floobits.') api.send_error('No username or secret') else: p = os.path.join(G.BASE_DIR, 'welcome.md') with open(p, 'w') as fd: text = editor.welcome_text % (G.USERNAME, self.proto.host) fd.write(text) d = utils.get_persistent_data() d['auto_generated_account'] = True utils.update_persistent_data(d) G.AUTO_GENERATED_ACCOUNT = True editor.open_file(p) except Exception as e: msg.debug(traceback.format_exc()) msg.error(str(e)) try: d = utils.get_persistent_data() d['disable_account_creation'] = True utils.update_persistent_data(d) finally: self.proto.stop()
def _handle(self, data): self._buf_in += data if self._handling: return self._handling = True while True: before, sep, after = self._buf_in.partition(b'\n') if not sep: break try: # Node.js sends invalid utf8 even though we're calling write(string, "utf8") # Python 2 can figure it out, but python 3 hates it and will die here with some byte sequences # Instead of crashing the plugin, we drop the data. Yes, this is horrible. before = before.decode('utf-8', 'ignore') data = json.loads(before) except Exception as e: msg.error('Unable to parse json: ', str_e(e)) msg.error('Data: ', before) # XXXX: THIS LOSES DATA self._buf_in = after continue name = data.get('name') self._buf_in = after try: msg.debug('got data ' + (name or 'no name')) self.emit('data', name, data) except Exception as e: api.send_error('Error handling %s event.' % name, str_e(e)) if name == 'room_info': editor.error_message('Error joining workspace: %s' % str_e(e)) self.stop() self._handling = False
def create_workspace(workspace_name, share_path, owner, perms=None, upload_path=None): workspace_url = 'https://%s/%s/%s' % (G.DEFAULT_HOST, G.USERNAME, workspace_name) try: api_args = { 'name': workspace_name, 'owner': owner, } if perms: api_args['perms'] = perms r = api.create_workspace(api_args) except Exception as e: return editor.error_message('Unable to create workspace %s: %s' % (workspace_url, unicode(e))) if r.code < 400: msg.debug('Created workspace %s' % workspace_url) return floobits_join_workspace(workspace_url, share_path, upload_path=upload_path) if r.code == 402: # TODO: Better behavior. Ask to create a public workspace instead? detail = r.body.get('detail') err_msg = 'Unable to create workspace because you have reached your maximum number of workspaces' if detail: err_msg += detail return editor.error_message(err_msg) if r.code == 400: workspace_name = re.sub('[^A-Za-z0-9_\-]', '-', workspace_name) workspace_name = vim_input( '%s is an invalid name. Workspace names must match the regex [A-Za-z0-9_\-]. Choose another name:' % workspace_name, workspace_name) elif r.code == 409: workspace_name = vim_input('Workspace %s already exists. Choose another name: ' % workspace_name, workspace_name + '1', 'file') else: return editor.error_message('Unable to create workspace: %s %s' % (workspace_url, unicode(e))) return create_workspace(workspace_name, share_path, perms, upload_path=upload_path)
def follow_user(self, context): users = self.agent.workspace_info.get('users') userNames = set() me = self.agent.get_username_by_id(self.agent.workspace_info['user_id']) for user in users.values(): username = user['username'] if username == me: continue if user['client'] == 'flooty': continue if 'highlight' not in user['perms']: continue userNames.add(username) if not userNames: editor.error_message("There are no other users that can be followed at this time." + "NOTE: you can only follow users who have highlight permission.") return userNames = list(userNames) userNames.sort() small = [(x in G.FOLLOW_USERS) and "unfollow" or "follow" for x in userNames] selected_user, index = yield self.user_select, context, "select a user to follow", list(userNames), small if not selected_user: return if selected_user in G.FOLLOW_USERS: G.FOLLOW_USERS.remove(selected_user) return G.FOLLOW_USERS.add(selected_user) G.AGENT.highlight(user=selected_user)
def upload(self, path): if not utils.is_shared(path): editor.error_message('Cannot share %s because is not in shared path %s.\n\nPlease move it there and try again.' % (path, G.PROJECT_PATH)) return ig = ignore.create_ignore_tree(G.PROJECT_PATH) G.IGNORE = ig is_dir = os.path.isdir(path) if ig.is_ignored(path, is_dir, True): editor.error_message('Cannot share %s because it is ignored.\n\nAdd an exclude rule (!%s) to your .flooignore file.' % (path, path)) return rel_path = utils.to_rel_path(path) if not is_dir: self._upload_file_by_path(rel_path) return for p in rel_path.split('/'): child = ig.children.get(p) if not child: break ig = child if ig.path != path: msg.warn(ig.path, ' is not the same as ', path) self._rate_limited_upload(ig.list_paths(), ig.total_size, upload_func=self._upload_file_by_path)
def follow_user(self, context): users = self.agent.workspace_info.get('users') userNames = set() me = self.agent.get_username_by_id( self.agent.workspace_info['user_id']) for user in users.values(): username = user['username'] if username == me: continue if user['client'] == 'flooty': continue if 'highlight' not in user['perms']: continue userNames.add(username) if not userNames: editor.error_message( "There are no other users that can be followed at this time. NOTE: you can only follow users who have highlight permission." ) return userNames = list(userNames) userNames.sort() small = [(x in G.FOLLOW_USERS) and "unfollow" or "follow" for x in userNames] selected_user, index = yield self.user_select, context, "select a user to follow", list( userNames), small if not selected_user: return if selected_user in G.FOLLOW_USERS: G.FOLLOW_USERS.remove(selected_user) return G.FOLLOW_USERS.add(selected_user) G.AGENT.highlight(user=selected_user)
def reconnect(self): try: api.get_workspace(self.host, 'Floobits', 'doesnotexist') except Exception as e: print(str_e(e)) editor.error_message('Something went wrong. See https://%s/help/floorc to complete the installation.' % self.host) else: editor.error_message(PORT_BLOCK_MSG % self.host) self.stop()
def floobits_check_and_join_workspace(workspace_url): try: r = api.get_workspace_by_url(workspace_url) except Exception as e: return editor.error_message('Error joining %s: %s' % (workspace_url, str(e))) if r.code >= 400: return editor.error_message('Error joining %s: %s' % (workspace_url, r.body)) msg.debug('Workspace %s exists' % workspace_url) return floobits_join_workspace(workspace_url)
def create_workspace(self, context, host, owner, name, api_args, dir_to_share): prompt = 'Workspace name: ' api_args['name'] = name api_args['owner'] = owner while True: new_name = yield self.user_charfield, context, prompt, name name = new_name or name try: api_args['name'] = name r = api.create_workspace(host, api_args) except Exception as e: msg.error('Unable to create workspace ', str_e(e)) editor.error_message('Unable to create workspace: %s' % str_e(e)) return if r.code < 400: workspace_url = 'https://%s/%s/%s' % (host, owner, name) msg.log('Created workspace ', workspace_url) self.remote_connect(context, host, owner, name, dir_to_share, utils.JOIN_ACTION.UPLOAD) return msg.error('Unable to create workspace: ', r.body) if r.code not in (400, 402, 409): try: r.body = r.body['detail'] except Exception: pass editor.error_message('Unable to create workspace: %s' % r.body) return if r.code == 402: try: r.body = r.body['detail'] except Exception: pass yes = yield self.user_y_or_n, context, '%s Open billing settings?' % r.body, "Yes" if yes: webbrowser.open('https://%s/%s/settings#billing' % (host, owner)) return if r.code == 400: # TODO: strip leading dots/dashes/etc name = re.sub('[^A-Za-z0-9_\-\.]', '_', name) prompt = 'Workspace names may only contain [A-Za-z0-9_\-\.]. Choose another name: ' continue yes = yield self.user_y_or_n, context, 'Workspace %s/%s already exists. Overwrite?' % (owner, name), 'Yes' if yes: # TODO: this doesn't set permissions on the workspace correctly self.remote_connect(context, host, owner, name, dir_to_share, utils.JOIN_ACTION.PROMPT) return prompt = 'Workspace %s/%s already exists. Choose new name: ' % (owner, name)
def reconnect(self): if self._reconnect_timeout: return self.cleanup() self._reconnect_delay = min(10000, int(1.5 * self._reconnect_delay)) if self._retries > 0: msg.log('Floobits: Reconnecting in %sms' % self._reconnect_delay) self._reconnect_timeout = utils.set_timeout(self.connect, self._reconnect_delay) elif self._retries == 0: editor.error_message('Floobits Error! Too many reconnect failures. Giving up.') self._retries -= 1
def remote_connect(self, context, host, owner, workspace, d, join_action=utils.JOIN_ACTION.PROMPT): G.PROJECT_PATH = os.path.realpath(d) try: utils.mkdir(os.path.dirname(G.PROJECT_PATH)) except Exception as e: msg.error("Couldn't create directory", G.PROJECT_PATH, str_e(e)) return auth = G.AUTH.get(host) if not auth: success = yield self.link_account, context, host if not success: return auth = G.AUTH.get(host) if not auth: msg.error("Something went really wrong.") return try: res = api.get_workspace(host, owner, workspace) if res.code == 404: msg.error("The workspace https://%s/%s/%s does not exist" % (host, owner, workspace)) return except Exception as e: message = 'Error getting workspace https://%s/%s/%s: %s' % ( host, owner, workspace, str_e(e)) msg.error(message) editor.error_message(message) return if self.agent: try: self.agent.stop() except Exception: pass G.WORKSPACE_WINDOW = yield self.get_a_window, d self.agent = self._make_agent(context, owner, workspace, auth, join_action) self.emit("agent", self.agent) reactor.reactor.connect(self.agent, host, G.DEFAULT_PORT, True) url = self.agent.workspace_url utils.add_workspace_to_persistent_json(owner, workspace, url, d) utils.update_recent_workspaces(url)
def create_workspace(self, context, host, owner, name, api_args, dir_to_share): prompt = "Workspace name: " api_args["name"] = name api_args["owner"] = owner while True: new_name = yield self.user_charfield, context, prompt, name name = new_name or name try: api_args["name"] = name r = api.create_workspace(host, api_args) except Exception as e: msg.error("Unable to create workspace ", str_e(e)) editor.error_message("Unable to create workspace: %s" % str_e(e)) return if r.code < 400: workspace_url = "https://%s/%s/%s" % (host, owner, name) msg.log("Created workspace ", workspace_url) self.remote_connect(context, host, owner, name, dir_to_share, utils.JOIN_ACTION.UPLOAD) return msg.error("Unable to create workspace: ", r.body) if r.code not in (400, 402, 409): try: r.body = r.body["detail"] except Exception: pass editor.error_message("Unable to create workspace: %s" % r.body) return if r.code == 402: try: r.body = r.body["detail"] except Exception: pass yes = yield self.user_y_or_n, context, "%s Open billing settings?" % r.body, "Yes" if yes: webbrowser.open("https://%s/%s/settings#billing" % (host, owner)) return if r.code == 400: # TODO: strip leading dots/dashes/etc name = re.sub("[^A-Za-z0-9_\-\.]", "_", name) prompt = "Workspace names may only contain [A-Za-z0-9_\-\.]. Choose another name: " continue prompt = "Workspace %s/%s already exists. Choose another name: " % (owner, name)
def prejoin_workspace(self, workspace_url, dir_to_share, api_args): try: result = utils.parse_url(workspace_url) except Exception as e: msg.error(str_e(e)) return False host = result.get('host') if not api.get_basic_auth(host): raise ValueError( 'No auth credentials for %s. Please add a username and secret for %s in your ~/.floorc.json' % (host, host)) try: w = api.get_workspace_by_url(workspace_url) except Exception as e: editor.error_message('Error opening url %s: %s' % (workspace_url, str_e(e))) return False if w.code >= 400: try: d = utils.get_persistent_data() try: del d['workspaces'][result['owner']][result['name']] except Exception: pass try: del d['recent_workspaces'][workspace_url] except Exception: pass utils.update_persistent_data(d) except Exception as e: msg.debug(str_e(e)) return False msg.debug('workspace: ', json.dumps(w.body)) anon_perms = w.body.get('perms', {}).get('AnonymousUser', []) msg.debug('api args: ', api_args) new_anon_perms = api_args.get('perms', {}).get('AnonymousUser', []) # TODO: prompt/alert user if going from private to public if set(anon_perms) != set(new_anon_perms): msg.debug(str(anon_perms), str(new_anon_perms)) w.body['perms']['AnonymousUser'] = new_anon_perms response = api.update_workspace(workspace_url, w.body) msg.debug(str(response.body)) utils.add_workspace_to_persistent_json(w.body['owner'], w.body['name'], workspace_url, dir_to_share) return result
def reconnect(self): if self._reconnect_timeout: return self.cleanup() self._reconnect_delay = min(10000, int(1.5 * self._reconnect_delay)) if self._retries > 0: msg.log('Floobits: Reconnecting in %sms' % self._reconnect_delay) self._reconnect_timeout = utils.set_timeout(self.connect, self._reconnect_delay) elif self._retries == 0: editor.error_message('Floobits Error! Too many reconnect failures. Giving up.') # Only use proxy.floobits.com if we're trying to connect to floobits.com G.OUTBOUND_FILTERING = self.host == 'floobits.com' and self._retries % 4 == 0 self._retries -= 1
def summon(self, view): buf = get_buf(view) if buf: msg.debug('summoning selection in view %s, buf id %s' % (buf['path'], buf['id'])) self.selection_changed.append((view, buf, True)) else: path = view.file_name() if not utils.is_shared(path): editor.error_message('Can\'t summon because %s is not in shared path %s.' % (path, G.PROJECT_PATH)) return share = editor.ok_cancel_dialog('This file isn\'t shared. Would you like to share it?', 'Share') if share: sel = [[x.a, x.b] for x in view.sel()] self.create_buf_cbs[utils.to_rel_path(path)] = lambda buf_id: send_summon(buf_id, sel) self.upload(path)
def prejoin_workspace(self, workspace_url, dir_to_share, api_args): try: result = utils.parse_url(workspace_url) except Exception as e: msg.error(str_e(e)) return False host = result.get("host") if not api.get_basic_auth(host): raise ValueError( "No auth credentials for %s. Please add a username and secret for %s in your ~/.floorc.json" % (host, host) ) try: w = api.get_workspace_by_url(workspace_url) except Exception as e: editor.error_message("Error opening url %s: %s" % (workspace_url, str_e(e))) return False if w.code >= 400: try: d = utils.get_persistent_data() try: del d["workspaces"][result["owner"]][result["name"]] except Exception: pass try: del d["recent_workspaces"][workspace_url] except Exception: pass utils.update_persistent_data(d) except Exception as e: msg.debug(str_e(e)) return False msg.debug("workspace: ", json.dumps(w.body)) anon_perms = w.body.get("perms", {}).get("AnonymousUser", []) msg.debug("api args: ", api_args) new_anon_perms = api_args.get("perms", {}).get("AnonymousUser", []) # TODO: prompt/alert user if going from private to public if set(anon_perms) != set(new_anon_perms): msg.debug(str(anon_perms), str(new_anon_perms)) w.body["perms"]["AnonymousUser"] = new_anon_perms response = api.update_workspace(workspace_url, w.body) msg.debug(str(response.body)) utils.add_workspace_to_persistent_json(w.body["owner"], w.body["name"], workspace_url, dir_to_share) return result
def join_workspace_by_url(self, context, workspace_url, possible_dirs=None): try: d = utils.parse_url(workspace_url) except Exception as e: return editor.error_message(str_e(e)) return self.join_workspace(context, d['host'], d['workspace'], d['owner'], possible_dirs)
def on_data(self, name, data): if name == 'credentials': with open(G.FLOORC_PATH, 'w') as floorc_fd: floorc = self.BASE_FLOORC + '\n'.join(['%s %s' % (k, v) for k, v in data['credentials'].items()]) + '\n' floorc_fd.write(floorc) utils.reload_settings() if not G.USERNAME or not G.SECRET: editor.error_message('Something went wrong. See https://%s/help/floorc to complete the installation.' % self.proto.host) api.send_error('No username or secret') else: p = os.path.join(G.BASE_DIR, 'welcome.md') with open(p, 'w') as fd: text = WELCOME_MSG % (G.USERNAME, self.proto.host) fd.write(text) editor.open_file(p) self.proto.stop()
def create_workspace(workspace_name, share_path, owner, perms=None, upload_path=None): workspace_url = 'https://%s/%s/%s' % (G.DEFAULT_HOST, G.USERNAME, workspace_name) try: api_args = { 'name': workspace_name, 'owner': owner, } if perms: api_args['perms'] = perms r = api.create_workspace(api_args) except Exception as e: return editor.error_message('Unable to create workspace %s: %s' % (workspace_url, unicode(e))) if r.code < 400: msg.debug('Created workspace %s' % workspace_url) return floobits_join_workspace(workspace_url, share_path, upload_path=upload_path) if r.code == 402: # TODO: Better behavior. Ask to create a public workspace instead? return editor.error_message( 'Unable to create workspace because you have reached your maximum number of workspaces' ) if r.code == 400: workspace_name = re.sub('[^A-Za-z0-9_\-]', '-', workspace_name) workspace_name = vim_input( '%s is an invalid name. Workspace names must match the regex [A-Za-z0-9_\-]. Choose another name:' % workspace_name, workspace_name) elif r.code == 409: workspace_name = vim_input( 'Workspace %s already exists. Choose another name: ' % workspace_name, workspace_name + '1', 'file') else: return editor.error_message('Unable to create workspace: %s %s' % (workspace_url, unicode(e))) return create_workspace(workspace_name, share_path, perms, upload_path=upload_path)
def reconnect(self): if self._reconnect_timeout: return self.cleanup() self._reconnect_delay = min(10000, int(1.5 * self._reconnect_delay)) if self._retries > 0: msg.log('Floobits: Reconnecting in %sms' % self._reconnect_delay) self._reconnect_timeout = utils.set_timeout( self.connect, self._reconnect_delay) elif self._retries == 0: editor.error_message( 'Floobits Error! Too many reconnect failures. Giving up.') # Only use proxy.floobits.com if we're trying to connect to floobits.com G.OUTBOUND_FILTERING = self.host == 'floobits.com' and self._retries % 4 == 0 self._retries -= 1
def upload(self, path, cb=None): ig = ignore.Ignore(None, path) if ig.size > MAX_WORKSPACE_SIZE: size = ig.size child_dirs = sorted(ig.children, key=attrgetter("size")) ignored_cds = [] while size > MAX_WORKSPACE_SIZE and child_dirs: cd = child_dirs.pop() ignored_cds.append(cd) size -= cd.size if size > MAX_WORKSPACE_SIZE: editor.error_message("Maximum workspace size is %.2fMB.\n\n%s is too big (%.2fMB) to upload. Consider adding stuff to the .flooignore file." % (MAX_WORKSPACE_SIZE / 1000000.0, path, ig.size / 1000000.0)) return upload = yield self.ok_cancel_dialog, "Maximum workspace size is %.2fMB.\n\n%s is too big (%.2fMB) to upload.\n\nWould you like to ignore the following and continue?\n\n%s" % \ (MAX_WORKSPACE_SIZE / 1000000.0, path, ig.size / 1000000.0, "\n".join([x.path for x in ignored_cds])) if not upload: return ig.children = child_dirs self._uploader(ig.list_paths(), cb, ig.size)
def on_data(self, name, data): if name == 'credentials': s = utils.load_floorc_json() auth = s.get('AUTH', {}) auth[self.proto.host] = data['credentials'] s['AUTH'] = auth utils.save_floorc_json(s) utils.reload_settings() self.success = utils.can_auth(self.proto.host) if not self.success: editor.error_message('Something went wrong. See https://%s/help/floorc to complete the installation.' % self.proto.host) api.send_error('No username or secret') else: p = os.path.join(G.BASE_DIR, 'welcome.md') with open(p, 'w') as fd: text = WELCOME_MSG % (G.AUTH.get(self.proto.host, {}).get('username'), self.proto.host) fd.write(text) editor.open_file(p) self.stop()
def remote_connect(self, context, host, owner, workspace, d, join_action=utils.JOIN_ACTION.PROMPT): G.PROJECT_PATH = os.path.realpath(d) try: utils.mkdir(os.path.dirname(G.PROJECT_PATH)) except Exception as e: msg.error("Couldn't create directory", G.PROJECT_PATH, str_e(e)) return auth = G.AUTH.get(host) if not auth: success = yield self.link_account, context, host if not success: return auth = G.AUTH.get(host) if not auth: msg.error("Something went really wrong.") return try: res = api.get_workspace(host, owner, workspace) if res.code == 404: msg.error("The workspace https://%s/%s/%s does not exist" % (host, owner, workspace)) return except Exception as e: message = 'Error getting workspace https://%s/%s/%s: %s' % (host, owner, workspace, str_e(e)) msg.error(message) editor.error_message(message) return if self.agent: try: self.agent.stop() except Exception: pass G.WORKSPACE_WINDOW = yield self.get_a_window, d self.agent = self._make_agent(context, owner, workspace, auth, join_action) self.emit("agent", self.agent) reactor.reactor.connect(self.agent, host, G.DEFAULT_PORT, True) url = self.agent.workspace_url utils.add_workspace_to_persistent_json(owner, workspace, url, d) utils.update_recent_workspaces(url)
def on_data(self, name, data): if name == 'credentials': with open(G.FLOORC_PATH, 'w') as floorc_fd: floorc = self.BASE_FLOORC + '\n'.join( ['%s %s' % (k, v) for k, v in data['credentials'].items()]) + '\n' floorc_fd.write(floorc) utils.reload_settings() if not G.USERNAME or not G.SECRET: editor.error_message( 'Something went wrong. See https://%s/help/floorc to complete the installation.' % self.proto.host) api.send_error('No username or secret') else: p = os.path.join(G.BASE_DIR, 'welcome.md') with open(p, 'w') as fd: text = WELCOME_MSG % (G.USERNAME, self.proto.host) fd.write(text) editor.open_file(p) self.proto.stop()
def _do_ssl_handshake(self): try: sock_debug('Doing SSL handshake') self._sock.do_handshake() except ssl.SSLError as e: sock_debug('Floobits: ssl.SSLError. This is expected sometimes.') if e.args[0] in [ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE]: return False self.stop() editor.error_message('Floobits SSL handshake error: %s' % str(e)) sock_debug('SSLError args: %s' % ''.join([str(a) for a in e.args])) except Exception as e: msg.error('Error in SSL handshake: ', str_e(e)) else: sock_debug('Successful handshake') self._needs_handshake = False editor.status_message('%s:%s: SSL handshake completed' % (self.host, self.port)) return True self.reconnect() return False
def _on_credentials(self, data): s = utils.load_floorc_json() auth = s.get('AUTH', {}) auth[self.proto.host] = data['credentials'] s['AUTH'] = auth utils.save_floorc_json(s) utils.reload_settings() self.success = utils.can_auth(self.proto.host) if not self.success: editor.error_message('Something went wrong. See https://%s/help/floorc to complete the installation.' % self.proto.host) api.send_error('No username or secret') else: p = os.path.join(G.BASE_DIR, 'welcome.md') with open(p, 'w') as fd: username = G.AUTH.get(self.proto.host, {}).get('username') text = editor.LINKED_ACCOUNT_TXT.format(username=username, host=self.proto.host) fd.write(text) editor.open_file(p) try: self.stop() except Exception as e: print(str_e(e))
def on_data(self, name, data): if name == 'credentials': s = utils.load_floorc_json() auth = s.get('AUTH', {}) auth[self.proto.host] = data['credentials'] s['AUTH'] = auth utils.save_floorc_json(s) utils.reload_settings() self.success = utils.can_auth(self.proto.host) if not self.success: editor.error_message( 'Something went wrong. See https://%s/help/floorc to complete the installation.' % self.proto.host) api.send_error('No username or secret') else: p = os.path.join(G.BASE_DIR, 'welcome.md') with open(p, 'w') as fd: text = WELCOME_MSG % (G.AUTH.get( self.proto.host, {}).get('username'), self.proto.host) fd.write(text) editor.open_file(p) self.stop()
def _do_ssl_handshake(self): try: sock_debug('Doing SSL handshake') self._sock.do_handshake() except ssl.SSLError as e: sock_debug('Floobits: ssl.SSLError. This is expected sometimes.') if e.args[0] in [ ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE ]: return False self.stop() editor.error_message('Floobits SSL handshake error: %s' % str(e)) sock_debug('SSLError args: %s' % ''.join([str(a) for a in e.args])) except Exception as e: msg.error('Error in SSL handshake: ', str_e(e)) else: sock_debug('Successful handshake') self._needs_handshake = False editor.status_message('%s:%s: SSL handshake completed' % (self.host, self.port)) return True self.reconnect() return False
def reconnect(self): try: api.get_workspace(self.host, 'Floobits', 'doesnotexist') except Exception as e: print(str_e(e)) editor.error_message('Something went wrong. See https://%s/help/floorc to complete the installation.' % self.host) else: if G.OUTBOUND_FILTERING: editor.error_message('Something went wrong. See https://%s/help/floorc to complete the installation.' % self.host) return self.stop() if self.host == 'floobits.com': G.OUTBOUND_FILTERING = True return self.connect() editor.error_message(PORT_BLOCK_MSG % self.host) self.stop()
def delete_workspace(self, context, cb): host = yield self._get_host, context if not host: cb() return api_url = 'https://%s/api/workspaces/can/admin' % (host) try: r = api.api_request(host, api_url) except IOError as e: editor.error_message('Error getting workspaces can admin %s' % str_e(e)) cb() return if r.code >= 400: editor.error_message('Error getting workspace list: %s' % r.body) cb() return choices = [ '%s/%s' % (workspace['owner'], workspace['name']) for workspace in r.body ] (workspace, index) = yield self.user_select, context, 'Select workpace to delete', choices, [] if not workspace: cb() return if G.EXPERT_MODE: yes = True else: yes = yield self.user_y_or_n, context, 'Really delete %s?' % workspace, 'Yes' if not yes: cb() return workspace = r.body[index] try: api.delete_workspace(host, workspace['owner'], workspace['name']) except IOError as e: editor.error_message('Error deleting workspace' % str_e(e)) cb()
def reconnect(self): try: api.get_workspace(self.host, 'Floobits', 'doesnotexist') except Exception as e: print(str_e(e)) editor.error_message( 'Something went wrong. See https://%s/help/floorc to complete the installation.' % self.host) else: if G.OUTBOUND_FILTERING: editor.error_message( 'Something went wrong. See https://%s/help/floorc to complete the installation.' % self.host) return self.stop() if self.host == 'floobits.com': G.OUTBOUND_FILTERING = True return self.connect() editor.error_message(PORT_BLOCK_MSG % self.host) self.stop()
def delete_workspace(self, context, cb): host = yield self._get_host, context if not host: cb() return api_url = "https://%s/api/workspaces/can/admin" % (host) try: r = api.api_request(host, api_url) except IOError as e: editor.error_message("Error getting workspaces can admin %s" % str_e(e)) cb() return if r.code >= 400: editor.error_message("Error getting workspace list: %s" % r.body) cb() return choices = ["%s/%s" % (workspace["owner"], workspace["name"]) for workspace in r.body] (workspace, index) = yield self.user_select, context, "Select workpace to delete", choices, [] if not workspace: cb() return if G.EXPERT_MODE: yes = True else: yes = yield self.user_y_or_n, context, "Really delete %s?" % workspace, "Yes" if not yes: cb() return workspace = r.body[index] try: api.delete_workspace(host, workspace["owner"], workspace["name"]) except IOError as e: editor.error_message("Error deleting workspace" % str_e(e)) cb()
def delete_workspace(self, context, cb): host = yield self._get_host, context if not host: cb() return api_url = 'https://%s/api/workspaces/can/admin' % (host) try: r = api.api_request(host, api_url) except IOError as e: editor.error_message('Error getting workspaces can admin %s' % str_e(e)) cb() return if r.code >= 400: editor.error_message('Error getting workspace list: %s' % r.body) cb() return choices = ['%s/%s' % (workspace['owner'], workspace['name']) for workspace in r.body] (workspace, index) = yield self.user_select, context, 'Select workpace to delete', choices, [] if not workspace: cb() return if G.EXPERT_MODE: yes = True else: yes = yield self.user_y_or_n, context, 'Really delete %s?' % workspace, 'Yes' if not yes: cb() return workspace = r.body[index] try: api.delete_workspace(host, workspace['owner'], workspace['name']) except IOError as e: editor.error_message('Error deleting workspace' % str_e(e)) cb()
def share_dir(self, context, dir_to_share, api_args): utils.reload_settings() if not utils.can_auth(): success = yield self.create_or_link_account, context, G.DEFAULT_HOST, False if not success: return utils.reload_settings() dir_to_share = os.path.expanduser(dir_to_share) dir_to_share = os.path.realpath(dir_to_share) dir_to_share = utils.unfuck_path(dir_to_share) if os.path.isfile(dir_to_share): dir_to_share = os.path.dirname(dir_to_share) workspace_name = os.path.basename(dir_to_share) msg.debug('', workspace_name, dir_to_share) if os.path.isfile(dir_to_share): dir_to_share = os.path.dirname(dir_to_share) try: utils.mkdir(dir_to_share) except Exception: msg.error("The directory", dir_to_share, "doesn't exist and I can't create it.") return info = utils.read_floo_file(dir_to_share) def prejoin(workspace_url): try: return self.prejoin_workspace(workspace_url, dir_to_share, api_args) except ValueError: pass workspace_url = info.get('url') if workspace_url: parsed_url = prejoin(workspace_url) if parsed_url: self.remote_connect(context, parsed_url['host'], parsed_url['owner'], parsed_url['workspace'], dir_to_share) return parsed_url = utils.get_workspace_by_path(dir_to_share, prejoin) if parsed_url: self.remote_connect(context, parsed_url['host'], parsed_url['owner'], parsed_url['workspace'], dir_to_share) return host = yield self._get_host, context if not host: return try: r = api.get_orgs_can_admin(host) except IOError as e: editor.error_message('Error getting org list: %s' % str_e(e)) return choices = [G.AUTH[host]['username']] if r.code >= 400: editor.error_message('Error getting org list: %s' % r.body) elif r.body: choices += [org['name'] for org in r.body] if len(choices) == 1: owner = choices[0] else: little = ['Create workspace owned by %s' % s for s in choices] ( owner, index ) = yield self.user_select, context, 'Create workspace owned by', choices, little if not owner: return self.create_workspace(context, host, owner, workspace_name, api_args, dir_to_share)
def share_dir(self, context, dir_to_share, api_args): utils.reload_settings() if not utils.can_auth(): success = yield self.create_or_link_account, context, G.DEFAULT_HOST, False if not success: return utils.reload_settings() dir_to_share = os.path.expanduser(dir_to_share) dir_to_share = os.path.realpath(dir_to_share) dir_to_share = utils.unfuck_path(dir_to_share) if os.path.isfile(dir_to_share): dir_to_share = os.path.dirname(dir_to_share) workspace_name = os.path.basename(dir_to_share) msg.debug('', workspace_name, dir_to_share) if os.path.isfile(dir_to_share): dir_to_share = os.path.dirname(dir_to_share) try: utils.mkdir(dir_to_share) except Exception: msg.error("The directory", dir_to_share, "doesn't exist and I can't create it.") return info = utils.read_floo_file(dir_to_share) def prejoin(workspace_url): try: return self.prejoin_workspace(workspace_url, dir_to_share, api_args) except ValueError: pass workspace_url = info.get('url') if workspace_url: parsed_url = prejoin(workspace_url) if parsed_url: self.remote_connect(context, parsed_url['host'], parsed_url['owner'], parsed_url['workspace'], dir_to_share) return parsed_url = utils.get_workspace_by_path(dir_to_share, prejoin) if parsed_url: self.remote_connect(context, parsed_url['host'], parsed_url['owner'], parsed_url['workspace'], dir_to_share) return host = yield self._get_host, context if not host: return try: r = api.get_orgs_can_admin(host) except IOError as e: editor.error_message('Error getting org list: %s' % str_e(e)) return choices = [G.AUTH[host]['username']] if r.code >= 400: editor.error_message('Error getting org list: %s' % r.body) elif r.body: choices += [org['name'] for org in r.body] if len(choices) == 1: owner = choices[0] else: little = ['Create workspace owned by %s' % s for s in choices] (owner, index) = yield self.user_select, context, 'Create workspace owned by', choices, little if not owner: return self.create_workspace(context, host, owner, workspace_name, api_args, dir_to_share)
def _on_error(self, data): message = 'Error from server! Message: %s' % str(data.get('msg')) msg.error(message) if data.get('flash'): editor.error_message('Error from Floobits server: %s' % str(data.get('msg')))
def _on_room_info(self, data): self.joined_workspace = True self.workspace_info = data G.PERMS = data['perms'] self.proto.reset_retries() if G.OUTBOUND_FILTERING: msg.error( 'Detected outbound port blocking! See https://floobits.com/help/network for more info.' ) read_only = False if 'patch' not in data['perms']: read_only = True no_perms_msg = '''You don't have permission to edit this workspace. All files will be read-only.''' msg.log('No patch permission. Setting buffers to read-only') if 'request_perm' in data['perms']: should_send = yield self.ok_cancel_dialog, no_perms_msg + '\nDo you want to request edit permission?' # TODO: wait for perms to be OK'd/denied before uploading or bailing if should_send: self.send({ 'name': 'request_perms', 'perms': ['edit_room'] }) else: if G.EXPERT_MODE: editor.status_message(no_perms_msg) else: editor.error_message(no_perms_msg) floo_json = { 'url': utils.to_workspace_url({ 'owner': self.owner, 'workspace': self.workspace, 'host': self.proto.host, 'port': self.proto.port, 'secure': self.proto.secure, }) } utils.update_floo_file(os.path.join(G.PROJECT_PATH, '.floo'), floo_json) utils.update_recent_workspaces(self.workspace_url) changed_bufs = [] missing_bufs = [] new_files = set() ig = ignore.create_ignore_tree(G.PROJECT_PATH) G.IGNORE = ig if not read_only: new_files = set([utils.to_rel_path(x) for x in ig.list_paths()]) 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) self.bufs[buf_id] = buf self.paths_to_ids[buf['path']] = buf_id view = self.get_view(buf_id) if view and not view.is_loading() and buf['encoding'] == 'utf8': view_text = view.get_text() view_md5 = hashlib.md5(view_text.encode('utf-8')).hexdigest() buf['buf'] = view_text buf['view'] = view G.VIEW_TO_HASH[view.native_id] = view_md5 if view_md5 == buf['md5']: msg.debug('md5 sum matches view. not getting buffer ', buf['path']) else: changed_bufs.append(buf) buf['md5'] = view_md5 continue try: if buf['encoding'] == 'utf8': if io: buf_fd = io.open(buf_path, 'Urt', encoding='utf8') buf_buf = buf_fd.read() else: buf_fd = open(buf_path, 'rb') buf_buf = buf_fd.read().decode('utf-8').replace( '\r\n', '\n') md5 = hashlib.md5(buf_buf.encode('utf-8')).hexdigest() else: buf_fd = open(buf_path, 'rb') buf_buf = buf_fd.read() md5 = hashlib.md5(buf_buf).hexdigest() buf_fd.close() buf['buf'] = buf_buf if md5 == buf['md5']: msg.debug('md5 sum matches. not getting buffer ', buf['path']) else: msg.debug('md5 differs. possibly getting buffer later ', buf['path']) changed_bufs.append(buf) buf['md5'] = md5 except Exception as e: msg.debug('Error calculating md5 for ', buf['path'], ', ', str_e(e)) missing_bufs.append(buf) ignored = [] for p, buf_id in self.paths_to_ids.items(): if p not in new_files: ignored.append(p) new_files.discard(p) if self.action == utils.JOIN_ACTION.UPLOAD: yield self._initial_upload, ig, missing_bufs, changed_bufs # TODO: maybe use org name here who = 'Your friends' anon_perms = G.AGENT.workspace_info.get('anon_perms') if 'get_buf' in anon_perms: who = 'Anyone' _msg = 'You are sharing:\n\n%s\n\n%s can join your workspace at:\n\n%s' % ( G.PROJECT_PATH, who, G.AGENT.workspace_url) # Workaround for horrible Sublime Text bug utils.set_timeout(editor.message_dialog, 0, _msg) elif changed_bufs or missing_bufs or new_files: # TODO: handle readonly here if self.action == utils.JOIN_ACTION.PROMPT: stomp_local = yield self.stomp_prompt, changed_bufs, missing_bufs, list( new_files), ignored if stomp_local not in [0, 1]: self.stop() return elif self.action == utils.JOIN_ACTION.DOWNLOAD: stomp_local = True else: # This should never happen assert False return if stomp_local: for buf in changed_bufs: self.get_buf(buf['id'], buf.get('view')) self.save_on_get_bufs.add(buf['id']) for buf in missing_bufs: self.get_buf(buf['id'], buf.get('view')) self.save_on_get_bufs.add(buf['id']) else: yield self._initial_upload, ig, missing_bufs, changed_bufs success_msg = '%s@%s/%s: Joined!' % (self.username, self.owner, self.workspace) msg.log(success_msg) editor.status_message(success_msg) data = utils.get_persistent_data() data['recent_workspaces'].insert(0, {"url": self.workspace_url}) utils.update_persistent_data(data) utils.add_workspace_to_persistent_json(self.owner, self.workspace, self.workspace_url, G.PROJECT_PATH) 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) self.emit("room_info")
def _on_disconnect(self, data): message = 'Disconnected from server! Reason: %s' % str( data.get('reason')) msg.error(message) editor.error_message(message) self.stop()
def _on_room_info(self, data): self.reset() self.joined_workspace = True self.workspace_info = data G.PERMS = data['perms'] if 'patch' not in data['perms']: no_perms_msg = '''You don't have permission to edit this workspace. All files will be read-only.''' msg.log('No patch permission. Setting buffers to read-only') if 'request_perm' in data['perms']: should_send = yield self.ok_cancel_dialog, no_perms_msg + '\nDo you want to request edit permission?' if should_send: self.send({ 'name': 'request_perms', 'perms': ['edit_room'] }) else: editor.error_message(no_perms_msg) floo_json = { 'url': utils.to_workspace_url({ 'owner': self.owner, 'workspace': self.workspace, 'host': self.proto.host, 'port': self.proto.port, 'secure': self.proto.secure, }) } utils.update_floo_file(os.path.join(G.PROJECT_PATH, '.floo'), floo_json) changed_bufs = [] missing_bufs = [] 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) self.bufs[buf_id] = buf self.paths_to_ids[buf['path']] = buf_id view = self.get_view(buf_id) if view and not view.is_loading() and buf['encoding'] == 'utf8': view_text = view.get_text() view_md5 = hashlib.md5(view_text.encode('utf-8')).hexdigest() buf['buf'] = view_text buf['view'] = view G.VIEW_TO_HASH[view.native_id] = view_md5 if view_md5 == buf['md5']: msg.debug('md5 sum matches view. not getting buffer %s' % buf['path']) else: changed_bufs.append(buf_id) buf['md5'] = view_md5 else: try: if buf['encoding'] == "utf8": if io: buf_fd = io.open(buf_path, 'Urt', encoding='utf8') buf_buf = buf_fd.read() else: buf_fd = open(buf_path, 'rb') buf_buf = buf_fd.read().decode('utf-8').replace( '\r\n', '\n') md5 = hashlib.md5(buf_buf.encode('utf-8')).hexdigest() else: buf_fd = open(buf_path, 'rb') buf_buf = buf_fd.read() md5 = hashlib.md5(buf_buf).hexdigest() buf_fd.close() buf['buf'] = buf_buf if md5 == buf['md5']: msg.debug('md5 sum matches. not getting buffer %s' % buf['path']) else: msg.debug( 'md5 differs. possibly getting buffer later %s' % buf['path']) changed_bufs.append(buf_id) buf['md5'] = md5 except Exception as e: msg.debug('Error calculating md5 for %s, %s' % (buf['path'], e)) missing_bufs.append(buf_id) stomp_local = self.should_get_bufs if stomp_local and (changed_bufs or missing_bufs): changed = [self.bufs[buf_id] for buf_id in changed_bufs] missing = [self.bufs[buf_id] for buf_id in missing_bufs] choice = yield self.stomp_prompt, changed, missing if choice not in [0, 1]: self.stop() return stomp_local = bool(choice) for buf_id in changed_bufs: buf = self.bufs[buf_id] if stomp_local: self.get_buf(buf_id, buf.get('view')) self.save_on_get_bufs.add(buf_id) else: self._upload(utils.get_full_path(buf['path']), buf['buf']) for buf_id in missing_bufs: buf = self.bufs[buf_id] if stomp_local: self.get_buf(buf_id, buf.get('view')) self.save_on_get_bufs.add(buf_id) else: self.send({ 'name': 'delete_buf', 'id': buf['id'], }) success_msg = 'Successfully joined workspace %s/%s' % (self.owner, self.workspace) msg.log(success_msg) editor.status_message(success_msg) 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) data = utils.get_persistent_data() data['recent_workspaces'].insert(0, {"url": self.workspace_url}) utils.update_persistent_data(data) utils.add_workspace_to_persistent_json(self.owner, self.workspace, self.workspace_url, G.PROJECT_PATH) self.emit("room_info")
def _on_disconnect(self, data): message = 'Disconnected from server! Reason: %s' % str(data.get('reason')) msg.error(message) editor.error_message(message) self.proto.stop()
def _on_room_info(self, data): self.joined_workspace = True self.workspace_info = data G.PERMS = data['perms'] self.proto.reset_retries() if G.OUTBOUND_FILTERING: msg.error( 'Detected outbound port blocking! See https://floobits.com/help/network for more info.' ) read_only = False if 'patch' not in data['perms']: read_only = True no_perms_msg = '''You don't have permission to edit this workspace. All files will be read-only.''' msg.log('No patch permission. Setting buffers to read-only') if 'request_perm' in data['perms']: should_send = yield self.ok_cancel_dialog, no_perms_msg + '\nDo you want to request edit permission?' # TODO: wait for perms to be OK'd/denied before uploading or bailing if should_send: self.send({ 'name': 'request_perms', 'perms': ['edit_room'] }) else: if G.EXPERT_MODE: editor.status_message(no_perms_msg) else: editor.error_message(no_perms_msg) floo_json = { 'url': utils.to_workspace_url({ 'owner': self.owner, 'workspace': self.workspace, 'host': self.proto.host, 'port': self.proto.port, 'secure': self.proto.secure, }) } utils.update_floo_file(os.path.join(G.PROJECT_PATH, '.floo'), floo_json) utils.update_recent_workspaces(self.workspace_url) ig = ignore.create_ignore_tree(G.PROJECT_PATH) G.IGNORE = ig for buf_id, buf in data['bufs'].items(): buf_id = int(buf_id) # json keys must be strings self.bufs[buf_id] = buf self.paths_to_ids[buf['path']] = buf_id changed_bufs, missing_bufs, new_files = self._scan_dir( data['bufs'], ig, read_only) ignored = [] for p, buf_id in self.paths_to_ids.items(): if p not in new_files: ignored.append(p) new_files.discard(p) if self.action == utils.JOIN_ACTION.UPLOAD: yield self._initial_upload, ig, missing_bufs, changed_bufs # TODO: maybe use org name here who = 'Your friends' anon_perms = G.AGENT.workspace_info.get('anon_perms') if 'get_buf' in anon_perms: who = 'Anyone' _msg = 'You are sharing:\n\n%s\n\n%s can join your workspace at:\n\n%s' % ( G.PROJECT_PATH, who, G.AGENT.workspace_url) # Workaround for horrible Sublime Text bug utils.set_timeout(editor.message_dialog, 0, _msg) # Don't auto-upload again on reconnect self.action = utils.JOIN_ACTION.PROMPT elif changed_bufs or missing_bufs or new_files: # TODO: handle readonly here if self.action == utils.JOIN_ACTION.PROMPT: stomp_local = yield self.stomp_prompt, changed_bufs, missing_bufs, list( new_files), ignored if stomp_local not in [0, 1]: self.stop() return elif self.action == utils.JOIN_ACTION.DOWNLOAD: stomp_local = True else: # This should never happen assert False return if stomp_local: for buf in changed_bufs: self.get_buf(buf['id'], buf.get('view')) self.save_on_get_bufs.add(buf['id']) for buf in missing_bufs: self.get_buf(buf['id'], buf.get('view')) self.save_on_get_bufs.add(buf['id']) else: yield self._initial_upload, ig, missing_bufs, changed_bufs success_msg = '%s@%s/%s: Joined!' % (self.username, self.owner, self.workspace) msg.log(success_msg) editor.status_message(success_msg) data = utils.get_persistent_data() data['recent_workspaces'].insert(0, {"url": self.workspace_url}) utils.update_persistent_data(data) utils.add_workspace_to_persistent_json(self.owner, self.workspace, self.workspace_url, G.PROJECT_PATH) 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 data.get('repo_info'): msg.log('Repo info:', data.get('repo_info')) # TODO: check local repo info and update remote (or prompt?) else: repo_info = repo.get_info(self.workspace_url, G.PROJECT_PATH) if repo_info and 'repo' in G.PERMS: self.send({ 'name': 'repo', 'action': 'set', 'data': repo_info, }) self.emit("room_info")