1 /*
2 ** 1998-05-29 - A command to change the access flags of a file or directory.
3 ** Made significantly simpler by the new cmd_generic module.
4 ** 1999-03-06 - Adapted for new selection/generic handling.
5 ** 2010-03-02 - GIO porting more or less complete.
6 */
7
8 #include "gentoo.h"
9 #include "errors.h"
10 #include "dirpane.h"
11 #include "strutil.h"
12 #include "window.h"
13
14 #include "cmd_generic.h"
15 #include "cmd_chmod.h"
16
17 #define CMD_ID "chmod"
18
19 /* ----------------------------------------------------------------------------------------- */
20
21 typedef struct {
22 GtkWidget *frame;
23 GtkWidget *vbox;
24 GtkWidget *check[3];
25 gulong signal[3];
26 } PFrame;
27
28 typedef struct {
29 GtkWidget *vbox;
30 GtkWidget *label;
31 GtkWidget *fbox;
32 PFrame frame[4];
33 GtkWidget *entry_text;
34 GtkWidget *entry_octal;
35 GtkWidget *bbox;
36 GtkWidget *all, *none, *toggle, *revert;
37 mode_t last_mode;
38 GtkWidget *recurse;
39 gboolean last_recurse;
40 GtkWidget *nodirs;
41 gboolean last_nodirs;
42 } ChmInfo;
43
44 static const mode_t mask[] = { S_ISUID, S_ISGID, S_ISVTX,
45 S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH };
46
47 /* ----------------------------------------------------------------------------------------- */
48
get_checks(const ChmInfo * chm)49 static mode_t get_checks(const ChmInfo *chm)
50 {
51 mode_t mode = 0U;
52 guint i, j, k;
53
54 for(i = k = 0; i < sizeof chm->frame / sizeof *chm->frame; i++)
55 {
56 for(j = 0; j < sizeof chm->frame[i].check / sizeof *chm->frame[i].check; j++, k++)
57 {
58 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chm->frame[i].check[j])))
59 mode |= mask[k];
60 }
61 }
62 return mode;
63 }
64
65 /* 2009-03-25 - Update the textual representations. These are currently read-only. */
set_texts(ChmInfo * chm,mode_t mode)66 static void set_texts(ChmInfo *chm, mode_t mode)
67 {
68 gchar buf[32];
69
70 stu_mode_to_text(buf, sizeof buf, mode);
71 gtk_entry_set_text(GTK_ENTRY(chm->entry_text), buf + 1); /* Skip the directory indicator. */
72 g_snprintf(buf, sizeof buf, "%o", mode);
73 gtk_entry_set_text(GTK_ENTRY(chm->entry_octal), buf);
74 }
75
set_checks(ChmInfo * chm,mode_t mode)76 static void set_checks(ChmInfo *chm, mode_t mode)
77 {
78 guint i, j, k;
79
80 for(i = k = 0; i < sizeof chm->frame / sizeof *chm->frame; i++)
81 {
82 for(j = 0; j < sizeof chm->frame[i].check / sizeof *chm->frame[i].check; j++, k++)
83 {
84 g_signal_handler_block(G_OBJECT(chm->frame[i].check[j]), chm->frame[i].signal[j]);
85 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chm->frame[i].check[j]), mode & mask[k]);
86 g_signal_handler_unblock(G_OBJECT(chm->frame[i].check[j]), chm->frame[i].signal[j]);
87 }
88 }
89 set_texts(chm, mode);
90 }
91
chm_body(MainInfo * min,DirPane * src,DirRow2 * row,gpointer gen,gpointer user)92 static void chm_body(MainInfo *min, DirPane *src, DirRow2 *row, gpointer gen, gpointer user)
93 {
94 ChmInfo *chm = user;
95 gchar temp[2 * FILENAME_MAX + 128];
96 mode_t mode = dp_row_get_mode(dp_get_tree_model(src), row);
97
98 g_snprintf(temp, sizeof temp, _("Set protection bits for \"%s\":"), dp_row_get_name_display(dp_get_tree_model(src), row));
99 gtk_label_set_text(GTK_LABEL(chm->label), temp);
100 set_checks(chm, chm->last_mode = (mode & 07777));
101 }
102
103 /* ----------------------------------------------------------------------------------------- */
104
chmod_gfile(MainInfo * min,DirPane * src,const GFile * file,mode_t mode,gboolean recurse,gboolean nodirs,GError ** err)105 static gboolean chmod_gfile(MainInfo *min, DirPane *src, const GFile *file, mode_t mode, gboolean recurse, gboolean nodirs, GError **err)
106 {
107 GFileInfo *fi;
108 gboolean ok = FALSE;
109
110 if((fi = g_file_query_info((GFile *) file, G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, err)) != NULL)
111 {
112 const gboolean isdir = (g_file_info_get_file_type(fi) == G_FILE_TYPE_DIRECTORY);
113
114 /* Don't try to change the file's type. */
115 g_file_info_remove_attribute(fi, G_FILE_ATTRIBUTE_STANDARD_TYPE);
116
117 /* If non-directory or we're attacking dirs, set the mode first. */
118 if(!isdir || !nodirs)
119 {
120 g_file_info_set_attribute_uint32(fi, G_FILE_ATTRIBUTE_UNIX_MODE, mode);
121 ok = g_file_set_attributes_from_info((GFile *) file, fi, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, err);
122 }
123 else
124 ok = TRUE;
125 /* Drop the info, we're done. */
126 g_object_unref(fi);
127
128 /* If successful so far, consider recursing. */
129 if(ok && isdir && recurse)
130 {
131 GFileEnumerator *fe;
132
133 if((fe = g_file_enumerate_children((GFile *) file, "standard::name,unix::mode", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, err)) != NULL)
134 {
135 GFileInfo *cfi;
136 GFile *child;
137
138 while(ok && (cfi = g_file_enumerator_next_file(fe, NULL, err)) != NULL)
139 {
140 if((child = g_file_get_child((GFile *) file, g_file_info_get_name(cfi))) != NULL)
141 {
142 ok = chmod_gfile(min, src, child, mode, recurse, nodirs, err);
143 g_object_unref(child);
144 }
145 else
146 ok = FALSE;
147 g_object_unref(cfi);
148 }
149 g_object_unref(fe);
150 }
151 else
152 ok = FALSE;
153 }
154 }
155 return ok;
156 }
157
chm_action(MainInfo * min,DirPane * src,DirPane * dst,DirRow2 * row,GError ** err,gpointer user)158 static gint chm_action(MainInfo *min, DirPane *src, DirPane *dst, DirRow2 *row, GError **err, gpointer user)
159 {
160 ChmInfo *chm = user;
161 const mode_t mode = get_checks(chm);
162 GFile *dfile;
163 gboolean ok;
164
165 chm->last_recurse = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chm->recurse));
166 chm->last_nodirs = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(chm->nodirs));
167 dfile = dp_get_file_from_row(src, row);
168 ok = chmod_gfile(min, src, dfile, mode, chm->last_recurse, chm->last_nodirs, err);
169 if(ok)
170 dp_unselect(src, row);
171
172 return ok;
173 }
174
175 /* ----------------------------------------------------------------------------------------- */
176
evt_clicked(GtkWidget * wid,gpointer user)177 static void evt_clicked(GtkWidget *wid, gpointer user)
178 {
179 ChmInfo *chm = user;
180
181 if(wid == chm->all)
182 set_checks(chm, S_ISUID | S_ISGID | S_ISVTX | S_IRUSR | S_IWUSR | S_IXUSR |
183 S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
184 else if(wid == chm->none)
185 set_checks(chm, 0);
186 else if(wid == chm->toggle)
187 set_checks(chm, (~get_checks(chm)) & 07777);
188 else if(wid == chm->revert)
189 set_checks(chm, chm->last_mode);
190 else
191 set_texts(chm, get_checks(chm));
192 }
193
194 /* 1998-05-29 - Build a protection frame, with three checkboxes. If type is 0, we build a
195 ** special one (with setuid/setgid/sticky), otherwise a standard (read/write/exec).
196 */
build_frame(ChmInfo * ci,gint pos,gint type)197 static void build_frame(ChmInfo *ci, gint pos, gint type)
198 {
199 gchar *label[] = { N_("Special"), N_("Owner"), N_("Group"), N_("Others") };
200 gchar *check[] = { N_("Set UID"), N_("Set GID"), N_("Sticky"), N_("Read"), N_("Write"), N_("Execute") };
201 PFrame *fr = &ci->frame[pos];
202 gint i;
203
204 fr->frame = gtk_frame_new(_(label[pos]));
205 fr->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
206 for(i = 0; i < 3; i++)
207 {
208 fr->check[i] = gtk_check_button_new_with_label(_(check[type * 3 + i]));
209 fr->signal[i] = g_signal_connect(G_OBJECT(fr->check[i]), "toggled", G_CALLBACK(evt_clicked), ci);
210 gtk_box_pack_start(GTK_BOX(fr->vbox), fr->check[i], TRUE, TRUE, 0);
211 }
212 gtk_container_add(GTK_CONTAINER(fr->frame), fr->vbox);
213 }
214
cmd_chmod(MainInfo * min,DirPane * src,DirPane * dst,const CmdArg * ca)215 gint cmd_chmod(MainInfo *min, DirPane *src, DirPane *dst, const CmdArg *ca)
216 {
217 static ChmInfo ci;
218 guint i;
219 GtkWidget *hbox, *w;
220
221 ci.vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
222 ci.label = gtk_label_new(_("Protection Bits"));
223 ci.fbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
224 for(i = 0; i < sizeof ci.frame / sizeof ci.frame[0]; i++)
225 {
226 build_frame(&ci, i, (i == 0) ? 0 : 1);
227 gtk_box_pack_start(GTK_BOX(ci.fbox), ci.frame[i].frame, TRUE, TRUE, 5);
228 }
229 gtk_box_pack_start(GTK_BOX(ci.vbox), ci.label, FALSE, FALSE, 0);
230 gtk_box_pack_start(GTK_BOX(ci.vbox), ci.fbox, TRUE, TRUE, 0);
231
232 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
233 w = gtk_label_new(_("Textual"));
234 gtk_box_pack_start(GTK_BOX(hbox), w, FALSE, FALSE, 0);
235 ci.entry_text = gtk_entry_new();
236 gtk_entry_set_max_length(GTK_ENTRY(ci.entry_text), 10);
237 gtk_entry_set_width_chars(GTK_ENTRY(ci.entry_text), 10);
238 gtk_editable_set_editable(GTK_EDITABLE(ci.entry_text), FALSE);
239 gtk_box_pack_start(GTK_BOX(hbox), ci.entry_text, FALSE, FALSE, 0);
240
241 ci.entry_octal = gtk_entry_new();
242 gtk_entry_set_max_length(GTK_ENTRY(ci.entry_octal), 4);
243 gtk_entry_set_width_chars(GTK_ENTRY(ci.entry_octal), 4);
244 gtk_editable_set_editable(GTK_EDITABLE(ci.entry_octal), FALSE);
245 gtk_box_pack_end(GTK_BOX(hbox), ci.entry_octal, FALSE, FALSE, 0);
246 w = gtk_label_new(_("Octal"));
247 gtk_box_pack_end(GTK_BOX(hbox), w, FALSE, FALSE, 0);
248 gtk_box_pack_start(GTK_BOX(ci.vbox), hbox, FALSE, FALSE, 0);
249
250 ci.bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
251 ci.all = gtk_button_new_with_label(_("All"));
252 ci.none = gtk_button_new_with_label(_("None"));
253 ci.toggle = gtk_button_new_with_label(_("Toggle"));
254 ci.revert = gtk_button_new_with_label(_("Revert"));
255
256 g_signal_connect(G_OBJECT(ci.all), "clicked", G_CALLBACK(evt_clicked), &ci);
257 g_signal_connect(G_OBJECT(ci.none), "clicked", G_CALLBACK(evt_clicked), &ci);
258 g_signal_connect(G_OBJECT(ci.toggle), "clicked", G_CALLBACK(evt_clicked), &ci);
259 g_signal_connect(G_OBJECT(ci.revert), "clicked", G_CALLBACK(evt_clicked), &ci);
260 gtk_box_pack_start(GTK_BOX(ci.bbox), ci.all, TRUE, TRUE, 5);
261 gtk_box_pack_start(GTK_BOX(ci.bbox), ci.none, TRUE, TRUE, 5);
262 gtk_box_pack_start(GTK_BOX(ci.bbox), ci.toggle, TRUE, TRUE, 5);
263 gtk_box_pack_start(GTK_BOX(ci.bbox), ci.revert, TRUE, TRUE, 5);
264 gtk_box_pack_start(GTK_BOX(ci.vbox), ci.bbox, TRUE, TRUE, 0);
265 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
266 ci.recurse = gtk_check_button_new_with_label(_("Recurse Directories?"));
267 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ci.recurse), ci.last_recurse);
268 gtk_box_pack_start(GTK_BOX(hbox), ci.recurse, TRUE, TRUE, 0);
269 ci.nodirs = gtk_check_button_new_with_label(_("Don't Touch Directories?"));
270 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ci.nodirs), ci.last_nodirs);
271 gtk_box_pack_start(GTK_BOX(hbox), ci.nodirs, TRUE, TRUE, 0);
272 gtk_box_pack_start(GTK_BOX(ci.vbox), hbox, FALSE, FALSE, 0);
273
274 return cmd_generic(min, _("Change Mode"), CGF_SRC, chm_body, chm_action, NULL, &ci);
275 }
276