Beispiel #1
0
def _account_mod(login_name, ext, settings):
    """Modify a KBasix user account.

       _account_mod(login_name, ext, settings)

    The 'ext' can be 'profile' or 'prefs' depending if a user's
    profile information or preferences are being changed. The
    'settings' is a dictionary. Nothing is returned.
    """
    (OK, status) = _check_args(locals())
    if not OK:
        raise AccountModError(status)
    uid = str(_info(login_name)['uid'])
    f = os.path.join(users_root_dir_, uid, uid + '.%s' % ext)
    try:
        extfile = _read_file(f)
    except Exception as reason:
        raise AccountModError(reason)
    for key in settings:
        extfile[key] = settings[key]
    try:
        _save_file(extfile, f)
    except Exception as reason:
        raise AccountModError(reason)
    return
Beispiel #2
0
def _account_mod(login_name, ext, settings):
    """Modify a KBasix user account.

       _account_mod(login_name, ext, settings)

    The 'ext' can be 'profile' or 'prefs' depending if a user's
    profile information or preferences are being changed. The
    'settings' is a dictionary. Nothing is returned.
    """
    (OK, status) = _check_args(locals())
    if not OK:
        raise AccountModError(status)
    uid = str(_info(login_name)['uid'])
    f = os.path.join(users_root_dir_, uid, uid + '.%s' % ext)
    try:
        extfile = _read_file(f)
    except Exception as reason:
        raise AccountModError(reason)
    for key in settings:
        extfile[key] = settings[key]
    try:
        _save_file(extfile, f)
    except Exception as reason:
        raise AccountModError(reason)
    return
Beispiel #3
0
def _finger(login_name):
    """Interactively retrieve information about a user account.

       info = _finger(login_name)

    The return type may vary, should only be used interactively.
    """
    (OK, status) = _check_args(locals())
    if not OK:
        raise FingerError(status)
    import pprint
    user_info = _info(login_name)
    if not user_info:
        return 'User "%s" not found.' % login_name
    uid = str(user_info['uid'])
    f = os.path.join(users_root_dir_, uid, uid + '.%s' % 'profile')
    try:
        return pprint.pprint(_read_file(f, lock=False))
    except Exception as reason:
        raise FingerError(reason)
Beispiel #4
0
def _finger(login_name):
    """Interactively retrieve information about a user account.

       info = _finger(login_name)

    The return type may vary, should only be used interactively.
    """
    (OK, status) = _check_args(locals())
    if not OK:
        raise FingerError(status)
    import pprint
    user_info = _info(login_name)
    if not user_info:
        return 'User "%s" not found.' % login_name
    uid = str(user_info['uid'])
    f = os.path.join(users_root_dir_, uid, uid + '.%s' % 'profile')
    try:
        return pprint.pprint(_read_file(f, lock=False))
    except Exception as reason:
        raise FingerError(reason)
Beispiel #5
0
def _get_file_info(file_tag, info):
    """Read the file metadata.

       file_info = _get_file_info(file_tag, info)

    Returns a dictionary with the file metadata (an empty one if
    either the metadata or content file isn't there).
    """
    import logging
    import os
    import manage_users
    logging.debug('Getting file metadata for "%s" (%s)' % \
                      (file_tag, info['login_name']))
    _check_file_tag(file_tag, info['login_name'])
    the_file = os.path.join(info['users_root_dir_'], str(info['uid']), \
                                file_tag)
    the_id_file = the_file + '-id'
    if not os.path.exists(the_id_file) or not os.path.exists(the_file):
        logging.warn('Unable to retrieve file metadata and/or content \
for "%s" (%s)' % (the_id_file, info['login_name']))
        return {}
    return manage_users._read_file(the_id_file, lock=False)
