Example #1
0
 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')
Example #2
0
 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')
Example #4
0
 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()
Example #5
0
 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])
Example #6
0
 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()
Example #7
0
    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])
Example #10
0
 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()
Example #11
0
    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
Example #12
0
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)
Example #13
0
    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
Example #14
0
    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)
Example #15
0
    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)
Example #16
0
    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()
Example #18
0
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)
Example #19
0
    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)
Example #20
0
    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)
Example #21
0
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 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
Example #23
0
    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)
Example #24
0
    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)
Example #25
0
    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
Example #26
0
    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
Example #27
0
 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)
Example #28
0
 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)
Example #29
0
    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
Example #30
0
    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()
Example #32
0
    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)
Example #33
0
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)
Example #34
0
    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
Example #35
0
 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()
Example #37
0
    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)
Example #38
0
 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()
Example #39
0
    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
Example #40
0
 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()
Example #42
0
    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
Example #43
0
 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()
Example #44
0
    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()
Example #45
0
 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()
Example #46
0
    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()
Example #47
0
    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()
Example #48
0
    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)
Example #49
0
    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)
Example #50
0
 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')))
Example #51
0
    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")
Example #52
0
 def _on_disconnect(self, data):
     message = 'Disconnected from server! Reason: %s' % str(
         data.get('reason'))
     msg.error(message)
     editor.error_message(message)
     self.stop()
Example #53
0
    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_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')))
Example #56
0
    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")