1 #ifdef COPYRIGHT_INFORMATION
2 #include "gplv3.h"
3 #endif
4 /*
5 * Copyright (C) 2002-2012 Edscott Wilson Garcia
6 * EMail: edscott@users.sf.net
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program;
21 */
22
23 ///////////////////////////// CPY ////////////////////////////////////
24 //
25 //
26
27
28
29
30 //#define MAX_LINE_SIZE (_POSIX_PATH_MAX*3)
31 //#define CHILD_FILE_LENGTH 64
32
33
34 static gboolean
make_overwrite_dialog(const gchar * target,const gchar * src,const gchar * command)35 make_overwrite_dialog (const gchar * target, const gchar * src, const gchar *command) {
36 struct stat st;
37 if (lstat (target, &st)<0){
38 fprintf (stderr, "make_overwrite_dialog(): cannot lstat %s\n", target);
39 }
40 gchar *mess;
41 gchar *ss1 = rfm_time_to_string (st.st_mtime);
42 gchar *ss2 = rfm_sizetag ((off_t) st.st_size, -1);
43 if(src) {
44 struct stat s_st;
45 if(lstat (src, &s_st) < 0) {
46 fprintf (stderr, "make_overwrite_dialog(): cannot lstat %s\n", src);
47 }
48 gchar *s1 = rfm_time_to_string (s_st.st_mtime);
49 gchar *s2 = rfm_sizetag ((off_t) s_st.st_size, -1);
50 gchar *target_utf = rfm_utf_string (target);
51 gchar *source_utf = rfm_utf_string (src);
52
53 gchar *standard_text = g_strdup_printf(_("%s: overwrite %s? "),
54 command, target_utf);
55 gchar *dates = g_strdup_printf("%s %s %s\n%s %s %s",
56 _("Target location: "), s1, s2,
57 _("Source Location:"), ss1, ss2);
58
59 mess = g_strdup_printf ("<b>%s</b>\n<i>%s</i>",
60 standard_text, dates);
61 g_free(standard_text);
62 g_free(dates);
63
64 g_free (s1); g_free (s2);
65 g_free (source_utf); g_free (target_utf);
66 } else {
67 // This is deprecated... for remove/shred...
68 mess = g_strdup_printf ("%s\n(%s %s)", target, ss1, ss2);
69 }
70
71 g_free (ss1);
72 g_free (ss2);
73 TRACE( "confirm %s\n", mess);
74 widgets_t *widgets_p = rfm_get_widget ("widgets_p");
75 gint result = rfm_confirm ( widgets_p, GTK_MESSAGE_WARNING,
76 mess,
77 _("Cancel"), // if NULL, button not shown
78 NULL // if NULL, "Ok" button shown
79 );
80 g_free(mess);
81
82 return result;
83 }
84
85 static gchar *
mktgpath(const gchar * target_dir,char * source)86 mktgpath (const gchar * target_dir, char *source) {
87 gchar *name = g_path_get_basename (source);
88 gchar *target = g_build_filename (target_dir, name, NULL);
89 g_free (name);
90 return target;
91 }
92
93 static gboolean
warn_target_exists(const gchar * target,const gchar * src,gboolean force_override,const gchar * command)94 warn_target_exists (const gchar * target, const gchar * src, gboolean force_override, const gchar *command) {
95 TRACE( "force_override=%d\n", force_override);
96 gint result = TRUE;
97 if(!force_override) {
98 result = make_overwrite_dialog (target, src, command);
99 }
100 return result;
101 }
102
103 static gboolean
ok_input(char * target,const gchar * path,gboolean force_override,const gchar * command)104 ok_input (char *target, const gchar *path, gboolean force_override, const gchar *command) {
105 TRACE( "%s at ok_input\n", target);
106 if(!rfm_g_file_test (target, G_FILE_TEST_EXISTS)) {
107 TRACE( "%s does not exist\n", target);
108 return (TRUE);
109 }
110 return warn_target_exists (target, path, force_override, command);
111 }
112
113 static void
verify_list(widgets_t * widgets_p,GSList ** list_p,const gchar * target_path,gboolean interactive,const gchar * command)114 verify_list (widgets_t * widgets_p, GSList **list_p, const gchar * target_path, gboolean interactive, const gchar *command) {
115 gboolean force_override = !interactive;
116
117
118
119 GSList *list;
120 GSList *out_list=NULL;
121 for(list = *list_p; list && list->data; list = list->next) {
122 // Duplicate path for verified list
123 gchar *path=g_strdup((gchar *)list->data);
124 //record_entry_t *s_en = rfm_stat_entry (url, 0);
125
126 gchar *target = (rfm_g_file_test(target_path, G_FILE_TEST_IS_DIR))?
127 mktgpath (target_path, path) : g_strdup(target_path);
128
129 NOOP (stderr, "verify_list: path=\"%s\" target=%s\n", path, target_path);
130
131 if (ok_input (target, path, force_override, command)) {
132 out_list = g_slist_append (out_list, path);
133 } else {
134 rfm_threaded_diagnostics (widgets_p, "xffm_tag/green",
135 g_strconcat(_("Cancel"), " :", path, "\n", NULL));
136 g_free(path);
137 }
138
139 g_free (target);
140 }
141 g_slist_free(*list_p);
142 *list_p = out_list;
143 return;
144 }
145
146 static void *
thread_cp(void * arg_p)147 thread_cp (void *arg_p){
148 void **arg = (void **)arg_p;
149 int mode = GPOINTER_TO_INT(arg[0]);
150 GSList **verified_list_p=arg[1];
151 gchar *target_path = arg[2];
152 widgets_t *widgets_p = arg[3];
153 g_free(arg_p);
154
155 view_t *view_p=widgets_p->view_p;
156 gchar *sources[MAX_COMMAND_ARGS];
157 int i = 0;
158
159 // Choose the GNU command to execute:
160 switch (mode) {
161 case (TR_MOVE_REMOTE):
162 mode = TR_MOVE;
163 // case falls through. This is intentional
164 // coverity[unterminated_case : FALSE]
165 case (TR_MOVE):
166 sources[i++] = g_strdup("mv");
167 break;
168 case (TR_LINK_REMOTE):
169 mode = TR_LINK;
170 // case falls through. This is intentional
171 // coverity[unterminated_case : FALSE]
172 case (TR_LINK):
173 sources[i++] = g_strdup("ln");
174 break;
175 case (TR_COPY_REMOTE):
176 mode = TR_COPY;
177 // case falls through. This is intentional
178 // coverity[unterminated_case : FALSE]
179 case (TR_COPY):
180 sources[i++] = g_strdup("cp");
181 break;
182 default:
183 DBG("cp mode not specified\n");
184 return NULL;
185 }
186
187 // Set the options for each command. These options are listed as environment
188 RfmProgramOptions *options_p=NULL;
189 gint64 flag;
190 const gchar *cflag="0x0";
191 switch (mode) {
192 case (TR_COPY):
193 options_p = get_cp_options();
194 cflag = getenv("RFM_CP_FLAGS");
195 break;
196 case (TR_MOVE):
197 options_p = get_mv_options();
198 cflag = getenv("RFM_MV_FLAGS");
199 break;
200 case (TR_LINK):
201 options_p = get_ln_options();
202 cflag = getenv("RFM_LN_FLAGS");
203 break;
204 }
205 errno=0;
206 flag = strtoll(cflag, NULL, 16);
207 if (errno){
208 DBG("cp.i: strtollfailed \n");
209 }
210
211 gboolean interactive = FALSE; //cp, mv, but not ln (yet)
212 // flag j==0 is the always ask flag, which is not a command flag
213 gint j = 0;
214 for (; options_p && options_p->option; options_p++, j++){
215 if (j==0) continue;
216 if (!(options_p->sensitive)) continue;
217 if (!(flag & (ONE64<<j))) continue;
218 if (!options_p->choice_id){
219 if (strcmp(options_p->option, "-i")==0) interactive = TRUE;
220 else {
221 sources[i++] = g_strdup(options_p->option);
222 if (strcmp(options_p->option, "-n")==0) interactive = FALSE;
223 }
224 }
225 #if GNU_CP
226 else {
227 const gchar *item=NULL;
228 switch(options_p->choice_id){
229 case RFM_CP_backup:
230 item = getenv("RFM_CP_backup");
231 break;
232 case RFM_CP_preserve:
233 item = getenv("RFM_CP_preserve");
234 break;
235 case RFM_CP_no_preserve:
236 item = getenv("RFM_CP_no_preserve");
237 break;
238 case RFM_CP_reflink:
239 item = getenv("RFM_CP_reflink");
240 break;
241 case RFM_CP_sparse:
242 item = getenv("RFM_CP_sparse");
243 break;
244 case RFM_CP_suffix:
245 item = getenv("RFM_CP_suffix");
246 break;
247 case RFM_MV_backup:
248 item = getenv("RFM_MV_backup");
249 break;
250 case RFM_MV_suffix:
251 item = getenv("RFM_MV_suffix");
252 break;
253 case RFM_LN_backup:
254 item = getenv("RFM_LN_backup");
255 break;
256 case RFM_LN_suffix:
257 item = getenv("RFM_LN_suffix");
258 break;
259 }
260 sources[i++] = g_strconcat(options_p->option,item,NULL);
261 }
262 #endif
263 }
264
265 // Options are now set. What follows is listing the parameters to
266 // the respective GNU command.
267 //
268 // Interactive processing (if applicable) for cp/mv
269 verify_list (widgets_p, verified_list_p, target_path, interactive, sources[0]);
270 if(g_slist_length(*verified_list_p) == 0 || *verified_list_p == NULL) {
271 NOOP ("CPY: verified_list_p == NULL\n");
272 g_slist_free(*verified_list_p);
273 g_free(verified_list_p);
274 g_free(target_path);
275 gint k = 0; for(; k<i; k++) g_free(sources[k]);
276 return GINT_TO_POINTER(FALSE);
277 }
278 // If we are in link mode, we make all targets relative to
279 // avoid absolute paths for symlinks.
280 GSList *list;
281 GSList *ln_list=NULL;
282 gchar *tgtdir = NULL;
283 gboolean ok = TRUE;
284 gboolean readOK = TRUE;
285 if (mode==TR_LINK) {
286 const gchar *ln_path=(*verified_list_p)->data;
287
288 tgtdir = g_strdup (target_path);
289 gchar *base_dir = g_path_get_dirname (ln_path);
290 int up = 0;
291 while(base_dir && tgtdir && !strstr (base_dir, tgtdir)
292 && strchr (tgtdir, '/')) {
293 up++;
294 *(strrchr (tgtdir, '/')) = 0;
295 NOOP ("CPY: tgtdir=%s\n", tgtdir);
296 }
297 g_free (base_dir);
298
299 for(list = *verified_list_p; list && list->data; list = list->next) {
300 const gchar *orig_path=list->data;
301 NOOP(stderr, "Adding source: %s\n", orig_path);
302 const gchar *source_substring = orig_path + strlen (tgtdir) + 1;
303 int newsource_length = up * strlen ("../") + strlen (source_substring) + 1;
304 gchar *newsource = (gchar *)malloc (newsource_length);
305 if (!newsource) g_error("malloc: %s", strerror(errno));
306 memset (newsource, 0, newsource_length);
307 int k;
308 for(k = 0; k < up; k++) {
309 strcat (newsource, "../");
310 }
311 strcat (newsource, source_substring);
312 // relative links may fail if the path we are building
313 // from contains a symlink itself...
314 gchar *target;
315 if(rfm_g_file_test (target_path, G_FILE_TEST_IS_DIR)) {
316 target = g_strdup (target_path);
317 } else {
318 target = g_path_get_dirname (target_path);
319 }
320 gchar *relative_path=g_build_filename(target, newsource, NULL);
321 g_free(target);
322 if (!rfm_g_file_test(relative_path, G_FILE_TEST_EXISTS)){
323 // in this case, fall back to absolute path
324 NOOP("%s does not exist! Fallback= %s\n",
325 newsource, orig_path);
326 g_free(newsource);
327 newsource=g_strdup(orig_path);
328 } else {
329 NOOP("%s is ok\n", newsource);
330 }
331 g_free(relative_path);
332 sources[i++] = g_strdup(newsource);
333 ln_list=g_slist_prepend(ln_list, newsource);
334 if(i == MAX_COMMAND_ARGS - 3) {
335 rfm_diagnostics (widgets_p, "xffm/stock_dialog-warning", NULL);
336 gchar *max=g_strdup_printf("%d",MAX_COMMAND_ARGS);
337 rfm_diagnostics (widgets_p, "xffm_tag/stderr", sources[0], ": ", strerror(E2BIG)," (> ",max,")","\n", NULL);
338 g_free(max);
339 ok = FALSE;
340 break;
341 }
342 }
343 g_free (tgtdir);
344
345 gboolean exists=g_file_test (target_path, G_FILE_TEST_IS_DIR);
346
347 if(exists) {
348 tgtdir = g_strdup (target_path);
349 } else {
350 tgtdir = g_path_get_dirname (target_path);
351 }
352 sources[i++] = g_strdup(tgtdir);
353 if(strcmp(tgtdir, ".") && !g_file_test(tgtdir, G_FILE_TEST_IS_DIR)) {
354 DBG ("Cannot chdir to %s\n", tgtdir);
355 g_free (tgtdir);
356 tgtdir=NULL;
357 ok = FALSE;
358 } else {
359 g_free (widgets_p->workdir);
360 widgets_p->workdir = g_strdup(tgtdir);
361 }
362 // End TR_LINK...
363 } else {
364 for(list = *verified_list_p; list && list->data; list = list->next) {
365 sources[i++] = g_strdup((gchar *)list->data);
366 if (!rfm_read_ok_path((gchar *)list->data)){
367 NOOP("readOK=false %s\n", (gchar *) list->data);
368 readOK=FALSE;
369 }
370 if(i == MAX_COMMAND_ARGS - 3) {
371 ok = FALSE;
372 break;
373 }
374 }
375 sources[i++] = g_strdup((gchar *) target_path);
376 }
377
378 sources[i++] = NULL;
379
380 TRACE("cplnmv: ok=%d\n", ok);
381 // note: with run_argv we do not need to escape paths.
382 if(ok) {
383 if (view_p->flags.type != DESKVIEW_TYPE) {
384 rfm_threaded_show_text(widgets_p);
385 }
386 // Not for dummies. On cp/mv/ln we will check for permissions on target
387 // directory, and if permission do not allow the operation,
388 // then we will try to do it with sudo.
389 gboolean need_sudo = FALSE;
390 gchar *src_dir=NULL;
391 if (mode==TR_MOVE) {
392 src_dir=g_path_get_dirname(sources[i-3]);
393 NOOP(stderr, "target path=%s src_dir=%s readOK=%d\n", target_path, src_dir, readOK);
394 need_sudo = !rfm_write_ok_path(target_path) ||
395 (src_dir && !rfm_write_ok_path(src_dir)) ||
396 !readOK;
397 } else if (mode==TR_COPY) {
398 src_dir=g_path_get_dirname(sources[i-3]);
399 need_sudo = !rfm_write_ok_path(target_path) || !readOK;
400 } else if (mode==TR_LINK) {
401 gchar *ttt = g_path_get_dirname(sources[i-2]);
402 gchar *t = realpath(ttt, NULL);
403 TRACE("cplnmv: realpath=%s \n", t);
404 need_sudo = !rfm_write_ok_path(t);
405 g_free(t);
406 g_free(ttt);
407 }
408 TRACE("cplnmv: need sudo=%d \n", need_sudo);
409
410
411 if (!need_sudo) {
412 rfm_thread_run_argv (widgets_p, sources, FALSE);
413
414 } else {
415
416 gchar *failed=NULL;
417 gchar *tgt=NULL;
418 const gchar *operation;
419 if (rfm_g_file_test (target_path, G_FILE_TEST_IS_DIR)){
420 tgt = g_strdup (target_path);
421 } else {
422 tgt = g_path_get_dirname (target_path);
423 }
424 switch (mode){
425 case TR_COPY:
426 failed=g_strdup_printf("%s.", _("Failed to copy file"));
427 operation="cp";
428 break;
429 case TR_MOVE:
430 failed = g_strdup_printf("%s: %s", _( "Move files"),
431 strerror(EACCES));
432
433 if (src_dir && !rfm_write_ok_path(src_dir)) {
434 g_free(tgt);
435 tgt=g_strdup(src_dir);
436 }
437 operation="mv";
438 break;
439 case TR_LINK:
440 failed=g_strdup_printf(_("Failed to link %s to %s"),
441 _("File"), _("Destination"));
442 operation="ln";
443 break;
444 default:
445 DBG("non valid condition in thread_cp()\n");
446 }
447 if (confirm_sudo(widgets_p, tgt, failed, (void *)operation)){
448 RFM_TRY_SUDO (widgets_p, sources, FALSE);
449 }
450 g_free(failed);
451 g_free(tgt);
452 }
453 gchar **s = sources;
454 for (;s && *s; s++) g_free(*s);
455 g_free(src_dir);
456 } else {
457 NOOP ("PASTE CP: ok == FALSE\n");
458 }
459 // We should do partial cleanup here...
460 // Clean up stuff generated in link mode:
461 g_free(tgtdir);
462 for(list = ln_list; list && list->data; list = list->next) {
463 g_free(list->data);
464 }
465 g_slist_free (ln_list);
466 // 1. Clean up input selection_list
467 for(list = *verified_list_p; list && list->data; list = list->next) {
468 g_free(list->data);
469 }
470 g_slist_free (*verified_list_p);
471 g_free (verified_list_p);
472 g_free (target_path);
473 return NULL;
474 }
475
476 // plain_cp is an exception: it is a main thread action, the threaded action
477 // is the thread_cp() function.
478 static void *
plain_cp(widgets_t * widgets_p,gint mode,GList * in_list,const gchar * target_path,gboolean new_thread)479 plain_cp (widgets_t *widgets_p, gint mode, GList *in_list, const gchar *target_path, gboolean new_thread) {
480
481 const gchar *flag_id=NULL;
482 switch (mode){
483 case (TR_MOVE_REMOTE):
484 case (TR_MOVE):
485 flag_id = "RFM_MV_FLAGS"; break;
486 case (TR_LINK_REMOTE):
487 case (TR_LINK):
488 flag_id = "RFM_LN_FLAGS"; break;
489 case (TR_COPY_REMOTE):
490 case (TR_COPY):
491 flag_id = "RFM_CP_FLAGS"; break;
492 }
493 if (!flag_id){
494 DBG("plain_cp(): flag_id is NULL\n");
495 return NULL;
496 }
497
498 if (!rfm_rational(RFM_MODULE_DIR, "settings", widgets_p, (void *)flag_id, "options_dialog")){
499 return NULL;
500 }
501
502 GSList **verified_list_p = (GSList **)malloc(sizeof(GSList *));
503 if (!verified_list_p) g_error("malloc: %s\n", strerror(errno));
504
505 *verified_list_p = NULL;
506
507
508 GList *list;
509 for(list = in_list; list && list->data; list = list->next) {
510 // Duplicate path for thread list
511 gchar *path=g_strdup((gchar *)list->data);
512 *verified_list_p = g_slist_prepend (*verified_list_p, path);
513 NOOP(stderr, "adding %s to the operation list\n", path);
514 }
515 *verified_list_p = g_slist_reverse (*verified_list_p);
516
517
518 void **arg = (void **)malloc(4*sizeof(void *));
519 if (!arg) g_error("malloc: %s\n", strerror(errno));
520 arg[0] = GINT_TO_POINTER(mode);
521 arg[1] = verified_list_p;
522 arg[2] = g_strdup(target_path);
523 arg[3] = widgets_p;
524 if (new_thread){
525 rfm_view_thread_create(widgets_p->view_p, thread_cp, (void *)arg,
526 "callbacks: thread_cp");
527 } else {
528 thread_cp((void *)arg);
529 }
530 return GINT_TO_POINTER(TRUE);
531 }
532
533