From 2c1bfce38da557bd20019689c15cd03881863097 Mon Sep 17 00:00:00 2001 From: Benedikt Meurer <benny@xfce.org> Date: Fri, 1 Jul 2005 22:55:40 +0000 Subject: [PATCH] 2005-07-02 Benedikt Meurer <benny@xfce.org> * configure.in.in: Add checks for functions used in the user module. * thunar-vfs/thunar-vfs-user.{c,h}: Add a user module to the VFS library, which is extensible and performs some caching to reduce the overhead caused by the underlying NSS implementation. * docs/papers/HackingOnThunar.odt: Add comments about the newly added ThunarVfsUser module. * thunar-vfs/thunar-vfs-info.h: Add ThunarVfsUserId and ThunarVfsGroupId typedefs. * thunar-vfs/thunar-vfs.h, thunar-vfs/Makefile.am: Add the new ThunarVfsUser module to the build framework. * thunar/thunar-file.{c,h}, thunar/thunar-local-file.c, thunar/thunar-trash-file.c: Add two new methods to the ThunarFile class - get_group() and get_user() - and add an implementation of these methods to the local and trash backends. (Old svn revision: 16363) --- ChangeLog | 17 + configure.in.in | 6 +- docs/papers/HackingOnThunar.odt | Bin 16367 -> 16595 bytes thunar-vfs/Makefile.am | 2 + thunar-vfs/thunar-vfs-info.h | 16 +- thunar-vfs/thunar-vfs-user.c | 938 ++++++++++++++++++++++++++++++++ thunar-vfs/thunar-vfs-user.h | 129 +++++ thunar-vfs/thunar-vfs.h | 1 + thunar/thunar-file.c | 100 +++- thunar/thunar-file.h | 6 + thunar/thunar-local-file.c | 27 + thunar/thunar-trash-file.c | 20 + 12 files changed, 1239 insertions(+), 23 deletions(-) create mode 100644 thunar-vfs/thunar-vfs-user.c create mode 100644 thunar-vfs/thunar-vfs-user.h diff --git a/ChangeLog b/ChangeLog index 8585c8095..32d9a1b27 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2005-07-02 Benedikt Meurer <benny@xfce.org> + + * configure.in.in: Add checks for functions used in the user module. + * thunar-vfs/thunar-vfs-user.{c,h}: Add a user module to the VFS + library, which is extensible and performs some caching to reduce + the overhead caused by the underlying NSS implementation. + * docs/papers/HackingOnThunar.odt: Add comments about the newly added + ThunarVfsUser module. + * thunar-vfs/thunar-vfs-info.h: Add ThunarVfsUserId and ThunarVfsGroupId + typedefs. + * thunar-vfs/thunar-vfs.h, thunar-vfs/Makefile.am: Add the new + ThunarVfsUser module to the build framework. + * thunar/thunar-file.{c,h}, thunar/thunar-local-file.c, + thunar/thunar-trash-file.c: Add two new methods to the ThunarFile + class - get_group() and get_user() - and add an implementation of + these methods to the local and trash backends. + 2005-06-29 Benedikt Meurer <benny@xfce.org> * thunar/thunar-details-view-icon-renderer.c diff --git a/configure.in.in b/configure.in.in index 9502fe587..41426884b 100644 --- a/configure.in.in +++ b/configure.in.in @@ -41,12 +41,12 @@ AC_PROG_INSTALL() AC_PROG_LIBTOOL() dnl Check for standard headers -AC_CHECK_HEADERS([errno.h fcntl.h fstab.h memory.h stdlib.h string.h \ - sys/cdio.h sys/event.h \ +AC_CHECK_HEADERS([errno.h fcntl.h fstab.h grp.h memory.h pwd.h \ + stdlib.h string.h sys/cdio.h sys/event.h \ sys/mount.h sys/stat.h sys/time.h sys/param.h time.h]) dnl Check for standard functions -AC_CHECK_FUNCS([kqueue localtime_r]) +AC_CHECK_FUNCS([kqueue localtime_r setgroupent setpassent]) dnl Check for required packages XDT_CHECK_PACKAGE([EXO], [exo-0.3], [0.3.1]) diff --git a/docs/papers/HackingOnThunar.odt b/docs/papers/HackingOnThunar.odt index 08be983bb8f0571eb3e043092adc637e88e49475..c2ff310f16ba07fdb5b5d9b5b8006dab147686f3 100644 GIT binary patch delta 12409 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�K|N;rOci zFIPw~WjW>n>?1IUi`*y<nkD+w3I5__%E`)bRSrPK-Zt6@$dzQ~r!J&p)Wz$JlDA>m z#BElOL&Om~`U{U1_YcK!sOJeB@G2~jE5kU<PU(oB2~o>in}HBFQLs1v8FdrWBL)j< zY|3h72dNzA@Xe2GcK3k}1|k9plvG>V@FmOhV`dIdwNQ=uacXrPPKzjoouuEgn6v2k z`Yoi>%^&kD2E^T7Yi!~f@Taw>1n0LhdeGh@*)|)*ut32|-=toU*gH;aibO7`C>bUJ z@oeUMh>i#bisZ6;TgZwu{-pdxg^&>sA759bgR$aORj@T1LGS4s3qu0d<0D*cOcX*i zvdvio@4a}ADM_v3IrmmhkWAtjZa;m$Zyv%$L((Ju5_O-2Y&o?IssR$!oP`GMYEfX0 ze)gTq4K}0O6}#f$Jc{-K2%tjvr(e6m1nfdW0RZ3x|Mu(u<*qlN2!w(C(<w6q+5eGK zg8ctTBq0u@zYYEWXzxi%LQEuoE&qA={Y!WM{|hcDQwSgQ*XKV61pNPZ_@ix50swCA zKK2%FfBEq$@?hYYfd3=v|1&<4AOV2Cfc>wyVITnTcUb&q)V~CL_WRqJ9S8tG=T8(a z%729gvL}fNQ|tV#=|5ir;(s}nasR2w!rk4*(dxgd`ZvFxXZg7#7yyv+CqLGIMII*g M2-8A43jH<yKTOwsrvLx| delta 12042 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 diff --git a/thunar-vfs/Makefile.am b/thunar-vfs/Makefile.am index 8d176d2f5..78f760f54 100644 --- a/thunar-vfs/Makefile.am +++ b/thunar-vfs/Makefile.am @@ -17,6 +17,7 @@ libthunar_vfs_headers = \ thunar-vfs-info.h \ thunar-vfs-monitor.h \ thunar-vfs-trash.h \ + thunar-vfs-user.h \ thunar-vfs-util.h \ thunar-vfs-uri.h \ thunar-vfs-volume.h \ @@ -28,6 +29,7 @@ libthunar_vfs_la_SOURCES = \ thunar-vfs-info.c \ thunar-vfs-monitor.c \ thunar-vfs-trash.c \ + thunar-vfs-user.c \ thunar-vfs-util.c \ thunar-vfs-uri.c \ thunar-vfs-volume.c \ diff --git a/thunar-vfs/thunar-vfs-info.h b/thunar-vfs/thunar-vfs-info.h index b62495557..6d1ab3eff 100644 --- a/thunar-vfs/thunar-vfs-info.h +++ b/thunar-vfs/thunar-vfs-info.h @@ -134,6 +134,18 @@ typedef off_t ThunarVfsFileSize; **/ typedef time_t ThunarVfsFileTime; +/** + * ThunarVfsGroupId: + * Datatype to represent a group id. + **/ +typedef gid_t ThunarVfsGroupId; + +/** + * ThunarVfsUserId: + * Datatype to represent a user id. + **/ +typedef uid_t ThunarVfsUserId; + /** * ThunarVfsInfo: * @@ -152,10 +164,10 @@ typedef struct ThunarVfsFileFlags flags; /* Owner's user id */ - uid_t uid; + ThunarVfsUserId uid; /* Owner's group id */ - gid_t gid; + ThunarVfsGroupId gid; /* Size in bytes */ ThunarVfsFileSize size; diff --git a/thunar-vfs/thunar-vfs-user.c b/thunar-vfs/thunar-vfs-user.c new file mode 100644 index 000000000..7c6f4ec1a --- /dev/null +++ b/thunar-vfs/thunar-vfs-user.c @@ -0,0 +1,938 @@ +/* $Id$ */ +/*- + * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +#ifdef HAVE_GRP_H +#include <grp.h> +#endif +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <thunar-vfs/thunar-vfs-user.h> + + + +/* the interval in which the user/group cache is flushed (in ms) */ +#define THUNAR_VFS_USER_MANAGER_FLUSH_INTERVAL (10 * 60 * 1000) + + + +GType +thunar_vfs_group_get_type (void) +{ + static GType type = G_TYPE_INVALID; + + if (G_UNLIKELY (type == G_TYPE_INVALID)) + { + static const GTypeInfo info = + { + sizeof (ThunarVfsGroupClass), + NULL, + NULL, + NULL, + NULL, + NULL, + sizeof (ThunarVfsGroup), + 0, + NULL, + NULL, + }; + + type = g_type_register_static (G_TYPE_OBJECT, "ThunarVfsGroup", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + + + +/** + * thunar_vfs_group_get_id: + * @group : a #ThunarVfsGroup. + * + * Returns the unique id of the given @group. + * + * Return value: the unique id of @group. + **/ +ThunarVfsGroupId +thunar_vfs_group_get_id (ThunarVfsGroup *group) +{ + g_return_val_if_fail (THUNAR_VFS_IS_GROUP (group), 0); + return THUNAR_VFS_GROUP_GET_CLASS (group)->get_id (group); +} + + + +/** + * thunar_vfs_group_get_name: + * @group : a #ThunarVfsGroup. + * + * Returns the name of the @group. If the system is + * unable to determine the name of @group, it'll + * return the group id as string. + * + * Return value: the name of @group. + **/ +const gchar* +thunar_vfs_group_get_name (ThunarVfsGroup *group) +{ + g_return_val_if_fail (THUNAR_VFS_IS_GROUP (group), NULL); + return THUNAR_VFS_GROUP_GET_CLASS (group)->get_name (group); +} + + + + +GType +thunar_vfs_user_get_type (void) +{ + static GType type = G_TYPE_INVALID; + + if (G_UNLIKELY (type == G_TYPE_INVALID)) + { + static const GTypeInfo info = + { + sizeof (ThunarVfsUserClass), + NULL, + NULL, + NULL, + NULL, + NULL, + sizeof (ThunarVfsUser), + 0, + NULL, + NULL, + }; + + type = g_type_register_static (G_TYPE_OBJECT, "ThunarVfsUser", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + + + +/** + * thunar_vfs_user_get_groups: + * @user : a #ThunarVfsUser. + * + * Returns all #ThunarVfsGroup<!---->s that @user + * belongs to. The returned list and the #ThunarVfsGroup<!---->s + * contained within the list are owned by @user and must not be + * freed or altered by the caller. + * + * Note that if @user has a primary group, this group will + * also be contained in the returned list. + * + * Return value: the groups that @user belongs to. + **/ +GList* +thunar_vfs_user_get_groups (ThunarVfsUser *user) +{ + g_return_val_if_fail (THUNAR_VFS_IS_USER (user), NULL); + return THUNAR_VFS_USER_GET_CLASS (user)->get_groups (user); +} + + + +/** + * thunar_vfs_user_get_primary_group: + * @user : a #ThunarVfsUser. + * + * Returns the primary group of @user or %NULL if @user + * has no primary group. + * + * No reference is taken for the caller, so you must + * not call #g_object_unref() on the returned object. + * + * Return value: the primary group of @user or %NULL. + **/ +ThunarVfsGroup* +thunar_vfs_user_get_primary_group (ThunarVfsUser *user) +{ + g_return_val_if_fail (THUNAR_VFS_IS_USER (user), NULL); + return THUNAR_VFS_USER_GET_CLASS (user)->get_primary_group (user); +} + + + +/** + * thunar_vfs_user_get_id: + * @user : a #ThunarVfsUser. + * + * Returns the unique id of @user. + * + * Return value: the unique id of @user. + **/ +ThunarVfsUserId +thunar_vfs_user_get_id (ThunarVfsUser *user) +{ + g_return_val_if_fail (THUNAR_VFS_IS_USER (user), 0); + return THUNAR_VFS_USER_GET_CLASS (user)->get_id (user); +} + + + +/** + * thunar_vfs_user_get_name: + * @user : a #ThunarVfsUser. + * + * Returns the name of @user. If the system is + * unable to determine the account name of @user, + * it'll return the user id as string. + * + * Return value: the name of @user. + **/ +const gchar* +thunar_vfs_user_get_name (ThunarVfsUser *user) +{ + g_return_val_if_fail (THUNAR_VFS_IS_USER (user), 0); + return THUNAR_VFS_USER_GET_CLASS (user)->get_name (user); +} + + + +/** + * thunar_vfs_user_get_real_name: + * @user : a #ThunarVfsUser. + * + * Returns the real name of @user or %NULL if the + * real name for @user is not known to the underlying + * system. + * + * Return value: the real name for @user or %NULL. + **/ +const gchar* +thunar_vfs_user_get_real_name (ThunarVfsUser *user) +{ + g_return_val_if_fail (THUNAR_VFS_IS_USER (user), 0); + return THUNAR_VFS_USER_GET_CLASS (user)->get_real_name (user); +} + + + +/** + * thunar_vfs_user_is_me: + * @user : a #ThunarVfsUser. + * + * Checks whether the owner of the current process is + * described by @user. + * + * Return value: %TRUE if @user is the owner of the current + * process, else %FALSE. + **/ +gboolean +thunar_vfs_user_is_me (ThunarVfsUser *user) +{ + g_return_val_if_fail (THUNAR_VFS_IS_USER (user), FALSE); + return THUNAR_VFS_USER_GET_CLASS (user)->is_me (user); +} + + + + +typedef struct _ThunarVfsLocalGroupClass ThunarVfsLocalGroupClass; +typedef struct _ThunarVfsLocalGroup ThunarVfsLocalGroup; + + + +#define THUNAR_VFS_TYPE_LOCAL_GROUP (thunar_vfs_local_group_get_type ()) +#define THUNAR_VFS_LOCAL_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_VFS_TYPE_LOCAL_GROUP, ThunarVfsLocalGroup)) +#define THUNAR_VFS_LOCAL_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_VFS_TYPE_LOCAL_GROUP, ThunarVfsLocalGroupClass)) +#define THUNAR_VFS_IS_LOCAL_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_VFS_TYPE_LOCAL_GROUP)) +#define THUNAR_VFS_IS_LOCAL_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_VFS_TYPE_LOCAL_GROUP)) +#define THUNAR_VFS_LOCAL_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_VFS_TYPE_LOCAL_GROUP, ThunarVfsLocalGroupClass)) + + + +static GType thunar_vfs_local_group_get_type (void) G_GNUC_CONST; +static void thunar_vfs_local_group_class_init (ThunarVfsLocalGroupClass *klass); +static void thunar_vfs_local_group_finalize (GObject *object); +static ThunarVfsGroupId thunar_vfs_local_group_get_id (ThunarVfsGroup *group); +static const gchar *thunar_vfs_local_group_get_name (ThunarVfsGroup *group); +static ThunarVfsGroup *thunar_vfs_local_group_new (ThunarVfsGroupId id); + + + +struct _ThunarVfsLocalGroupClass +{ + ThunarVfsGroupClass __parent__; +}; + +struct _ThunarVfsLocalGroup +{ + ThunarVfsGroup __parent__; + + ThunarVfsGroupId id; + gchar *name; +}; + + + +static GObjectClass *thunar_vfs_local_group_parent_class; + + + +static GType +thunar_vfs_local_group_get_type (void) +{ + static GType type = G_TYPE_INVALID; + + if (G_UNLIKELY (type == G_TYPE_INVALID)) + { + static const GTypeInfo info = + { + sizeof (ThunarVfsLocalGroupClass), + NULL, + NULL, + (GClassInitFunc) thunar_vfs_local_group_class_init, + NULL, + NULL, + sizeof (ThunarVfsLocalGroup), + 0, + NULL, + NULL, + }; + + type = g_type_register_static (THUNAR_VFS_TYPE_GROUP, + "ThunarVfsLocalGroup", + &info, 0); + } + + return type; +} + + + +static void +thunar_vfs_local_group_class_init (ThunarVfsLocalGroupClass *klass) +{ + ThunarVfsGroupClass *thunarvfs_group_class; + GObjectClass *gobject_class; + + thunar_vfs_local_group_parent_class = g_type_class_peek_parent (klass); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = thunar_vfs_local_group_finalize; + + thunarvfs_group_class = THUNAR_VFS_GROUP_CLASS (klass); + thunarvfs_group_class->get_id = thunar_vfs_local_group_get_id; + thunarvfs_group_class->get_name = thunar_vfs_local_group_get_name; +} + + + +static void +thunar_vfs_local_group_finalize (GObject *object) +{ + ThunarVfsLocalGroup *local_group = THUNAR_VFS_LOCAL_GROUP (object); + + /* free the group name */ + g_free (local_group->name); + + G_OBJECT_CLASS (thunar_vfs_local_group_parent_class)->finalize (object); +} + + + +static ThunarVfsGroupId +thunar_vfs_local_group_get_id (ThunarVfsGroup *group) +{ + return THUNAR_VFS_LOCAL_GROUP (group)->id; +} + + + +static const gchar* +thunar_vfs_local_group_get_name (ThunarVfsGroup *group) +{ + ThunarVfsLocalGroup *local_group = THUNAR_VFS_LOCAL_GROUP (group); + struct group *grp; + + /* determine the name on-demand */ + if (G_UNLIKELY (local_group->name == NULL)) + { + grp = getgrgid (local_group->id); + if (G_LIKELY (grp != NULL)) + local_group->name = g_strdup (grp->gr_name); + else + local_group->name = g_strdup_printf ("%u", (guint) local_group->id); + } + + return local_group->name; +} + + + +static ThunarVfsGroup* +thunar_vfs_local_group_new (ThunarVfsGroupId id) +{ + ThunarVfsLocalGroup *local_group; + + local_group = g_object_new (THUNAR_VFS_TYPE_LOCAL_GROUP, NULL); + local_group->id = id; + + return THUNAR_VFS_GROUP (local_group); +} + + + + +typedef struct _ThunarVfsLocalUserClass ThunarVfsLocalUserClass; +typedef struct _ThunarVfsLocalUser ThunarVfsLocalUser; + + + +#define THUNAR_VFS_TYPE_LOCAL_USER (thunar_vfs_local_user_get_type ()) +#define THUNAR_VFS_LOCAL_USER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_VFS_TYPE_LOCAL_USER, ThunarVfsLocalUser)) +#define THUNAR_VFS_LOCAL_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_VFS_TYPE_LOCAL_USER, ThunarVfsLocalUserClass)) +#define THUNAR_VFS_IS_LOCAL_USER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_VFS_TYPE_LOCAL_USER)) +#define THUNAR_VFS_IS_LOCAL_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_VFS_TYPE_LOCAL_USER)) +#define THUNAR_VFS_LOCAL_USER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_VFS_TYPE_LOCAL_USER, ThunarVfsLocalUserClass)) + + + +static GType thunar_vfs_local_user_get_type (void) G_GNUC_CONST; +static void thunar_vfs_local_user_class_init (ThunarVfsLocalUserClass *klass); +static void thunar_vfs_local_user_finalize (GObject *object); +static GList *thunar_vfs_local_user_get_groups (ThunarVfsUser *user); +static ThunarVfsGroup *thunar_vfs_local_user_get_primary_group (ThunarVfsUser *user); +static ThunarVfsUserId thunar_vfs_local_user_get_id (ThunarVfsUser *user); +static const gchar *thunar_vfs_local_user_get_name (ThunarVfsUser *user); +static const gchar *thunar_vfs_local_user_get_real_name (ThunarVfsUser *user); +static gboolean thunar_vfs_local_user_is_me (ThunarVfsUser *user); +static void thunar_vfs_local_user_load (ThunarVfsLocalUser *local_user); +static ThunarVfsUser *thunar_vfs_local_user_new (ThunarVfsUserId id); + + + +struct _ThunarVfsLocalUserClass +{ + ThunarVfsUserClass __parent__; + + ThunarVfsUserId effective_uid; +}; + +struct _ThunarVfsLocalUser +{ + ThunarVfsUser __parent__; + + GList *groups; + ThunarVfsGroup *primary_group; + ThunarVfsUserId id; + gchar *name; + gchar *real_name; +}; + + + +static GObjectClass *thunar_vfs_local_user_parent_class; + + + +static GType +thunar_vfs_local_user_get_type (void) +{ + static GType type = G_TYPE_INVALID; + + if (G_UNLIKELY (type == G_TYPE_INVALID)) + { + static const GTypeInfo info = + { + sizeof (ThunarVfsLocalUserClass), + NULL, + NULL, + (GClassInitFunc) thunar_vfs_local_user_class_init, + NULL, + NULL, + sizeof (ThunarVfsLocalUser), + 0, + NULL, + NULL, + }; + + type = g_type_register_static (THUNAR_VFS_TYPE_USER, + "ThunarVfsLocalUser", + &info, 0); + } + + return type; +} + + + +static void +thunar_vfs_local_user_class_init (ThunarVfsLocalUserClass *klass) +{ + ThunarVfsUserClass *thunarvfs_user_class; + GObjectClass *gobject_class; + + /* determine the current process' effective user id, we do + * this only once to avoid the syscall overhead on every + * is_me() invokation. + */ + klass->effective_uid = geteuid (); + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = thunar_vfs_local_user_finalize; + + thunarvfs_user_class = THUNAR_VFS_USER_CLASS (klass); + thunarvfs_user_class->get_groups = thunar_vfs_local_user_get_groups; + thunarvfs_user_class->get_primary_group = thunar_vfs_local_user_get_primary_group; + thunarvfs_user_class->get_id = thunar_vfs_local_user_get_id; + thunarvfs_user_class->get_name = thunar_vfs_local_user_get_name; + thunarvfs_user_class->get_real_name = thunar_vfs_local_user_get_real_name; + thunarvfs_user_class->is_me = thunar_vfs_local_user_is_me; +} + + + +static void +thunar_vfs_local_user_finalize (GObject *object) +{ + ThunarVfsLocalUser *local_user = THUNAR_VFS_LOCAL_USER (object); + GList *lp; + + /* unref the associated groups */ + for (lp = local_user->groups; lp != NULL; lp = lp->next) + g_object_unref (G_OBJECT (lp->data)); + g_list_free (local_user->groups); + + /* drop the ref on the primary group */ + if (G_LIKELY (local_user->primary_group != NULL)) + g_object_unref (G_OBJECT (local_user->primary_group)); + + /* free the names */ + g_free (local_user->real_name); + g_free (local_user->name); + + G_OBJECT_CLASS (thunar_vfs_local_user_parent_class)->finalize (object); +} + + + +static GList* +thunar_vfs_local_user_get_groups (ThunarVfsUser *user) +{ + ThunarVfsUserManager *manager; + ThunarVfsLocalUser *local_user = THUNAR_VFS_LOCAL_USER (user); + ThunarVfsGroup *primary_group; + ThunarVfsGroup *group; + gid_t gidset[NGROUPS_MAX]; + gint gidsetlen; + gint n; + + /* load the groups on-demand */ + if (G_UNLIKELY (local_user->groups == NULL)) + { + primary_group = thunar_vfs_local_user_get_primary_group (user); + + /* we can only determine the groups list for the current + * process owner in a portable fashion, and in fact, we + * only need the list for the current user. + */ + if (thunar_vfs_local_user_is_me (user)) + { + manager = thunar_vfs_user_manager_get_default (); + + /* add all supplementary groups */ + gidsetlen = getgroups (G_N_ELEMENTS (gidset), gidset); + for (n = 0; n < gidsetlen; ++n) + if (primary_group == NULL || thunar_vfs_group_get_id (primary_group) != gidset[n]) + { + group = thunar_vfs_user_manager_get_group_by_id (manager, gidset[n]); + if (G_LIKELY (group != NULL)) + local_user->groups = g_list_append (local_user->groups, group); + } + + g_object_unref (G_OBJECT (manager)); + } + + /* prepend the primary group (if any) */ + if (G_LIKELY (primary_group != NULL)) + { + local_user->groups = g_list_prepend (local_user->groups, primary_group); + g_object_ref (G_OBJECT (primary_group)); + } + } + + return local_user->groups; +} + + + +static ThunarVfsGroup* +thunar_vfs_local_user_get_primary_group (ThunarVfsUser *user) +{ + ThunarVfsLocalUser *local_user = THUNAR_VFS_LOCAL_USER (user); + + if (G_UNLIKELY (local_user->name == NULL)) + thunar_vfs_local_user_load (local_user); + + return local_user->primary_group; +} + + + +static ThunarVfsUserId +thunar_vfs_local_user_get_id (ThunarVfsUser *user) +{ + return THUNAR_VFS_LOCAL_USER (user)->id; +} + + + +static const gchar* +thunar_vfs_local_user_get_name (ThunarVfsUser *user) +{ + ThunarVfsLocalUser *local_user = THUNAR_VFS_LOCAL_USER (user); + + if (G_UNLIKELY (local_user->name == NULL)) + thunar_vfs_local_user_load (local_user); + + return local_user->name; +} + + + +static const gchar* +thunar_vfs_local_user_get_real_name (ThunarVfsUser *user) +{ + ThunarVfsLocalUser *local_user = THUNAR_VFS_LOCAL_USER (user); + + if (G_UNLIKELY (local_user->name == NULL)) + thunar_vfs_local_user_load (local_user); + + return local_user->real_name; +} + + + +static gboolean +thunar_vfs_local_user_is_me (ThunarVfsUser *user) +{ + return (THUNAR_VFS_LOCAL_USER (user)->id == THUNAR_VFS_LOCAL_USER_GET_CLASS (user)->effective_uid); +} + + + +static void +thunar_vfs_local_user_load (ThunarVfsLocalUser *local_user) +{ + ThunarVfsUserManager *manager; + struct passwd *pw; + const gchar *s; + + g_return_if_fail (local_user->name == NULL); + + pw = getpwuid (local_user->id); + if (G_LIKELY (pw != NULL)) + { + manager = thunar_vfs_user_manager_get_default (); + + /* query name and primary group */ + local_user->name = g_strdup (pw->pw_name); + local_user->primary_group = thunar_vfs_user_manager_get_group_by_id (manager, pw->pw_gid); + + /* try to figure out the real name */ + s = strchr (pw->pw_gecos, ','); + if (s != NULL) + local_user->real_name = g_strndup (pw->pw_gecos, s - pw->pw_gecos); + else if (pw->pw_gecos[0] != '\0') + local_user->real_name = g_strdup (pw->pw_gecos); + + g_object_unref (G_OBJECT (manager)); + } + else + { + local_user->name = g_strdup_printf ("%u", (guint) local_user->id); + } +} + + + +static ThunarVfsUser* +thunar_vfs_local_user_new (ThunarVfsUserId id) +{ + ThunarVfsLocalUser *local_user; + + local_user = g_object_new (THUNAR_VFS_TYPE_LOCAL_USER, NULL); + local_user->id = id; + + return THUNAR_VFS_USER (local_user); +} + + + + +static void thunar_vfs_user_manager_class_init (ThunarVfsUserManagerClass *klass); +static void thunar_vfs_user_manager_init (ThunarVfsUserManager *manager); +static void thunar_vfs_user_manager_finalize (GObject *object); +static gboolean thunar_vfs_user_manager_flush_timer (gpointer user_data); +static void thunar_vfs_user_manager_flush_timer_destroy (gpointer user_data); + + + +struct _ThunarVfsUserManagerClass +{ + GObjectClass __parent__; +}; + +struct _ThunarVfsUserManager +{ + GObject __parent__; + + GHashTable *groups; + GHashTable *users; + + gint flush_timer_id; +}; + + + +G_DEFINE_TYPE (ThunarVfsUserManager, thunar_vfs_user_manager, G_TYPE_OBJECT); + + + +static void +thunar_vfs_user_manager_class_init (ThunarVfsUserManagerClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = thunar_vfs_user_manager_finalize; +} + + + +static void +thunar_vfs_user_manager_init (ThunarVfsUserManager *manager) +{ + manager->groups = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, g_object_unref); + manager->users = g_hash_table_new_full (g_int_hash, g_int_equal, NULL, g_object_unref); + + /* keep the groups file in memory if possible */ +#ifdef HAVE_SETGROUPENT + setgroupent (TRUE); +#endif + + /* keep the passwd file in memory if possible */ +#ifdef HAVE_SETPASSENT + setpassent (TRUE); +#endif + + /* start the flush timer */ + manager->flush_timer_id = g_timeout_add_full (G_PRIORITY_LOW, THUNAR_VFS_USER_MANAGER_FLUSH_INTERVAL, + thunar_vfs_user_manager_flush_timer, manager, + thunar_vfs_user_manager_flush_timer_destroy); +} + + + +static void +thunar_vfs_user_manager_finalize (GObject *object) +{ + ThunarVfsUserManager *manager = THUNAR_VFS_USER_MANAGER (object); + + /* stop the flush timer */ + if (G_LIKELY (manager->flush_timer_id >= 0)) + g_source_remove (manager->flush_timer_id); + + /* destroy the hash tables */ + g_hash_table_destroy (manager->groups); + g_hash_table_destroy (manager->users); + + /* unload the groups file */ + endgrent (); + + /* unload the passwd file */ + endpwent (); + + G_OBJECT_CLASS (thunar_vfs_user_manager_parent_class)->finalize (object); +} + + + +static gboolean +thunar_vfs_user_manager_flush_timer (gpointer user_data) +{ + ThunarVfsUserManager *manager = THUNAR_VFS_USER_MANAGER (user_data); + guint size = 0; + + GDK_THREADS_ENTER (); + + /* drop all cached groups */ + size += g_hash_table_foreach_remove (manager->groups, (GHRFunc) gtk_true, NULL); + + /* drop all cached users */ + size += g_hash_table_foreach_remove (manager->users, (GHRFunc) gtk_true, NULL); + + /* reload groups and passwd files if we had cached entities */ + if (G_LIKELY (size > 0)) + { + endgrent (); + endpwent (); + +#ifdef HAVE_SETGROUPENT + setgroupent (TRUE); +#endif + +#ifdef HAVE_SETPASSENT + setpassent (TRUE); +#endif + } + + GDK_THREADS_LEAVE (); + + return TRUE; +} + + + +static void +thunar_vfs_user_manager_flush_timer_destroy (gpointer user_data) +{ + GDK_THREADS_ENTER (); + THUNAR_VFS_USER_MANAGER (user_data)->flush_timer_id = -1; + GDK_THREADS_LEAVE (); +} + + + +/** + * thunar_vfs_user_manager_get_default: + * + * Returns the default #ThunarVfsUserManager instance, which is shared + * by all modules using the user module. Call #g_object_unref() on the + * returned object when you are done with it. + * + * Return value: the default #ThunarVfsUserManager instance. + **/ +ThunarVfsUserManager* +thunar_vfs_user_manager_get_default (void) +{ + static ThunarVfsUserManager *manager = NULL; + + if (G_UNLIKELY (manager == NULL)) + { + manager = g_object_new (THUNAR_VFS_TYPE_USER_MANAGER, NULL); + g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager); + } + else + { + g_object_ref (G_OBJECT (manager)); + } + + return manager; +} + + + +/** + * thunar_vfs_user_manager_get_group_by_id: + * @manager : a #ThunarVfsUserManager. + * @id : the group id. + * + * Looks up the #ThunarVfsGroup corresponding to @id in @manager. Returns + * %NULL if @manager is unable to determine the #ThunarVfsGroup for @id, + * else a pointer to the corresponding #ThunarVfsGroup. The caller is + * responsible for freeing the returned object using #g_object_unref(). + * + * Return value: the #ThunarVfsGroup corresponding to @id or %NULL. + **/ +ThunarVfsGroup* +thunar_vfs_user_manager_get_group_by_id (ThunarVfsUserManager *manager, + ThunarVfsGroupId id) +{ + ThunarVfsGroup *group; + + g_return_val_if_fail (THUNAR_VFS_IS_USER_MANAGER (manager), NULL); + + /* lookup/load the group corresponding to id */ + group = g_hash_table_lookup (manager->groups, GINT_TO_POINTER (id)); + if (group == NULL) + { + group = thunar_vfs_local_group_new (id); + g_hash_table_insert (manager->groups, GINT_TO_POINTER (id), group); + } + + /* take a reference for the caller */ + g_object_ref (G_OBJECT (group)); + + return group; +} + + + +/** + * thunar_vfs_user_manager_get_user_by_id: + * @manager : a #ThunarVfsUserManager. + * @id : the user id. + * + * Looks up the #ThunarVfsUser corresponding to @id in @manager. Returns + * %NULL if @manager is unable to determine the #ThunarVfsUser for @id, + * else a pointer to the corresponding #ThunarVfsUser. The caller is + * responsible for freeing the returned object using #g_object_unref(). + * + * Return value: the #ThunarVfsUser corresponding to @id or %NULL. + **/ +ThunarVfsUser* +thunar_vfs_user_manager_get_user_by_id (ThunarVfsUserManager *manager, + ThunarVfsUserId id) +{ + ThunarVfsUser *user; + + g_return_val_if_fail (THUNAR_VFS_IS_USER_MANAGER (manager), NULL); + + /* lookup/load the user corresponding to id */ + user = g_hash_table_lookup (manager->users, GINT_TO_POINTER (id)); + if (user == NULL) + { + user = thunar_vfs_local_user_new (id); + g_hash_table_insert (manager->users, GINT_TO_POINTER (id), user); + } + + /* take a reference for the caller */ + g_object_ref (G_OBJECT (user)); + + return user; +} diff --git a/thunar-vfs/thunar-vfs-user.h b/thunar-vfs/thunar-vfs-user.h new file mode 100644 index 000000000..6a30c6448 --- /dev/null +++ b/thunar-vfs/thunar-vfs-user.h @@ -0,0 +1,129 @@ +/* $Id$ */ +/*- + * Copyright (c) 2005 Benedikt Meurer <benny@xfce.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __THUNAR_VFS_USER_H__ +#define __THUNAR_VFS_USER_H__ + +#include <thunar-vfs/thunar-vfs-info.h> + +G_BEGIN_DECLS; + +typedef struct _ThunarVfsGroupClass ThunarVfsGroupClass; +typedef struct _ThunarVfsGroup ThunarVfsGroup; + +#define THUNAR_VFS_TYPE_GROUP (thunar_vfs_group_get_type ()) +#define THUNAR_VFS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_VFS_TYPE_GROUP, ThunarVfsGroup)) +#define THUNAR_VFS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_VFS_TYPE_GROUP, ThunarVfsGroupClass)) +#define THUNAR_VFS_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_VFS_TYPE_GROUP)) +#define THUNAR_VFS_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_VFS_TYPE_GROUP)) +#define THUNAR_VFS_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_VFS_TYPE_GROUP, ThunarVfsGroupClass)) + +struct _ThunarVfsGroupClass +{ + GObjectClass __parent__; + + /* virtual methods */ + ThunarVfsGroupId (*get_id) (ThunarVfsGroup *group); + const gchar *(*get_name) (ThunarVfsGroup *group); + + /*< private >*/ + void (*reserved1) (void); + void (*reserved2) (void); + void (*reserved3) (void); + void (*reserved4) (void); +}; + +struct _ThunarVfsGroup +{ + GObject __parent__; +}; + +GType thunar_vfs_group_get_type (void) G_GNUC_CONST; + +ThunarVfsGroupId thunar_vfs_group_get_id (ThunarVfsGroup *group); +const gchar *thunar_vfs_group_get_name (ThunarVfsGroup *group); + + +typedef struct _ThunarVfsUserClass ThunarVfsUserClass; +typedef struct _ThunarVfsUser ThunarVfsUser; + +#define THUNAR_VFS_TYPE_USER (thunar_vfs_user_get_type ()) +#define THUNAR_VFS_USER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_VFS_TYPE_USER, ThunarVfsUser)) +#define THUNAR_VFS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_VFS_TYPE_USER, ThunarVfsUserClass)) +#define THUNAR_VFS_IS_USER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_VFS_TYPE_USER)) +#define THUNAR_VFS_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_VFS_TYPE_USER)) +#define THUNAR_VFS_USER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_VFS_TYPE_USER, ThunarVfsUserClass)) + +struct _ThunarVfsUserClass +{ + GObjectClass __parent__; + + /* virtual methods */ + GList *(*get_groups) (ThunarVfsUser *user); + ThunarVfsGroup *(*get_primary_group) (ThunarVfsUser *user); + ThunarVfsUserId (*get_id) (ThunarVfsUser *user); + const gchar *(*get_name) (ThunarVfsUser *user); + const gchar *(*get_real_name) (ThunarVfsUser *user); + gboolean (*is_me) (ThunarVfsUser *user); + + /*< private >*/ + void (*reserved1) (void); + void (*reserved2) (void); + void (*reserved3) (void); + void (*reserved4) (void); +}; + +struct _ThunarVfsUser +{ + GObject __parent__; +}; + +GType thunar_vfs_user_get_type (void) G_GNUC_CONST; + +GList *thunar_vfs_user_get_groups (ThunarVfsUser *user); +ThunarVfsGroup *thunar_vfs_user_get_primary_group (ThunarVfsUser *user); +ThunarVfsUserId thunar_vfs_user_get_id (ThunarVfsUser *user); +const gchar *thunar_vfs_user_get_name (ThunarVfsUser *user); +const gchar *thunar_vfs_user_get_real_name (ThunarVfsUser *user); +gboolean thunar_vfs_user_is_me (ThunarVfsUser *user); + + +typedef struct _ThunarVfsUserManagerClass ThunarVfsUserManagerClass; +typedef struct _ThunarVfsUserManager ThunarVfsUserManager; + +#define THUNAR_VFS_TYPE_USER_MANAGER (thunar_vfs_user_manager_get_type ()) +#define THUNAR_VFS_USER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_VFS_TYPE_USER_MANAGER, ThunarVfsUserManager)) +#define THUNAR_VFS_USER_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_VFS_TYPE_USER_MANAGER, ThunarVfsUserManagerClass)) +#define THUNAR_VFS_IS_USER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_VFS_TYPE_USER_MANAGER)) +#define THUNAR_VFS_IS_USER_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_VFS_TYPE_USER_MANAGER)) +#define THUNAR_VFS_USER_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_VFS_TYPE_USER_MANAGER, ThunarVfsUserManagerClass)) + +GType thunar_vfs_user_manager_get_type (void) G_GNUC_CONST; + +ThunarVfsUserManager *thunar_vfs_user_manager_get_default (void); + +ThunarVfsGroup *thunar_vfs_user_manager_get_group_by_id (ThunarVfsUserManager *manager, + ThunarVfsGroupId id); +ThunarVfsUser *thunar_vfs_user_manager_get_user_by_id (ThunarVfsUserManager *manager, + ThunarVfsUserId id); + +G_END_DECLS; + +#endif /* !__THUNAR_VFS_USER_H__ */ diff --git a/thunar-vfs/thunar-vfs.h b/thunar-vfs/thunar-vfs.h index 31170a9db..24c597b17 100644 --- a/thunar-vfs/thunar-vfs.h +++ b/thunar-vfs/thunar-vfs.h @@ -26,6 +26,7 @@ #include <thunar-vfs/thunar-vfs-monitor.h> #include <thunar-vfs/thunar-vfs-trash.h> #include <thunar-vfs/thunar-vfs-uri.h> +#include <thunar-vfs/thunar-vfs-user.h> #include <thunar-vfs/thunar-vfs-util.h> #include <thunar-vfs/thunar-vfs-volume.h> diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c index 5099da20d..7aa4e25d7 100644 --- a/thunar/thunar-file.c +++ b/thunar/thunar-file.c @@ -43,24 +43,26 @@ enum -static void thunar_file_class_init (ThunarFileClass *klass); -static void thunar_file_finalize (GObject *object); -static gboolean thunar_file_real_has_parent (ThunarFile *file); -static ThunarFile *thunar_file_real_get_parent (ThunarFile *file, - GError **error); -static ThunarFolder*thunar_file_real_open_as_folder (ThunarFile *file, - GError **error); -static ExoMimeInfo *thunar_file_real_get_mime_info (ThunarFile *file); -static const gchar *thunar_file_real_get_special_name (ThunarFile *file); -static gboolean thunar_file_real_get_date (ThunarFile *file, - ThunarFileDateType date_type, - ThunarVfsFileTime *date_return); -static gboolean thunar_file_real_get_size (ThunarFile *file, - ThunarVfsFileSize *size_return); -static GList *thunar_file_real_get_emblem_names (ThunarFile *file); -static void thunar_file_real_changed (ThunarFile *file); -static void thunar_file_destroyed (gpointer data, - GObject *object); +static void thunar_file_class_init (ThunarFileClass *klass); +static void thunar_file_finalize (GObject *object); +static gboolean thunar_file_real_has_parent (ThunarFile *file); +static ThunarFile *thunar_file_real_get_parent (ThunarFile *file, + GError **error); +static ThunarFolder *thunar_file_real_open_as_folder (ThunarFile *file, + GError **error); +static ExoMimeInfo *thunar_file_real_get_mime_info (ThunarFile *file); +static const gchar *thunar_file_real_get_special_name (ThunarFile *file); +static gboolean thunar_file_real_get_date (ThunarFile *file, + ThunarFileDateType date_type, + ThunarVfsFileTime *date_return); +static gboolean thunar_file_real_get_size (ThunarFile *file, + ThunarVfsFileSize *size_return); +static ThunarVfsGroup *thunar_file_real_get_group (ThunarFile *file); +static ThunarVfsUser *thunar_file_real_get_user (ThunarFile *file); +static GList *thunar_file_real_get_emblem_names (ThunarFile *file); +static void thunar_file_real_changed (ThunarFile *file); +static void thunar_file_destroyed (gpointer data, + GObject *object); @@ -158,6 +160,8 @@ thunar_file_class_init (ThunarFileClass *klass) klass->get_special_name = thunar_file_real_get_special_name; klass->get_date = thunar_file_real_get_date; klass->get_size = thunar_file_real_get_size; + klass->get_group = thunar_file_real_get_group; + klass->get_user = thunar_file_real_get_user; klass->get_emblem_names = thunar_file_real_get_emblem_names; klass->changed = thunar_file_real_changed; @@ -284,6 +288,22 @@ thunar_file_real_get_size (ThunarFile *file, +static ThunarVfsGroup* +thunar_file_real_get_group (ThunarFile *file) +{ + return NULL; +} + + + +static ThunarVfsUser* +thunar_file_real_get_user (ThunarFile *file) +{ + return NULL; +} + + + static GList* thunar_file_real_get_emblem_names (ThunarFile *file) { @@ -794,6 +814,50 @@ thunar_file_get_size_string (ThunarFile *file) +/** + * thunar_file_get_group: + * @file : a #ThunarFile instance. + * + * Determines the #ThunarVfsGroup for @file. If there's no + * group associated with @file or if the system is unable to + * determine the group, %NULL will be returned. + * + * The caller is responsible for freeing the returned object + * using #g_object_unref(). + * + * Return value: the #ThunarVfsGroup for @file or %NULL. + **/ +ThunarVfsGroup* +thunar_file_get_group (ThunarFile *file) +{ + g_return_val_if_fail (THUNAR_IS_FILE (file), NULL); + return THUNAR_FILE_GET_CLASS (file)->get_group (file); +} + + + +/** + * thunar_file_get_user: + * @file : a #ThunarFile instance. + * + * Determines the #ThunarVfsUser for @file. If there's no + * user associated with @file or if the system is unable + * to determine the user, %NULL will be returned. + * + * The caller is responsible for freeing the returned object + * using #g_object_unref(). + * + * Return value: the #ThunarVfsUser for @file or %NULL. + **/ +ThunarVfsUser* +thunar_file_get_user (ThunarFile *file) +{ + g_return_val_if_fail (THUNAR_IS_FILE (file), NULL); + return THUNAR_FILE_GET_CLASS (file)->get_user (file); +} + + + /** * thunar_file_get_emblem_names: * @file : a #ThunarFile instance. diff --git a/thunar/thunar-file.h b/thunar/thunar-file.h index 9b86d149c..3d99ffbf1 100644 --- a/thunar/thunar-file.h +++ b/thunar/thunar-file.h @@ -85,6 +85,9 @@ struct _ThunarFileClass gboolean (*get_size) (ThunarFile *file, ThunarVfsFileSize *size_return); + ThunarVfsGroup *(*get_group) (ThunarFile *file); + ThunarVfsUser *(*get_user) (ThunarFile *file); + GList *(*get_emblem_names) (ThunarFile *file); const gchar *(*get_icon_name) (ThunarFile *file, GtkIconTheme *icon_theme); @@ -139,6 +142,9 @@ gchar *thunar_file_get_date_string (ThunarFile *file, gchar *thunar_file_get_mode_string (ThunarFile *file); gchar *thunar_file_get_size_string (ThunarFile *file); +ThunarVfsGroup *thunar_file_get_group (ThunarFile *file); +ThunarVfsUser *thunar_file_get_user (ThunarFile *file); + GList *thunar_file_get_emblem_names (ThunarFile *file); GdkPixbuf *thunar_file_load_icon (ThunarFile *file, gint size); diff --git a/thunar/thunar-local-file.c b/thunar/thunar-local-file.c index e3cf3bce7..e71c8a020 100644 --- a/thunar/thunar-local-file.c +++ b/thunar/thunar-local-file.c @@ -44,6 +44,8 @@ static ThunarVfsFileType thunar_local_file_get_kind (ThunarFile static ThunarVfsFileMode thunar_local_file_get_mode (ThunarFile *file); static gboolean thunar_local_file_get_size (ThunarFile *file, ThunarVfsFileSize *size_return); +static ThunarVfsGroup *thunar_local_file_get_group (ThunarFile *file); +static ThunarVfsUser *thunar_local_file_get_user (ThunarFile *file); static GList *thunar_local_file_get_emblem_names (ThunarFile *file); static const gchar *thunar_local_file_get_icon_name (ThunarFile *file, GtkIconTheme *icon_theme); @@ -60,6 +62,8 @@ static void thunar_local_file_monitor (ThunarVfsMonitor struct _ThunarLocalFileClass { ThunarFileClass __parent__; + + ThunarVfsUserManager *user_manager; }; struct _ThunarLocalFile @@ -87,6 +91,9 @@ thunar_local_file_class_init (ThunarLocalFileClass *klass) ThunarFileClass *thunarfile_class; GObjectClass *gobject_class; + /* query the user manager instance */ + klass->user_manager = thunar_vfs_user_manager_get_default (); + gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = thunar_local_file_finalize; @@ -101,6 +108,8 @@ thunar_local_file_class_init (ThunarLocalFileClass *klass) thunarfile_class->get_kind = thunar_local_file_get_kind; thunarfile_class->get_mode = thunar_local_file_get_mode; thunarfile_class->get_size = thunar_local_file_get_size; + thunarfile_class->get_group = thunar_local_file_get_group; + thunarfile_class->get_user = thunar_local_file_get_user; thunarfile_class->get_emblem_names = thunar_local_file_get_emblem_names; thunarfile_class->get_icon_name = thunar_local_file_get_icon_name; thunarfile_class->watch = thunar_local_file_watch; @@ -304,6 +313,24 @@ thunar_local_file_get_kind (ThunarFile *file) +static ThunarVfsGroup* +thunar_local_file_get_group (ThunarFile *file) +{ + return thunar_vfs_user_manager_get_group_by_id (THUNAR_LOCAL_FILE_GET_CLASS (file)->user_manager, + THUNAR_LOCAL_FILE (file)->info.gid); +} + + + +static ThunarVfsUser* +thunar_local_file_get_user (ThunarFile *file) +{ + return thunar_vfs_user_manager_get_user_by_id (THUNAR_LOCAL_FILE_GET_CLASS (file)->user_manager, + THUNAR_LOCAL_FILE (file)->info.uid); +} + + + static GList* thunar_local_file_get_emblem_names (ThunarFile *file) { diff --git a/thunar/thunar-trash-file.c b/thunar/thunar-trash-file.c index 04481c3eb..e6a34fac1 100644 --- a/thunar/thunar-trash-file.c +++ b/thunar/thunar-trash-file.c @@ -47,6 +47,8 @@ static ThunarVfsFileType thunar_trash_file_get_kind (ThunarFile static ThunarVfsFileMode thunar_trash_file_get_mode (ThunarFile *file); static gboolean thunar_trash_file_get_size (ThunarFile *file, ThunarVfsFileSize *size_return); +static ThunarVfsGroup *thunar_trash_file_get_group (ThunarFile *file); +static ThunarVfsUser *thunar_trash_file_get_user (ThunarFile *file); static const gchar *thunar_trash_file_get_icon_name (ThunarFile *file, GtkIconTheme *icon_theme); @@ -92,6 +94,8 @@ thunar_trash_file_class_init (ThunarTrashFileClass *klass) thunarfile_class->get_kind = thunar_trash_file_get_kind; thunarfile_class->get_mode = thunar_trash_file_get_mode; thunarfile_class->get_size = thunar_trash_file_get_size; + thunarfile_class->get_group = thunar_trash_file_get_group; + thunarfile_class->get_user = thunar_trash_file_get_user; thunarfile_class->get_icon_name = thunar_trash_file_get_icon_name; } @@ -200,6 +204,22 @@ thunar_trash_file_get_size (ThunarFile *file, +static ThunarVfsGroup* +thunar_trash_file_get_group (ThunarFile *file) +{ + return thunar_file_get_group (THUNAR_TRASH_FILE (file)->real_file); +} + + + +static ThunarVfsUser* +thunar_trash_file_get_user (ThunarFile *file) +{ + return thunar_file_get_user (THUNAR_TRASH_FILE (file)->real_file); +} + + + static const gchar* thunar_trash_file_get_icon_name (ThunarFile *file, GtkIconTheme *icon_theme) -- GitLab