diff options
author | 6 <NotZed@Ximian.com> | 2001-10-27 14:02:04 +0800 |
---|---|---|
committer | Michael Zucci <zucchi@src.gnome.org> | 2001-10-27 14:02:04 +0800 |
commit | 8f8b24c737a907f91b88bf0b2b985d56b4051848 (patch) | |
tree | 8af1be6e77686cb935ae5a21ac17d7b14a9b0463 | |
parent | c4d87ba45ad016d5a6607df96ed27ba02910e2e6 (diff) | |
download | gsoc2013-evolution-8f8b24c737a907f91b88bf0b2b985d56b4051848.tar.gz gsoc2013-evolution-8f8b24c737a907f91b88bf0b2b985d56b4051848.tar.zst gsoc2013-evolution-8f8b24c737a907f91b88bf0b2b985d56b4051848.zip |
Changed to open the source store from '/', so we can do renames across
2001-10-26 <NotZed@Ximian.com>
* mail-local.c (mlf_set_folder): Changed to open the source store
from '/', so we can do renames across directories. Of course,
this doesn't quite work with different filesystems, but we'll
assume this isn't a problem we're going to have.
(mail_local_folder_get_type): Setup parent_class.
(mlf_rename): Implement folder rename call, rename internal data.
Is this a security issue? Well, not really, no more than anything
else.
* mail-folder-cache.c (mail_note_store): Listen to rename event.
(store_folder_renamed): Function to handle it, empty.
(mail_note_store_remove): Unhook from rename event.
(mail_note_folder): Hook onto renamed event.
(unset_folder_info): Unhook from renamed event.
(folder_finalised): Lock around update.
(folder_deleted): Lock around update.
(real_flush_updates): If we have a path to remove remove it.
(rename_folders): Scan folderinfo's, if we can find ones renamed,
rename them, otherwise add them.
* component-factory.c (owner_unset_cb): Use a timeout not an idle handler.
(storage_xfer_folder): Implementation of xfer_folder signal
handler, so we can rename imap/vfolders/etc.
svn path=/trunk/; revision=14205
-rw-r--r-- | mail/ChangeLog | 25 | ||||
-rw-r--r-- | mail/component-factory.c | 48 | ||||
-rw-r--r-- | mail/mail-folder-cache.c | 139 | ||||
-rw-r--r-- | mail/mail-local.c | 84 | ||||
-rw-r--r-- | mail/mail-vfolder.c | 61 |
5 files changed, 342 insertions, 15 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog index 2b74b79682..631fed11d4 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -22,6 +22,31 @@ 2001-10-26 <NotZed@Ximian.com> + * mail-local.c (mlf_set_folder): Changed to open the source store + from '/', so we can do renames across directories. Of course, + this doesn't quite work with different filesystems, but we'll + assume this isn't a problem we're going to have. + (mail_local_folder_get_type): Setup parent_class. + (mlf_rename): Implement folder rename call, rename internal data. + + Is this a security issue? Well, not really, no more than anything + else. + + * mail-folder-cache.c (mail_note_store): Listen to rename event. + (store_folder_renamed): Function to handle it, empty. + (mail_note_store_remove): Unhook from rename event. + (mail_note_folder): Hook onto renamed event. + (unset_folder_info): Unhook from renamed event. + (folder_finalised): Lock around update. + (folder_deleted): Lock around update. + (real_flush_updates): If we have a path to remove remove it. + (rename_folders): Scan folderinfo's, if we can find ones renamed, + rename them, otherwise add them. + + * component-factory.c (owner_unset_cb): Use a timeout not an idle handler. + (storage_xfer_folder): Implementation of xfer_folder signal + handler, so we can rename imap/vfolders/etc. + * component-factory.c (owner_unset_cb): Use a timeout not an idle handler. diff --git a/mail/component-factory.c b/mail/component-factory.c index 0e3fafb010..d521d97df1 100644 --- a/mail/component-factory.c +++ b/mail/component-factory.c @@ -322,6 +322,8 @@ xfer_folder (EvolutionShellComponent *shell_component, CamelException ex; GPtrArray *uids; CamelURL *url; + + printf("Renaming folder '%s' to dest '%s' type '%s'\n", source_physical_uri, destination_physical_uri, type); url = camel_url_new (destination_physical_uri, NULL); noselect = url ? camel_url_get_param (url, "noselect") : NULL; @@ -1092,6 +1094,43 @@ storage_remove_folder (EvolutionStorage *storage, } static void +storage_xfer_folder (EvolutionStorage *storage, + const Bonobo_Listener listener, + const char *source_path, + const char *destination_path, + gboolean remove_source, + CamelStore *store) +{ + CamelException ex; + + printf("Transfer folder on store source = '%s' dest = '%s'\n", source_path, destination_path); + + /* FIXME: Need to remap path to real name */ + + if (source_path[0] == '/') + source_path++; + if (destination_path[0] == '/') + destination_path++; + + camel_exception_init (&ex); + if (remove_source) { + printf("trying to rename\n"); + camel_store_rename_folder(store, source_path, destination_path, &ex); + } else { + printf("No remove, can't rename\n"); + camel_exception_setv(&ex, 1, "Can copy folders"); + } + + if (camel_exception_is_set(&ex)) { + notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI); + } else { + notify_listener (listener, GNOME_Evolution_Storage_OK); + } + + camel_exception_clear (&ex); +} + +static void add_storage (const char *name, const char *uri, CamelService *store, GNOME_Evolution_Shell corba_shell, CamelException *ex) { @@ -1099,12 +1138,9 @@ add_storage (const char *name, const char *uri, CamelService *store, EvolutionStorageResult res; storage = evolution_storage_new (name, uri, "mailstorage"); - gtk_signal_connect (GTK_OBJECT (storage), "create_folder", - GTK_SIGNAL_FUNC (storage_create_folder), - store); - gtk_signal_connect (GTK_OBJECT (storage), "remove_folder", - GTK_SIGNAL_FUNC (storage_remove_folder), - store); + gtk_signal_connect (GTK_OBJECT (storage), "create_folder", storage_create_folder, store); + gtk_signal_connect (GTK_OBJECT (storage), "remove_folder", storage_remove_folder, store); + gtk_signal_connect ((GtkObject *)storage, "xfer_folder", storage_xfer_folder, store); res = evolution_storage_register_on_shell (storage, corba_shell); diff --git a/mail/mail-folder-cache.c b/mail/mail-folder-cache.c index c08da26a5a..6ae250df0c 100644 --- a/mail/mail-folder-cache.c +++ b/mail/mail-folder-cache.c @@ -75,6 +75,8 @@ struct _folder_update { char *path; char *name; char *uri; + char *oldpath; + int unread; CamelStore *store; }; @@ -95,6 +97,7 @@ struct _store_info { static void folder_changed(CamelObject *o, gpointer event_data, gpointer user_data); static void folder_deleted(CamelObject *o, gpointer event_data, gpointer user_data); +static void folder_renamed(CamelObject *o, gpointer event_data, gpointer user_data); static void folder_finalised(CamelObject *o, gpointer event_data, gpointer user_data); /* Store to storeinfo table, active stores */ @@ -149,6 +152,12 @@ real_flush_updates(void *o, void *event_data, void *data) else mail_vfolder_add_uri(up->store, up->uri, TRUE); } else { + if (up->oldpath) { + if (storage != NULL) + evolution_storage_removed_folder(storage, up->oldpath); + /* ELSE? Shell supposed to handle the local snot case */ + } + if (up->name == NULL) { if (storage != NULL) { d(printf("Updating existing folder: %s (%d unread)\n", up->path, up->unread)); @@ -198,6 +207,7 @@ unset_folder_info(struct _folder_info *mfi, int delete) camel_object_unhook_event((CamelObject *)folder, "folder_changed", folder_changed, mfi); camel_object_unhook_event((CamelObject *)folder, "message_changed", folder_changed, mfi); camel_object_unhook_event((CamelObject *)folder, "deleted", folder_deleted, mfi); + camel_object_unhook_event((CamelObject *)folder, "renamed", folder_renamed, mfi); camel_object_unhook_event((CamelObject *)folder, "finalize", folder_finalised, mfi); } @@ -352,7 +362,9 @@ folder_finalised(CamelObject *o, gpointer event_data, gpointer user_data) struct _folder_info *mfi = user_data; d(printf("Folder finalised '%s'!\n", ((CamelFolder *)o)->full_name)); + LOCK(info_lock); mfi->folder = NULL; + UNLOCK(info_lock); } static void @@ -361,7 +373,21 @@ folder_deleted(CamelObject *o, gpointer event_data, gpointer user_data) struct _folder_info *mfi = user_data; d(printf("Folder deleted '%s'!\n", ((CamelFolder *)o)->full_name)); + LOCK(info_lock); mfi->folder = NULL; + UNLOCK(info_lock); +} + +static void +folder_renamed(CamelObject *o, gpointer event_data, gpointer user_data) +{ + struct _folder_info *mfi = user_data; + CamelFolder *folder = (CamelFolder *)o; + char *old = event_data; + + printf("Folder renamed from '%s' to '%s'\n", old, folder->full_name); + mfi = mfi; + /* Dont do anything, do it from the store rename event? */ } void mail_note_folder(CamelFolder *folder) @@ -401,6 +427,7 @@ void mail_note_folder(CamelFolder *folder) camel_object_hook_event((CamelObject *)folder, "folder_changed", folder_changed, mfi); camel_object_hook_event((CamelObject *)folder, "message_changed", folder_changed, mfi); camel_object_hook_event((CamelObject *)folder, "deleted", folder_deleted, mfi); + camel_object_hook_event((CamelObject *)folder, "renamed", folder_renamed, mfi); camel_object_hook_event((CamelObject *)folder, "finalize", folder_finalised, mfi); update_1folder(mfi, NULL); @@ -461,6 +488,116 @@ store_folder_deleted(CamelObject *o, void *event_data, void *data) store_folder_unsubscribed(o, event_data, data); } +static void +rename_folders(struct _store_info *si, const char *oldbase, const char *newbase, CamelFolderInfo *fi) +{ + char *old; + struct _folder_info *mfi; + struct _folder_update *up; + + up = g_malloc0(sizeof(*up)); + + /* Form what was the old name, and try and look it up */ + old = g_strdup_printf("%s%s", oldbase, fi->full_name + strlen(newbase)); + mfi = g_hash_table_lookup(si->folders, old); + if (mfi) { + printf("Found old folder '%s' renaming to '%s'\n", mfi->full_name, fi->full_name); + + up->oldpath = mfi->path; + + /* Its a rename op */ + g_hash_table_remove(si->folders, mfi->full_name); + g_hash_table_remove(si->folders, mfi->uri); + g_free(mfi->full_name); + g_free(mfi->uri); + mfi->path = g_strdup(fi->path); + mfi->full_name = g_strdup(fi->full_name); + mfi->uri = g_strdup(fi->url); + g_hash_table_insert(si->folders, mfi->full_name, mfi); + g_hash_table_insert(si->folders_uri, mfi->uri, mfi); + } else { + printf("Rename found a new folder? old '%s' new '%s'\n", old, fi->full_name); + /* Its a new op */ + mfi = g_malloc0(sizeof(*mfi)); + mfi->path = g_strdup(fi->path); + mfi->full_name = g_strdup(fi->full_name); + mfi->uri = g_strdup(fi->url); + mfi->store_info = si; + g_hash_table_insert(si->folders, mfi->full_name, mfi); + g_hash_table_insert(si->folders_uri, mfi->uri, mfi); + } + + g_free(old); + + up->path = g_strdup(mfi->path); + if (si->storage) + up->name = g_strdup(fi->name); + up->uri = g_strdup(mfi->uri); + up->unread = fi->unread_message_count==-1?0:fi->unread_message_count; + up->store = si->store; + camel_object_ref((CamelObject *)up->store); + if (strstr(fi->url, ";noselect") == NULL) + up->add = TRUE; + + e_dlist_addtail(&updates, (EDListNode *)up); + flush_updates(); + + if (fi->sibling) + rename_folders(si, oldbase, newbase, fi->sibling); + if (fi->child) + rename_folders(si, oldbase, newbase, fi->child); +} + +#if 0 +/* This is if we want to get the folders individually and sort them and change them on + the shell atomically (whcih of course, we can't) */ +static void +get_folders(GPtrArray *folders, CamelFolderInfo *fi) +{ +} + +static int +folder_cmp(const void *ap, const void *bp) +{ + const struct _folder_update *a = ((struct _folder_update **)ap)[0]; + const struct _folder_update *b = ((struct _folder_update **)bp)[0]; + + return strcmp(a->path, b->path); +} +#endif + +static void +store_folder_renamed(CamelObject *o, void *event_data, void *data) +{ + CamelStore *store = (CamelStore *)o; + CamelRenameInfo *info = event_data; + struct _store_info *si; + + d(printf("Folder renamed?\n")); + + LOCK(info_lock); + si = g_hash_table_lookup(stores, store); + if (si) { + GPtrArray *folders = g_ptr_array_new(); + struct _folder_update *up; + +#if 0 + /* first, get an array of all folders */ + get_folders(folders, info->new); +#endif + rename_folders(si, info->old_base, info->new->full_name, info->new); +#if 0 + qsort(folders->pdata, folders->len, sizeof(folders->pdata[0]), folder_cmp); + up = g_malloc0(sizeof(*up)); + up->renamed = folders; + + e_dlist_addtail(&updates, (EDListNode *)up); + flush_updates(); +#endif + } + UNLOCK(info_lock); +} + struct _update_data { struct _update_data *next; struct _update_data *prev; @@ -502,6 +639,7 @@ mail_note_store_remove(CamelStore *store) camel_object_unhook_event((CamelObject *)store, "folder_created", store_folder_created, NULL); camel_object_unhook_event((CamelObject *)store, "folder_deleted", store_folder_deleted, NULL); + camel_object_unhook_event((CamelObject *)store, "folder_renamed", store_folder_renamed, NULL); camel_object_unhook_event((CamelObject *)store, "folder_subscribed", store_folder_subscribed, NULL); camel_object_unhook_event((CamelObject *)store, "folder_unsubscribed", store_folder_unsubscribed, NULL); g_hash_table_foreach(si->folders, (GHFunc)unset_folder_info_hash, NULL); @@ -593,6 +731,7 @@ mail_note_store(CamelStore *store, EvolutionStorage *storage, GNOME_Evolution_St camel_object_hook_event((CamelObject *)store, "folder_created", store_folder_created, NULL); camel_object_hook_event((CamelObject *)store, "folder_deleted", store_folder_deleted, NULL); + camel_object_hook_event((CamelObject *)store, "folder_renamed", store_folder_renamed, NULL); camel_object_hook_event((CamelObject *)store, "folder_subscribed", store_folder_subscribed, NULL); camel_object_hook_event((CamelObject *)store, "folder_unsubscribed", store_folder_unsubscribed, NULL); } diff --git a/mail/mail-local.c b/mail/mail-local.c index ed72bbc9f5..ffaa890e9a 100644 --- a/mail/mail-local.c +++ b/mail/mail-local.c @@ -221,6 +221,8 @@ save_metainfo(struct _local_meta *meta) return ret; } +static CamelFolderClass *mlf_parent_class = NULL; + /* forward a bunch of functions to the real folder. This pretty * much sucks but I haven't found a better way of doing it. */ @@ -401,6 +403,27 @@ mlf_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, camel_object_unref((CamelObject *)f); } +/* Internal store-rename call, update our strings */ +static void +mlf_rename(CamelFolder *folder, const char *new) +{ + MailLocalFolder *mlf = (MailLocalFolder *)folder; + + /* first, proxy it down */ + if (mlf->real_folder) + camel_folder_rename(mlf->real_folder, new); + + /* Then do our stuff */ + g_free(mlf->real_path); + mlf->real_path = g_strdup(new); + + g_free(mlf->meta->path); + mlf->meta->path = g_strdup_printf("%s/%s/local-metadata.xml", ((CamelService *)folder->parent_store)->url->path, new); + + /* Then pass it up */ + ((CamelFolderClass *)mlf_parent_class)->rename(folder, new); +} + /* and, conversely, forward the real folder's signals. */ static void @@ -446,11 +469,13 @@ static gboolean mlf_set_folder(MailLocalFolder *mlf, guint32 flags, CamelException *ex) { CamelFolder *folder = (CamelFolder *)mlf; - char *uri; + char *uri, *mbox; g_assert(mlf->real_folder == NULL); - uri = g_strdup_printf("%s:%s%s", mlf->meta->format, ((CamelService *)folder->parent_store)->url->path, mlf->real_path); + /*uri = g_strdup_printf("%s:%s%s", mlf->meta->format, ((CamelService *)folder->parent_store)->url->path, mlf->real_path);*/ + uri = g_strdup_printf("%s:%s", mlf->meta->format, ((CamelService *)folder->parent_store)->url->path); + d(printf("opening real store: %s\n", uri)); mlf->real_store = camel_session_get_store(session, uri, ex); g_free(uri); @@ -460,7 +485,11 @@ mlf_set_folder(MailLocalFolder *mlf, guint32 flags, CamelException *ex) if (mlf->meta->indexed) flags |= CAMEL_STORE_FOLDER_BODY_INDEX; - mlf->real_folder = camel_store_get_folder(mlf->real_store, mlf->meta->name, flags, ex); + /* mlf->real_folder = camel_store_get_folder(mlf->real_store, mlf->meta->name, flags, ex); */ + mbox = g_strdup_printf("%s/%s", mlf->real_path, mlf->meta->name); + printf("Opening mbox on real path: %s\n", mbox); + mlf->real_folder = camel_store_get_folder(mlf->real_store, mbox, flags, ex); + g_free(mbox); if (mlf->real_folder == NULL) return FALSE; @@ -497,6 +526,8 @@ mlf_class_init (CamelObjectClass *camel_object_class) camel_folder_class->set_message_flags = mlf_set_message_flags; camel_folder_class->set_message_user_flag = mlf_set_message_user_flag; camel_folder_class->set_message_user_tag = mlf_set_message_user_tag; + + camel_folder_class->rename = mlf_rename; } static void @@ -538,6 +569,7 @@ mail_local_folder_get_type (void) NULL, mlf_init, mlf_finalize); + mlf_parent_class = (CamelFolderClass *)camel_type_get_global_classfuncs (CAMEL_FOLDER_TYPE); } return mail_local_folder_type; @@ -772,6 +804,51 @@ mls_delete_folder(CamelStore *store, const char *folder_name, CamelException *ex g_free(metapath); } +static void +mls_rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex) +{ + char *oldname, *newname; + + /* folder:rename() updates all our in-memory data to match */ + + /* FIXME: Need to lock the subfolder that matches this if its open + Then rename it and unlock it when done */ + + printf("Renaming folder from '%s' to '%s'\n", old_name, new_name); + + oldname = g_strdup_printf("%s%s", ((CamelService *)store)->url->path, old_name); + newname = g_strdup_printf("%s%s", ((CamelService *)store)->url->path, new_name); + + if (rename(oldname, newname) == -1) { + printf("Rename failed!\n"); + camel_exception_setv(ex, 1, "Rename failed: %s", strerror(errno)); + } + + g_free(oldname); + g_free(newname); +#if 0 + /* find the real store for this folder, and proxy the call */ + metapath = g_strdup_printf("%s%s/local-metadata.xml", ((CamelService *)store)->url->path, old_name); + meta = load_metainfo(metapath); + uri = g_strdup_printf("%s:%s%s", meta->format, ((CamelService *)store)->url->path); + real_store = (CamelStore *)camel_session_get_service(session, uri, CAMEL_PROVIDER_STORE, ex); + g_free(uri); + if (real_store == NULL) { + g_free(metapath); + free_metainfo(meta); + return; + } + oldname = g_strdup_printf("%s/%s", old_name, mlf->meta->name); + newname = g_strdup_printf("%s/%s", new_name, mlf->meta->name); + + camel_store_rename_folder(real_store, old_name, new_name, &local_ex); + + + + camel_exception_setv(ex, 1, "Not supported"); +#endif +} + static char * mls_get_name (CamelService *service, gboolean brief) { @@ -814,6 +891,7 @@ mls_class_init (CamelObjectClass *camel_object_class) camel_service_class->get_name = mls_get_name; camel_store_class->get_folder = mls_get_folder; camel_store_class->delete_folder = mls_delete_folder; + camel_store_class->rename_folder = mls_rename_folder; local_store_parent_class = camel_type_get_global_classfuncs (CAMEL_STORE_TYPE); } diff --git a/mail/mail-vfolder.c b/mail/mail-vfolder.c index 2132d8b699..d4c979060f 100644 --- a/mail/mail-vfolder.c +++ b/mail/mail-vfolder.c @@ -44,7 +44,7 @@ #include "e-util/e-unicode-i18n.h" -#define d(x) +#define d(x) x static VfolderContext *context; /* context remains open all time */ static CamelStore *vfolder_store; /* the 1 static vfolder store */ @@ -458,16 +458,30 @@ rule_changed(FilterRule *rule, CamelFolder *folder) char *path, *key; CamelFolder *old; - gtk_signal_disconnect_by_func((GtkObject *)rule, rule_changed, folder); - - context_rule_added((RuleContext *)context, rule); + LOCK(); + if (g_hash_table_lookup_extended(vfolder_hash, folder->full_name, (void **)&key, (void **)&old)) { + g_hash_table_remove(vfolder_hash, key); + g_free(key); + g_hash_table_insert(vfolder_hash, g_strdup(rule->name), folder); + UNLOCK(); + } else { + UNLOCK(); + g_warning("couldn't find a vfolder rule in our table? %s", folder->full_name); + } - /* TODO: remove folder from folder info cache? */ + camel_store_rename_folder(vfolder_store, folder->full_name, rule->name, NULL); +#if 0 path = g_strdup_printf("/%s", folder->full_name); evolution_storage_removed_folder(mail_lookup_storage(vfolder_store), path); g_free(path); + gtk_signal_disconnect_by_func((GtkObject *)rule, rule_changed, folder); + + context_rule_added((RuleContext *)context, rule); + + /* TODO: remove folder from folder info cache? */ + LOCK(); if (g_hash_table_lookup_extended(vfolder_hash, folder->full_name, (void **)&key, (void **)&old)) { g_hash_table_remove(vfolder_hash, key); @@ -480,6 +494,7 @@ rule_changed(FilterRule *rule, CamelFolder *folder) } return; +#endif } d(printf("Filter rule changed? for folder '%s'!!\n", folder->name)); @@ -595,8 +610,12 @@ store_folder_deleted(CamelObject *o, void *event_data, void *data) d(printf("Folder deleted: %s\n", info->name)); store = store; + /* WARNING: Not thread safe, joy! */ + + LOCK(); + /* delete it from our list */ - rule = rule_context_find_rule((RuleContext *)context, info->name, NULL); + rule = rule_context_find_rule((RuleContext *)context, info->full_name, NULL); if (rule) { /* We need to stop listening to removed events, otherwise we'll try and remove it again */ gtk_signal_disconnect_by_func((GtkObject *)context, context_rule_removed, context); @@ -610,6 +629,34 @@ store_folder_deleted(CamelObject *o, void *event_data, void *data) } else { g_warning("Cannot find rule for deleted vfolder '%s'", info->name); } + + UNLOCK(); +} + +static void +store_folder_renamed(CamelObject *o, void *event_data, void *data) +{ + CamelStore *store = (CamelStore *)o; + CamelRenameInfo *info = event_data; + FilterRule *rule; + char *user; + + /* TODO: Scan all sub-folders? */ + + printf("Folder renamed to '%s' from '%s'\n", info->new->full_name, info->old_base); + + rule = rule_context_find_rule((RuleContext *)context, info->old_base, NULL); + if (rule) { + /* TODO: We need to stop listening to removed events, otherwise we'll try and rename it again? */ + + filter_rule_set_name(rule, info->new->name); + + user = g_strdup_printf("%s/vfolders.xml", evolution_dir); + rule_context_save((RuleContext *)context, user); + g_free(user); + } else { + /* We just got it inside renaming it ourself, ignore */ + } } void @@ -632,6 +679,8 @@ vfolder_load_storage(GNOME_Evolution_Shell shell) (CamelObjectEventHookFunc)store_folder_created, NULL); camel_object_hook_event((CamelObject *)vfolder_store, "folder_deleted", (CamelObjectEventHookFunc)store_folder_deleted, NULL); + camel_object_hook_event((CamelObject *)vfolder_store, "folder_renamed", + (CamelObjectEventHookFunc)store_folder_renamed, NULL); d(printf("got store '%s' = %p\n", storeuri, vfolder_store)); mail_load_storage_by_uri(shell, storeuri, U_("VFolders")); |