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