Beispiel #6
0
def _get_file_info(file_tag, info):
    """Read the file metadata.

       file_info = _get_file_info(file_tag, info)

    Returns a dictionary with the file metadata (an empty one if
    either the metadata or content file isn't there).
    """
    import logging
    import os
    import manage_users
    logging.debug('Getting file metadata for "%s" (%s)' % \
                      (file_tag, info['login_name']))
    _check_file_tag(file_tag, info['login_name'])
    the_file = os.path.join(info['users_root_dir_'], str(info['uid']), \
                                file_tag)
    the_id_file = the_file + '-id'
    if not os.path.exists(the_id_file) or not os.path.exists(the_file):
        logging.warn('Unable to retrieve file metadata and/or content \
for "%s" (%s)' % (the_id_file, info['login_name']))
        return {}
    return manage_users._read_file(the_id_file, lock=False)
Beispiel #7
0
def _account_info(login_name, ext):
    """Retrieve information about a user account.

       info = _account_info(login_name, ext)

    The 'ext' can be 'profile' or 'prefs' depending if a user's
    profile information or preferences are being accessed. Return
    is a dictionary (possibly an empty one). This function is suitable
    for function calls from other programs.
    """
    (OK, status) = _check_args(locals())
    if not OK:
        raise AccountInfoError(status)
    user_info = _info(login_name)
    if not user_info:
        return {}
    uid = str(user_info['uid'])
    f = os.path.join(users_root_dir_, uid, uid + '.%s' % ext)
    try:
        return _read_file(f, lock=False)
    except Exception as reason:
        raise AccountInfoError(reason)
Beispiel #8
0
def _account_info(login_name, ext):
    """Retrieve information about a user account.

       info = _account_info(login_name, ext)

    The 'ext' can be 'profile' or 'prefs' depending if a user's
    profile information or preferences are being accessed. Return
    is a dictionary (possibly an empty one). This function is suitable
    for function calls from other programs.
    """
    (OK, status) = _check_args(locals())
    if not OK:
        raise AccountInfoError(status)
    user_info = _info(login_name)
    if not user_info:
        return {}
    uid = str(user_info['uid'])
    f = os.path.join(users_root_dir_, uid, uid + '.%s' % ext)
    try:
        return _read_file(f, lock=False)
    except Exception as reason:
        raise AccountInfoError(reason)
Beispiel #9
0
def _copy_file(req, info):
    """Copy a file internally (within KBasix).

       _copy_file(req, info)

    Depending on the situation it may return an over-quota page, an
    access denied page or, if successful, the file manager page.
    """
    import logging
    import os
    import hashlib
    import time
    import shutil
    import json
    import manage_users
    import upload
    (info['user_dir_size'], over_page) = upload._check_quota(req, 'copy', \
                                                                 info)
    if info['user_dir_size'] < 0:
        return over_page
    user_dir = os.path.join(info['users_root_dir_'], str(info['uid']))
    hash_val = hashlib.sha256(os.urandom(info['random_length'])).hexdigest()
    dst_file_tag = '%r-%s-file' % (time.time(), hash_val)
    src_file_tag = req.form['file_tag'].value
    _check_file_tag(src_file_tag, info['login_name'])
    file_info = _get_file_info(src_file_tag, info)
    denied_page = _check_permissions(file_info, info)
    if denied_page:
        return denied_page
    src_file = os.path.join(user_dir, src_file_tag)
    src_id_file = src_file + '-id'
    dst_file = os.path.join(user_dir, dst_file_tag)
    dst_id_file = dst_file + '-id'
    id_info = manage_users._read_file(src_id_file, lock=False)
    # We massage the appropriate metadata, removing all previous private
    # information.
    id_info['owner'] = info['login_name']
    id_info['owner_uid'] = info['uid']
    id_info['uid_shares'] = []
    id_info['gid_shares'] = []
    id_info['local_share'] = False
    id_info['world_share'] = False
    id_info['timestamp'] = time.time()
    id_info['file_tag'] = dst_file_tag
    id_info['custom'] = {}
    try:
        f = open(dst_id_file, 'wb')
        json.dump(id_info, f)
        f.close()
        os.chmod(dst_id_file, 0600)
    except Exception as reason:
        logging.critical('Unable to create id file "%s" because "%s" \
(%s)' % (dst_id_file, reason, info['login_name']))
        raise CopyFileError('Unable to create id file')
    try:
        shutil.copy2(src_file, dst_file)
    except Exception as reason:
        logging.critical('Unable to copy file "%s" because "%s" (%s)' % \
                             (src_file, reason, info['login_name']))
        raise CopyFileError('Unable to copy file')
    logging.debug('Copied file "%s" -> "%s" (%s)' % \
                      (src_file, dst_file, info['login_name']))
    return _initialize(req, info)
