1 /*
2 ** 1998-06-07 -	A command to set the owner and group information on a file
3 **		(or set of files). Might get GUI-ish.
4 ** 1998-06-08 -	Fixed the userinfo module, which allowed this one to take another
5 **		step towards completetion; now it preselects the current user/group
6 **		names from the option menus. I still miss some form of higher-level
7 **		control widgetry, though. Later...
8 ** 1998-10-16 -	The defaults now work (even if you don't operate the menus).
9 ** 1999-03-06 -	Adapted for new selection/generic/dirrow representations.
10 ** 2000-03-23 -	Now uses combo boxes rather than menus to display user and group
11 **		lists. Also allows user to type in either number or name of the
12 **		desired user and/or group directly. All in all, a lot smoother.
13 ** 2001-04-24 -	Added recursion option.
14 ** 2010-03-02 -	Rewritten using GIO.
15 */
16 
17 #include "gentoo.h"
18 
19 #include <ctype.h>
20 #include <stdlib.h>
21 
22 #include "dirpane.h"
23 #include "errors.h"
24 #include "fileutil.h"
25 #include "userinfo.h"
26 
27 #include "cmd_generic.h"
28 #include "cmd_chown.h"
29 
30 #define	CMD_ID	"chown"
31 
32 /* ----------------------------------------------------------------------------------------- */
33 
34 typedef struct {
35 	GtkWidget	*combo;
36 	gint		history;			/* The weirdly named thing (see gtk_option_menu widget). */
37 } URow;
38 
39 typedef struct {
40 	GtkWidget	*vbox;
41 	GtkWidget	*label;
42 	GtkWidget	*rowgrid;
43 	URow		row[2];
44 	guint		user, group;			/* The currently selected IDs. */
45 	gint		user_index, group_index;	/* Indices to default to. */
46 	GtkWidget	*recurse;
47 	gboolean	last_recurse;
48 } ChoInfo;
49 
50 /* ----------------------------------------------------------------------------------------- */
51 
cho_body(MainInfo * min,DirPane * src,DirRow2 * row,gpointer gen,gpointer user)52 static void cho_body(MainInfo *min, DirPane *src, DirRow2 *row, gpointer gen, gpointer user)
53 {
54 	gchar	label[2 * MAXNAMLEN + 64];
55 	ChoInfo	*cho = user;
56 
57 	g_snprintf(label, sizeof label, _("Set Ownership for '%s':"), dp_row_get_name_display(dp_get_tree_model(src), row));
58 	gtk_label_set_text(GTK_LABEL(cho->label), label);
59 	gtk_widget_grab_focus(cho->row[0].combo);
60 }
61 
62 /* ----------------------------------------------------------------------------------------- */
63 
parse_or_lookup(const gchar * str,long lookup (const gchar * name),long * tmp)64 static gboolean parse_or_lookup(const gchar *str, long lookup(const gchar *name), long *tmp)
65 {
66 	while(isspace((guchar) *str))
67 		str++;
68 	if(isdigit((guchar) *str))
69 	{
70 		gchar	*eptr;
71 
72 		*tmp = strtol(str, &eptr, 10);
73 		if(eptr == str)
74 			return FALSE;
75 	}
76 	else
77 		*tmp = lookup(str);
78 	return TRUE;
79 }
80 
get_own(ChoInfo * cho,uid_t * owner,gid_t * group)81 static gboolean get_own(ChoInfo *cho, uid_t *owner, gid_t *group)
82 {
83 	const gchar	*text;
84 	long		tmp;
85 	gboolean	ok = FALSE;
86 
87 	if((text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cho->row[0].combo))) == NULL)
88 		return FALSE;
89 	if(parse_or_lookup(text, usr_lookup_uid, &tmp))
90 	{
91 		*owner = tmp;
92 		if((text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cho->row[1].combo))) != NULL)
93 		{
94 			if(parse_or_lookup(text, usr_lookup_gid, &tmp))
95 			{
96 				*group = tmp;
97 				ok = TRUE;
98 			}
99 		}
100 	}
101 	return ok;
102 }
103 
chown_gfile(MainInfo * min,GFile * file,uid_t owner,gid_t group,gboolean recurse,GError ** err)104 static gboolean chown_gfile(MainInfo *min, GFile *file, uid_t owner, gid_t group, gboolean recurse, GError **err)
105 {
106 	gboolean	ok = FALSE;
107 	GFileInfo	*info;
108 	GFileType	type;
109 
110 	if(file != NULL && (info = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_UNIX_UID "," G_FILE_ATTRIBUTE_UNIX_GID,
111 						     G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, err)) != NULL)
112 	{
113 		type = g_file_info_get_file_type(info);
114 		/* Don't try to change the file's type; now that we've buffered it we can remove it. */
115 		g_file_info_remove_attribute(info, G_FILE_ATTRIBUTE_STANDARD_TYPE);
116 		g_file_info_set_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_UID, owner);
117 		g_file_info_set_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_GID, group);
118 		ok = g_file_set_attributes_from_info(file, info, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, err);
119 		g_object_unref(info);	/* Drop this early, no point in holding recursively. */
120 		if(ok)
121 		{
122 			if(type == G_FILE_TYPE_DIRECTORY && recurse)
123 			{
124 				GFileEnumerator	*fen;
125 
126 				ok = FALSE;
127 				if((fen = g_file_enumerate_children(file, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, err)) != NULL)
128 				{
129 					GFileInfo	*chi;
130 
131 					ok = TRUE;
132 					while(ok && (chi = g_file_enumerator_next_file(fen, NULL, err)) != NULL)
133 					{
134 						GFile	*child = g_file_get_child(file, g_file_info_get_name(chi));
135 
136 						ok = chown_gfile(min, child, owner, group, recurse, err);
137 						g_object_unref(child);
138 					}
139 					g_object_unref(fen);
140 				}
141 			}
142 		}
143 	}
144 	return ok;
145 }
146 
cho_action(MainInfo * min,DirPane * src,DirPane * dst,DirRow2 * row,GError ** err,gpointer user)147 static gint cho_action(MainInfo *min, DirPane *src, DirPane *dst, DirRow2 *row, GError **err, gpointer user)
148 {
149 	ChoInfo		*cho = user;
150 	uid_t		owner = 0;
151 	gid_t		group = 0;
152 	GFile		*file;
153 	gboolean	ok = FALSE;
154 
155 	if(!get_own(cho, &owner, &group))
156 		return FALSE;
157 	cho->last_recurse = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cho->recurse));
158 	if((file = dp_get_file_from_row(src, row)) != NULL)
159 	{
160 		ok = chown_gfile(min, file, owner, group, cho->last_recurse, err);
161 		g_object_unref(file);
162 	}
163 	if(ok)
164 		dp_unselect(src, row);
165 
166 	return ok;
167 }
168 
169 /* ----------------------------------------------------------------------------------------- */
170 
cmd_chown(MainInfo * min,DirPane * src,DirPane * dst,const CmdArg * ca)171 gint cmd_chown(MainInfo *min, DirPane *src, DirPane *dst, const CmdArg *ca)
172 {
173 	UsrCategory	cat[] = { UIC_USER, UIC_GROUP };
174 	gchar		*labtext[] = { N_("User"), N_("Group") };
175 	gint		i, index;
176 	GtkWidget	*label;
177 	GList		*list, *iter;
178 	static ChoInfo	cho;
179 
180 	cho.vbox  = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
181 	cho.label = gtk_label_new("");
182 	gtk_box_pack_start(GTK_BOX(cho.vbox), cho.label, FALSE, FALSE, 0);
183 	cho.rowgrid = gtk_grid_new();
184 	for(i = 0; i < 2; i++)
185 	{
186 		label = gtk_label_new(_(labtext[i]));
187 		gtk_grid_attach(GTK_GRID(cho.rowgrid), label, 0, i, 1, 1);
188 		cho.row[i].combo = gtk_combo_box_text_new_with_entry();
189 		list = usr_string_list_create(cat[i], cat[i] == UIC_USER ? geteuid() : getgid(), &index);
190 		for(iter = list; iter != NULL; iter = g_list_next(iter))
191 			gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cho.row[i].combo), iter->data);
192 		gtk_combo_box_set_active(GTK_COMBO_BOX(cho.row[i].combo), index);
193 		usr_string_list_destroy(list);
194 		gtk_widget_set_hexpand(cho.row[i].combo, TRUE);
195 		gtk_widget_set_halign(cho.row[i].combo, GTK_ALIGN_FILL);
196 		gtk_grid_attach(GTK_GRID(cho.rowgrid), cho.row[i].combo, 1, i, 1, 1);
197 	}
198 	gtk_box_pack_start(GTK_BOX(cho.vbox), cho.rowgrid, FALSE, FALSE, 5);
199 	cho.recurse = gtk_check_button_new_with_label(_("Recurse Directories?"));
200 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cho.recurse), cho.last_recurse);
201 	gtk_box_pack_start(GTK_BOX(cho.vbox), cho.recurse, FALSE, FALSE, 0);
202 	gtk_widget_show_all(cho.vbox);
203 
204 	return cmd_generic(min, _("Change Ownership"), CGF_SRC | CGF_NODST, cho_body, cho_action, NULL, &cho);
205 }
206