Beispiel #10
0
def _get_file_list(info):
    """Get the file listing.

       file_list = _get_file_list(info)

    Returns a string.
    """
    # This function is way too big, it needs to be sensibly split up.
    import logging
    import os
    import fnmatch
    import time
    import aux
    import manage_users
    import manage_kbasix
    login_name = info['login_name']
    logging.debug('Creating a file list (%s)' % login_name)
    prefs = manage_kbasix._account_info(login_name, 'prefs')
    user_dir = os.path.join(info['users_root_dir_'], str(info['uid']))
    entries = {}
    count = {}
    gids = manage_users._info(login_name)['gids']
    # Check for local- and GID-shared files.
    for subdir in ['local'] + [str(i) for i in gids]:
        subpath = os.path.join(info['shared_dir_'], subdir)
        if os.path.exists(subpath):
            # Some house cleaning: delete broken symlinks.
            for i in os.listdir(subpath):
                f = os.path.join(subpath, i)
                # We are lenient with file removals in shared directories
                # because another user might have beat us to it.
                if not os.path.exists(f):
                    try:
                        os.remove(f)
                        logging.info('Deleted broken shared symlink "%s" \
(%s)' % (f, login_name))
                    except Exception as reason:
                        logging.warn(reason)
            for i in fnmatch.filter(os.listdir(subpath), '*-file'):
                rel_path = os.path.relpath(subpath, user_dir)
                src = os.path.join(rel_path, i)
                dst = os.path.join(user_dir, i)
                # Shared entries which are deleted by the user (the
                # sharee) are actually hidden from view by adding them
                # to the 'hidden_gidloc_shared_files' list when the
                # sharee deletes them.
                # Once hidden, local/gid-shared files cannot be recovered
                # unless explicitly re-shared with that user via uid_shares.
                # Note that if the file exists (important if it's a uid
                # point share, which takes precedence) we leave it alone.
                if not os.path.exists(dst) and \
                        i not in \
                        prefs['file_manager']['hidden_gidloc_shared_files']:
                    try:
                        # The target may not exist, but if the link does,
                        # delete it.
                        if os.path.islink(dst + '-id'):
                            os.remove(dst + '-id')
                        os.symlink(src + '-id', dst + '-id')
                    except:
                        logging.error('Cannot link shared id file "%s" \
(%s)' % (src + '-id', login_name))
                    else:
                        if os.path.islink(dst):
                            os.remove(dst)
                        os.symlink(src, dst)
                        logging.debug('Added shared file "%s" -> "%s" \
(%s)' % (src, dst, login_name))
    # Create the listing of the files/symlinks in the user's directory.
    for i in os.listdir(user_dir):
        f = os.path.join(user_dir, i)
        if not os.path.exists(f):
            try:
                os.remove(f)
                logging.info('Deleted broken user symlink "%s" (%s)' % \
                                 (f, login_name))
            except Exception as reason:
                logging.error(reason)
    for i in fnmatch.filter(os.listdir(user_dir), '*-id'):
        f = os.path.join(user_dir, i)
        # This should not normally happen. The magic [:-3] deletes '-id'
        # and leaves the content file name.
        if not os.path.exists(f[:-3]):
            try:
                os.remove(f)
                logging.error('Deleted orphan id file "%s" (%s)' % \
                                  (f, login_name))
            except Exception as reason:
                logging.error(reason)
            continue
        details = manage_users._read_file(f, lock=False)
        # A symlink implies the file is being shared with the user.
        if os.path.islink(f):
            details['shared_with_me'] = True
        else:
            details['shared_with_me'] = False
        for j in ['file_title', 'file_description']:
            if not details[j]: details[j] = info['empty_placeholder_']
        details['file_date'] = \
            time.strftime(info['file_manager_time_format_'], \
                              time.localtime(details['timestamp']))
        # We need the token for the buttons.
        details['token'] = info['token']
        sort_key = details[prefs['file_manager']['sort_criteria']]
        # We pad with zeros to obtain a natural sorting for entries with
        # the same non-string keys e.g. size or timestamp. Padding to 50
        # seems safe, although this is admittedly a magic number
        # (timestamps have less than 20 digits, and files size are smaller
        # than that). Worst case scenario is that the sorting on huge
        # numeric sort keys (larger than 50 digits) will be wrong, but this
        # is unlikely to happen and has no other consequences.
        if not isinstance(sort_key, basestring):
            sort_key = '%r' % sort_key
            sort_key = sort_key.zfill(50)
        # We internally distinguish between identical entry values
        # (say, identical file names), but although lumped together
        # in their proper place within the overall list they are
        # not sub-sorted in any deliberate way.
        if sort_key not in entries:
            count[sort_key] = 1
            entries[sort_key] = details
        else:
            count[sort_key] += 1
            entries[sort_key + ' (%s)' % count[sort_key]] = details
    if prefs['file_manager']['condensed_view']:
        info['template'] = 'condensed_entry_template_'
    else:
        info['template'] = 'entry_template_'
    file_list = ''
    for key in sorted(entries, \
                          reverse=bool(prefs['file_manager']['reverse'])):
        entries[key]['shared_status'] = ''
        entries[key]['file_colour'] = info['my_file_colour_']
        if entries[key]['shared_with_me']:
            # World shares are not symlinked with the individual accounts,
            # and entries which cannot be read any longer are deleted.
            if not entries[key]['local_share'] and \
                    not info['uid'] in entries[key]['uid_shares'] and not \
                    set(gids).intersection(set(entries[key]['gid_shares'])):
                shared_file = \
                    os.path.join(user_dir, entries[key]['file_tag'])
                try:
                    os.remove(shared_file)
                    os.remove(shared_file + '-id')
                except Exception as reason:
                    logging.error(reason)
                continue
            # Don't show the shares made by no-longer-exsting users if
            # 'exusers_cannot_share' is True.
            elif not manage_users._info(entries[key]['owner_uid']) and \
                    info['exusers_cannot_share']:
                continue
            else:
                entries[key]['shared_status'] = """
                 <img src="%s" title="File shared with me by: %s"
                   alt="[File shared with me by: %s]" />
""" % (info['shared_icon_with_me_'], entries[key]['owner'], \
           entries[key]['owner'])
                entries[key]['file_colour'] = info['other_file_colour_']
        if prefs['file_manager']['hide_shared'] and \
                entries[key]['shared_with_me']:
            continue
        # Shared files can be copied internally (it's more efficient than
        # downloading and uploading again).
        entries[key]['copy_file'] = ''
        if entries[key]['shared_with_me']:
            entries[key]['copy_file_icon_'] = info['copy_file_icon_']
            entries[key]['copy_file_form_style_'] = \
                info['copy_file_form_style_']
            entries[key]['copy_file'] = """
             <form %(copy_file_form_style_)s
              action="../file_manager.py/process?action=copy_file"
              method="post">
              <input type="hidden" name="token" value="%(token)s" />
              <input type="hidden" name="file_tag" value="%(file_tag)s" />
              <input title="Make a local copy" type="image"
              alt="Make a local copy" src="%(copy_file_icon_)s" />
             </form>
""" % entries[key]
        # Files that are shared (the user being the sharer) are tagged
        # in various ways to indicate this.
        else:
            if entries[key]['world_share']:
                entries[key]['shared_status'] += """
                 <img src="%s" title="File is shared with the world"
                  alt="[File is shared with the world]" />
""" % info['shared_icon_by_me_to_world_']
            if entries[key]['local_share']:
                entries[key]['shared_status'] += """
                 <img src="%s" title="File is shared with registered users"
                  alt="[File is shared with registered users]" />
""" % info['shared_icon_by_me_locally_']
            if entries[key]['gid_shares']:
                entries[key]['shared_status'] += """
                 <img src="%s" title="File is shared with selected groups"
                  alt="[File is shared with selected groups]" />
""" % info['shared_icon_by_me_to_groups_']
            if entries[key]['uid_shares']:
                entries[key]['shared_status'] += """
                 <img src="%s" title="File is shared with selected users"
                  alt="[File is shared with selected users]" />
""" % info['shared_icon_by_me_selectively_']
        # Convert the size to a string with the appropriate unit suffix e.g.
        # 'MB'.
        entries[key]['file_size_str'] = \
            aux._bytes_string(entries[key]['file_size'])
        entries[key]['toggle_select'] = info['toggle_select']
        # Limit the length of titles and descriptions.
        for i in ['description', 'title']:
            if info['max_chars_in_' + i]:
                max_char = len(entries[key]['file_' + i])
                char_num = min(max_char, info['max_chars_in_' + i])
                entries[key][i + '_blurb'] = \
                    entries[key]['file_' + i][:char_num]
                if max_char > char_num:
                    entries[key][i + '_blurb'] += '...'
            else:
                entries[key][i + '_blurb'] = entries[key]['file_' + i]
        # The keyword filter. This is actually a very important
        # functionality, and should be split into its own function.
        # Furthermore, it is currently very weak, and should be drastically
        # improved. Right now is just filters on words in the file title
        # and description, ignoring punctuation. It is also
        # case-insensitive.
        if prefs['file_manager']['keywords']:
            import re
            # Use '[\W_]+' to eliminate underscores. See also:
            #
            # http://stackoverflow.com/questions/6631870/strip-non-alpha-numeric-characters-from-string-in-python-but-keeping-special-cha
            #
            # The 're.U' works on unicode strings.
            nonalnum = re.compile('[\W]+', re.U)
            keywords = set([i.lower() for i in \
                                prefs['file_manager']['keywords'].split()])
            words = [nonalnum.sub('', i.lower()) for i in \
                         entries[key]['file_title'].split()]
            words += [nonalnum.sub('', i.lower()) for i in \
                          entries[key]['file_description'].split()]
            if keywords <= set(words):
                file_list += aux._fill_str(info[info['template']], \
                                               entries[key])
            continue
        file_list += aux._fill_str(info[info['template']], entries[key])
    if not file_list:
        return info['no_files_found_']
    else:
        return file_list
Beispiel #11
0
def _copy_file(req, info):
    """Copy a file internally (within KBasix).

       _copy_file(req, info)

    Depending on the situation it may return an over-quota page, an
    access denied page or, if successful, the file manager page.
    """
    import logging
    import os
    import hashlib
    import time
    import shutil
    import json
    import manage_users
    import upload
    (info['user_dir_size'], over_page) = upload._check_quota(req, 'copy', \
                                                                 info)
    if info['user_dir_size'] < 0:
        return over_page
    user_dir = os.path.join(info['users_root_dir_'], str(info['uid']))
    hash_val = hashlib.sha256(os.urandom(info['random_length'])).hexdigest()
    dst_file_tag = '%r-%s-file' % (time.time(), hash_val)
    src_file_tag = req.form['file_tag'].value
    _check_file_tag(src_file_tag, info['login_name'])
    file_info = _get_file_info(src_file_tag, info)
    denied_page = _check_permissions(file_info, info)
    if denied_page:
        return denied_page
    src_file = os.path.join(user_dir, src_file_tag)
    src_id_file = src_file + '-id'
    dst_file = os.path.join(user_dir, dst_file_tag)
    dst_id_file = dst_file + '-id'
    id_info = manage_users._read_file(src_id_file, lock=False)
    # We massage the appropriate metadata, removing all previous private
    # information.
    id_info['owner'] = info['login_name']
    id_info['owner_uid'] = info['uid']
    id_info['uid_shares'] = []
    id_info['gid_shares'] = []
    id_info['local_share'] = False
    id_info['world_share'] = False
    id_info['timestamp'] = time.time()
    id_info['file_tag'] = dst_file_tag
    id_info['custom'] = {}
    try:
        f = open(dst_id_file, 'wb')
        json.dump(id_info, f)
        f.close()
        os.chmod(dst_id_file, 0600)
    except Exception as reason:
        logging.critical('Unable to create id file "%s" because "%s" \
(%s)' % (dst_id_file, reason, info['login_name']))
        raise CopyFileError('Unable to create id file')
    try:
        shutil.copy2(src_file, dst_file)
    except Exception as reason:
        logging.critical('Unable to copy file "%s" because "%s" (%s)' % \
                             (src_file, reason, info['login_name']))
        raise CopyFileError('Unable to copy file')
    logging.debug('Copied file "%s" -> "%s" (%s)' % \
                      (src_file, dst_file, info['login_name']))
    return _initialize(req, info)
Beispiel #12
0
def _get_file_list(info):
    """Get the file listing.

       file_list = _get_file_list(info)

    Returns a string.
    """
    # This function is way too big, it needs to be sensibly split up.
    import logging
    import os
    import fnmatch
    import time
    import aux
    import manage_users
    import manage_kbasix
    login_name = info['login_name']
    logging.debug('Creating a file list (%s)' % login_name)
    prefs = manage_kbasix._account_info(login_name, 'prefs')
    user_dir = os.path.join(info['users_root_dir_'], str(info['uid']))
    entries = {}
    count = {}
    gids = manage_users._info(login_name)['gids']
    # Check for local- and GID-shared files.
    for subdir in ['local'] + [str(i) for i in gids]:
        subpath = os.path.join(info['shared_dir_'], subdir)
        if os.path.exists(subpath):
            # Some house cleaning: delete broken symlinks.
            for i in os.listdir(subpath):
                f = os.path.join(subpath, i)
                # We are lenient with file removals in shared directories
                # because another user might have beat us to it.
                if not os.path.exists(f):
                    try:
                        os.remove(f)
                        logging.info('Deleted broken shared symlink "%s" \
(%s)' % (f, login_name))
                    except Exception as reason:
                        logging.warn(reason)
            for i in fnmatch.filter(os.listdir(subpath), '*-file'):
                rel_path = os.path.relpath(subpath, user_dir)
                src = os.path.join(rel_path, i)
                dst = os.path.join(user_dir, i)
                # Shared entries which are deleted by the user (the
                # sharee) are actually hidden from view by adding them
                # to the 'hidden_gidloc_shared_files' list when the
                # sharee deletes them.
                # Once hidden, local/gid-shared files cannot be recovered
                # unless explicitly re-shared with that user via uid_shares.
                # Note that if the file exists (important if it's a uid
                # point share, which takes precedence) we leave it alone.
                if not os.path.exists(dst) and \
                        i not in \
                        prefs['file_manager']['hidden_gidloc_shared_files']:
                    try:
                        # The target may not exist, but if the link does,
                        # delete it.
                        if os.path.islink(dst + '-id'):
                            os.remove(dst + '-id')
                        os.symlink(src + '-id', dst + '-id')
                    except:
                        logging.error('Cannot link shared id file "%s" \
(%s)' % (src + '-id', login_name))
                    else:
                        if os.path.islink(dst):
                            os.remove(dst)
                        os.symlink(src, dst)
                        logging.debug('Added shared file "%s" -> "%s" \
(%s)' % (src, dst, login_name))
    # Create the listing of the files/symlinks in the user's directory.
    for i in os.listdir(user_dir):
        f = os.path.join(user_dir, i)
        if not os.path.exists(f):
            try:
                os.remove(f)
                logging.info('Deleted broken user symlink "%s" (%s)' % \
                                 (f, login_name))
            except Exception as reason:
                logging.error(reason)
    for i in fnmatch.filter(os.listdir(user_dir), '*-id'):
        f = os.path.join(user_dir, i)
        # This should not normally happen. The magic [:-3] deletes '-id'
        # and leaves the content file name.
        if not os.path.exists(f[:-3]):
            try:
                os.remove(f)
                logging.error('Deleted orphan id file "%s" (%s)' % \
                                  (f, login_name))
            except Exception as reason:
                logging.error(reason)
            continue
        details = manage_users._read_file(f, lock=False)
        # A symlink implies the file is being shared with the user.
        if os.path.islink(f):
            details['shared_with_me'] = True
        else:
            details['shared_with_me'] = False
        for j in ['file_title', 'file_description']:
            if not details[j]: details[j] = info['empty_placeholder_']
        details['file_date'] = \
            time.strftime(info['file_manager_time_format_'], \
                              time.localtime(details['timestamp']))
        # We need the token for the buttons.
        details['token'] = info['token']
        sort_key = details[prefs['file_manager']['sort_criteria']]
        # We pad with zeros to obtain a natural sorting for entries with
        # the same non-string keys e.g. size or timestamp. Padding to 50
        # seems safe, although this is admittedly a magic number
        # (timestamps have less than 20 digits, and files size are smaller
        # than that). Worst case scenario is that the sorting on huge
        # numeric sort keys (larger than 50 digits) will be wrong, but this
        # is unlikely to happen and has no other consequences.
        if not isinstance(sort_key, basestring):
            sort_key = '%r' % sort_key
            sort_key = sort_key.zfill(50)
        # We internally distinguish between identical entry values
        # (say, identical file names), but although lumped together
        # in their proper place within the overall list they are
        # not sub-sorted in any deliberate way.
        if sort_key not in entries:
            count[sort_key] = 1
            entries[sort_key] = details
        else:
            count[sort_key] += 1
            entries[sort_key + ' (%s)' % count[sort_key]] = details
    if prefs['file_manager']['condensed_view']:
        info['template'] = 'condensed_entry_template_'
    else:
        info['template'] = 'entry_template_'
    file_list = ''
    for key in sorted(entries, \
                          reverse=bool(prefs['file_manager']['reverse'])):
        entries[key]['shared_status'] = ''
        entries[key]['file_colour'] = info['my_file_colour_']
        if entries[key]['shared_with_me']:
            # World shares are not symlinked with the individual accounts,
            # and entries which cannot be read any longer are deleted.
            if not entries[key]['local_share'] and \
                    not info['uid'] in entries[key]['uid_shares'] and not \
                    set(gids).intersection(set(entries[key]['gid_shares'])):
                shared_file = \
                    os.path.join(user_dir, entries[key]['file_tag'])
                try:
                    os.remove(shared_file)
                    os.remove(shared_file + '-id')
                except Exception as reason:
                    logging.error(reason)
                continue
            # Don't show the shares made by no-longer-exsting users if
            # 'exusers_cannot_share' is True.
            elif not manage_users._info(entries[key]['owner_uid']) and \
                    info['exusers_cannot_share']:
                continue
            else:
                entries[key]['shared_status'] = """
                 <img src="%s" title="File shared with me by: %s"
                   alt="[File shared with me by: %s]" />
""" % (info['shared_icon_with_me_'], entries[key]['owner'], \
           entries[key]['owner'])
                entries[key]['file_colour'] = info['other_file_colour_']
        if prefs['file_manager']['hide_shared'] and \
                entries[key]['shared_with_me']:
            continue
        # Shared files can be copied internally (it's more efficient than
        # downloading and uploading again).
        entries[key]['copy_file'] = ''
        if entries[key]['shared_with_me']:
            entries[key]['copy_file_icon_'] = info['copy_file_icon_']
            entries[key]['copy_file_form_style_'] = \
                info['copy_file_form_style_'] 
            entries[key]['copy_file'] = """
             <form %(copy_file_form_style_)s
              action="../file_manager.py/process?action=copy_file"
              method="post">
              <input type="hidden" name="token" value="%(token)s" />
              <input type="hidden" name="file_tag" value="%(file_tag)s" />
              <input title="Make a local copy" type="image"
              alt="Make a local copy" src="%(copy_file_icon_)s" />
             </form>
""" % entries[key]
        # Files that are shared (the user being the sharer) are tagged
        # in various ways to indicate this.
        else:
            if entries[key]['world_share']:
                entries[key]['shared_status'] += """
                 <img src="%s" title="File is shared with the world"
                  alt="[File is shared with the world]" />
""" % info['shared_icon_by_me_to_world_']
            if entries[key]['local_share']:
                entries[key]['shared_status'] += """
                 <img src="%s" title="File is shared with registered users"
                  alt="[File is shared with registered users]" />
""" % info['shared_icon_by_me_locally_']
            if entries[key]['gid_shares']:
                entries[key]['shared_status'] += """
                 <img src="%s" title="File is shared with selected groups"
                  alt="[File is shared with selected groups]" />
""" % info['shared_icon_by_me_to_groups_']
            if entries[key]['uid_shares']:
                entries[key]['shared_status'] += """
                 <img src="%s" title="File is shared with selected users"
                  alt="[File is shared with selected users]" />
""" % info['shared_icon_by_me_selectively_']
        # Convert the size to a string with the appropriate unit suffix e.g.
        # 'MB'.
        entries[key]['file_size_str'] = \
            aux._bytes_string(entries[key]['file_size'])
        entries[key]['toggle_select'] = info['toggle_select']
        # Limit the length of titles and descriptions.
        for i in ['description', 'title']:
            if info['max_chars_in_' + i]:
                max_char = len(entries[key]['file_' + i])
                char_num = min(max_char, info['max_chars_in_' + i])
                entries[key][i + '_blurb'] = \
                    entries[key]['file_' + i][:char_num]
                if max_char > char_num:
                    entries[key][i + '_blurb'] += '...'
            else:
                entries[key][i + '_blurb'] = entries[key]['file_' + i]
        # The keyword filter. This is actually a very important
        # functionality, and should be split into its own function.
        # Furthermore, it is currently very weak, and should be drastically
        # improved. Right now is just filters on words in the file title
        # and description, ignoring punctuation. It is also
        # case-insensitive.
        if prefs['file_manager']['keywords']:
            import re
            # Use '[\W_]+' to eliminate underscores. See also:
            #
            # http://stackoverflow.com/questions/6631870/strip-non-alpha-numeric-characters-from-string-in-python-but-keeping-special-cha
            #
            # The 're.U' works on unicode strings.
            nonalnum = re.compile('[\W]+', re.U)
            keywords = set([i.lower() for i in \
                                prefs['file_manager']['keywords'].split()])
            words = [nonalnum.sub('', i.lower()) for i in \
                         entries[key]['file_title'].split()]
            words += [nonalnum.sub('', i.lower()) for i in \
                          entries[key]['file_description'].split()]
            if keywords <= set(words):
                file_list += aux._fill_str(info[info['template']], \
                                               entries[key])
            continue
        file_list += aux._fill_str(info[info['template']], entries[key])
    if not file_list:
        return info['no_files_found_']
    else:
        return file_list