1 /* copycore.cc
2  * This file belongs to Worker, a file manager for UN*X/X11.
3  * Copyright (C) 2013-2021 Ralf Hoffmann.
4  * You can contact me at: ralf@boomerangsworld.de
5  *   or http://www.boomerangsworld.de/worker
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "copycore.hh"
23 #include "worker_locale.h"
24 #include "copyopwin.hh"
25 #include "nmcopyopdir.hh"
26 #include "datei.h"
27 #include "copyorder.hh"
28 #include "simplelist.hh"
29 #include "verzeichnis.hh"
30 #include "aguix/lowlevelfunc.h"
31 #include "aguix/util.h"
32 #include <chrono>
33 #include <ratio>
34 #include "nwc_path.hh"
35 #include "nwc_os.hh"
36 #include "worker.h"
37 
CopyCore(std::shared_ptr<struct copyorder> co)38 CopyCore::CopyCore( std::shared_ptr< struct copyorder > co ) :
39     m_copyorder( co ),
40     m_cancel( false ),
41     m_bytes_to_copy( 0 ),
42     m_finished( false ),
43     m_ask_to_cancel( false )
44 {
45     buf = _allocsafe( BUFSIZE );
46 }
47 
~CopyCore()48 CopyCore::~CopyCore()
49 {
50     _freesafe( buf );
51 }
52 
getModeWithEnsureOption(const mode_t orig_mode,const struct copyorder & co)53 static mode_t getModeWithEnsureOption( const mode_t orig_mode,
54                                        const struct copyorder &co )
55 {
56     mode_t res = orig_mode;
57 
58     if ( co.ensure_file_permissions() == CopyParams::ENSURE_USER_RW_PERMISSION ) {
59         res |= S_IRUSR | S_IWUSR;
60     } else if ( co.ensure_file_permissions() == CopyParams::ENSURE_USER_RW_GROUP_R_PERMISSION ) {
61         res |= S_IRUSR | S_IWUSR | S_IRGRP;
62     } else if ( co.ensure_file_permissions() == CopyParams::ENSURE_USER_RW_ALL_R_PERMISSION ) {
63         res |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
64     }
65 
66     return res;
67 }
68 
getNewName4Dir(const FileEntry * oldfe,const char * dest,char ** newname_return,bool acceptrename)69 CopyCore::nm_newname_t CopyCore::getNewName4Dir( const FileEntry *oldfe, const char *dest,
70                                                  char **newname_return, bool acceptrename )
71 {
72     char *oldname=oldfe->name;
73     char *newname=dupstring(oldname);
74     bool nameok=false,skip=false,cancel=false;
75     char *buttonstr,*textstr,*newdest;
76     int erg;
77     Datei::d_fe_t se,lse;
78     int round=1;
79     nm_newname_t returnvalue=NM_NEWNAME_CANCEL;
80     bool trydel,do_rename,strongdirs;
81     char *title;
82 
83     if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->stoptimer();
84 
85     title = (char*)_allocsafe( strlen( catalog.getLocale( 500 ) ) + strlen( oldfe->fullname ) + 1 );
86     sprintf( title, catalog.getLocale( 500 ), oldfe->fullname );
87 
88     do_rename=false;
89     if(m_copyorder->do_rename==false) do_rename=false;
90     else if(acceptrename==true) do_rename=true;
91 
92     strongdirs=false;
93 
94     do {
95         if(!((round==1)&&(do_rename==false))) {
96             buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
97                                         strlen(catalog.getLocale(225))+1+
98                                         strlen(catalog.getLocale(8))+1);
99             sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
100                     catalog.getLocale(225),
101                     catalog.getLocale(8));
102             textstr=(char*)_allocsafe(strlen(catalog.getLocale(148))+strlen(oldname)+1);
103             sprintf(textstr,catalog.getLocale(148),oldname);
104 
105             std::string return_str;
106 
107             erg = m_copyorder->cowin->string_request( catalog.getLocale( 149 ),
108                                                       textstr,
109                                                       newname,
110                                                       buttonstr,
111                                                       return_str,
112                                                       Requester::REQUEST_SELECTALL );
113             _freesafe(buttonstr);
114             _freesafe(textstr);
115             if ( ! return_str.empty() ) {
116                 _freesafe(newname);
117                 newname = dupstring( return_str.c_str() );
118             }
119             if(erg==1) {
120                 skip=true;
121                 returnvalue=NM_NEWNAME_SKIP;
122                 break;
123             } else if(erg==2) {
124                 cancel=true;
125                 returnvalue=NM_NEWNAME_CANCEL;
126                 break;
127             }
128         }
129 
130         if ( strlen( newname ) < 1 ) {
131             // empty name->request
132             round++;  // not needed since empty names should only come from user input
133             // but perhaps there is (will be) a OS which allows empty names
134             continue;
135         }
136 
137         newdest=(char*)_allocsafe(strlen(dest)+1+strlen(newname)+1);
138         strcpy(newdest,dest);
139         if(strlen(dest)>1) strcat(newdest,"/");
140         strcat(newdest,newname);
141         se=Datei::fileExistsExt(newdest);
142         lse=Datei::lfileExistsExt(newdest);
143 
144         switch(lse) {
145             case Datei::D_FE_DIR:
146                 if(strongdirs==true) {
147                     textstr = (char*)_allocsafe( strlen( catalog.getLocale( 190 ) ) + strlen( newdest ) + 1 );
148                     sprintf( textstr, catalog.getLocale( 190 ), newdest );
149 
150                     buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
151                                                 strlen(catalog.getLocale(273))+1+
152                                                 strlen(catalog.getLocale(225))+1+
153                                                 strlen(catalog.getLocale(8))+1);
154                     sprintf(buttonstr,"%s|%s|%s|%s",catalog.getLocale(230),
155                             catalog.getLocale(273),
156                             catalog.getLocale(225),
157                             catalog.getLocale(8));
158 
159                     erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
160                     _freesafe(buttonstr);
161                     _freesafe(textstr);
162                     if(erg==1) {
163                         nameok=true;
164                         returnvalue=NM_NEWNAME_USE;
165                     } else if(erg==2) {
166                         skip=true;
167                         returnvalue=NM_NEWNAME_SKIP;
168                     } else if(erg==3) {
169                         cancel=true;
170                         returnvalue=NM_NEWNAME_CANCEL;
171                     }
172                 } else {
173                     // just use the dir
174                     nameok=true;
175                     returnvalue=NM_NEWNAME_USE;
176                 }
177                 break;
178             case Datei::D_FE_LINK:
179             case Datei::D_FE_FILE:
180                 if((lse==Datei::D_FE_LINK)&&(se==Datei::D_FE_DIR)) {
181                     // Symlink with dir as destination
182                     if(strongdirs==true) {
183                         //            textstr="There is already a symlink called %s, which points to a dir|You can use this link or delete it to|create a real directory";
184                         //            buttonstr="Enter new name|Use this dir|Delete link|Skip|Cancel";
185 
186                         textstr = (char*)_allocsafe( strlen( catalog.getLocale( 275 ) ) + strlen( newdest ) + 1 );
187                         sprintf( textstr, catalog.getLocale( 275 ), newdest );
188                         buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
189                                                     strlen(catalog.getLocale(273))+1+
190                                                     strlen(catalog.getLocale(274))+1+
191                                                     strlen(catalog.getLocale(225))+1+
192                                                     strlen(catalog.getLocale(8))+1);
193                         sprintf(buttonstr,"%s|%s|%s|%s|%s",catalog.getLocale(230),
194                                 catalog.getLocale(273),
195                                 catalog.getLocale(274),
196                                 catalog.getLocale(225),
197                                 catalog.getLocale(8));
198 
199                         erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
200                         _freesafe(buttonstr);
201                         _freesafe(textstr);
202                         switch(erg) {
203                             case 1:
204                                 nameok=true;
205                                 returnvalue=NM_NEWNAME_USE;
206                                 break;
207                             case 2:
208                                 // try to remove the link
209                                 // if success set nameok to true
210                                 // in case of failure show a request and repeat the whole
211                                 if ( worker_unlink( newdest ) == 0 ) {
212                                     nameok=true;
213                                     returnvalue=NM_NEWNAME_OK;
214                                 } else {
215                                     //                  textstr="Failed to remove this file|Please enter new name!";
216                                     //                  buttonstr="Ok|Skip|Cancel";
217 
218                                     textstr = dupstring( catalog.getLocale(276) );
219                                     buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
220                                                                 strlen(catalog.getLocale(225))+1+
221                                                                 strlen(catalog.getLocale(8))+1);
222                                     sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
223                                             catalog.getLocale(225),
224                                             catalog.getLocale(8));
225 
226                                     erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
227                                     _freesafe(buttonstr);
228                                     _freesafe( textstr );
229                                     if(erg==1) {
230                                         skip=true;
231                                         returnvalue=NM_NEWNAME_SKIP;
232                                     } else if(erg==2) {
233                                         cancel=true;
234                                         returnvalue=NM_NEWNAME_CANCEL;
235                                     }
236                                 }
237                                 break;
238                             case 3:
239                                 skip=true;
240                                 returnvalue=NM_NEWNAME_SKIP;
241                                 break;
242                             case 4:
243                                 cancel=true;
244                                 returnvalue=NM_NEWNAME_CANCEL;
245                                 break;
246                         }
247                     } else {
248                         // just use the link to the dir
249                         nameok=true;
250                         returnvalue=NM_NEWNAME_USE;
251                     }
252                 } else {
253                     trydel=false;
254                     if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_ALWAYS)) {
255                         trydel=true;
256                     } else if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_NEVER)) {
257                         // skip
258                         skip=true;
259                         returnvalue=NM_NEWNAME_SKIP;
260                     } else {
261                         if(lse==Datei::D_FE_LINK) {
262                             //              textstr="There is already a link named %s";
263                             //              buttonstr="Enter new name|Delete link|Skip|Cancel";
264 
265                             textstr = (char*)_allocsafe( strlen( catalog.getLocale( 278 ) ) + strlen( newdest ) + 1 );
266                             sprintf( textstr, catalog.getLocale( 278 ), newdest );
267                             buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
268                                                         strlen(catalog.getLocale(274))+1+
269                                                         strlen(catalog.getLocale(225))+1+
270                                                         strlen(catalog.getLocale(8))+1);
271                             sprintf(buttonstr,"%s|%s|%s|%s",catalog.getLocale(230),
272                                     catalog.getLocale(274),
273                                     catalog.getLocale(225),
274                                     catalog.getLocale(8));
275                         } else {
276                             //              textstr="There is already a file named %s";
277                             //              buttonstr="Enter new name|Delete file|Skip|Cancel";
278 
279                             textstr = (char*)_allocsafe( strlen( catalog.getLocale( 279 ) ) + strlen( newdest ) + 1 );
280                             sprintf( textstr, catalog.getLocale( 279 ), newdest );
281                             buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
282                                                         strlen(catalog.getLocale(277))+1+
283                                                         strlen(catalog.getLocale(225))+1+
284                                                         strlen(catalog.getLocale(8))+1);
285                             sprintf(buttonstr,"%s|%s|%s|%s",catalog.getLocale(230),
286                                     catalog.getLocale(277),
287                                     catalog.getLocale(225),
288                                     catalog.getLocale(8));
289                         }
290                         erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
291                         _freesafe(buttonstr);
292                         _freesafe(textstr);
293                         switch(erg) {
294                             case 1:
295                                 trydel=true;
296                                 break;
297                             case 2:
298                                 skip=true;
299                                 returnvalue=NM_NEWNAME_SKIP;
300                                 break;
301                             case 3:
302                                 cancel=true;
303                                 returnvalue=NM_NEWNAME_CANCEL;
304                                 break;
305                         }
306                     }
307                     if(trydel==true) {
308                         if ( worker_unlink( newdest ) == 0 ) {
309                             nameok=true;
310                             returnvalue=NM_NEWNAME_OK;
311                         } else {
312                             //              textstr="Failed to remove this file|Please enter new name!";
313                             //              buttonstr="Ok|Skip|Cancel";
314 
315                             textstr = dupstring( catalog.getLocale(276) );
316                             buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
317                                                         strlen(catalog.getLocale(225))+1+
318                                                         strlen(catalog.getLocale(8))+1);
319                             sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
320                                     catalog.getLocale(225),
321                                     catalog.getLocale(8));
322 
323                             erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
324                             _freesafe(buttonstr);
325                             _freesafe( textstr );
326                             if(erg==1) {
327                                 skip=true;
328                                 returnvalue=NM_NEWNAME_SKIP;
329                             } else if(erg==2) {
330                                 cancel=true;
331                                 returnvalue=NM_NEWNAME_CANCEL;
332                             }
333                         }
334                     }
335                 }
336                 break;
337             default:
338                 nameok=true;
339                 returnvalue=NM_NEWNAME_OK;
340                 break;
341         }
342         _freesafe(newdest);
343         round++;
344     } while((nameok==false)&&(skip==false)&&(cancel==false));
345     if((skip==true)||(cancel==true)) {
346         _freesafe(newname);
347         *newname_return=NULL;
348     } else {
349         *newname_return=newname;
350     }
351     _freesafe( title );
352 
353     if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->conttimer();
354 
355     return returnvalue;
356 }
357 
getNewName4File(const FileEntry * oldfe,const char * dest,char ** newname_return,bool acceptrename)358 CopyCore::nm_newname_t CopyCore::getNewName4File( const FileEntry *oldfe, const char *dest,
359                                                       char **newname_return, bool acceptrename )
360 {
361     char *oldname=oldfe->name;
362     char *newname=dupstring(oldname);
363     bool nameok=false,skip=false,cancel=false;
364     char *buttonstr,*textstr,*newdest;
365     int erg;
366     Datei::d_fe_t lse;
367     int round=1;
368     nm_newname_t returnvalue=NM_NEWNAME_CANCEL;
369     bool do_rename,trydel;
370     char *extrastr,*tstr,*newtime,*oldtime;
371     int newsizelen,oldsizelen,maxsizelen;
372     char *title;
373     time_t tvar;
374     std::string newsize, oldsize;
375 
376     if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->stoptimer();
377 
378     title = (char*)_allocsafe( strlen( catalog.getLocale( 116 ) ) + strlen( oldfe->fullname ) + 1 );
379     sprintf( title, catalog.getLocale( 116 ), oldfe->fullname );
380 
381     do_rename=false;
382     if(m_copyorder->do_rename==false) do_rename=false;
383     else if(acceptrename==true) do_rename=true;
384 
385     do {
386         if(!((round==1)&&(do_rename==false))) {
387             //      buttonstr="Ok|Skip|Cancel";
388             //      textstr="Enter new name for ...";
389             buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
390                                         strlen(catalog.getLocale(225))+1+
391                                         strlen(catalog.getLocale(8))+1);
392             sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
393                     catalog.getLocale(225),
394                     catalog.getLocale(8));
395             textstr=(char*)_allocsafe(strlen(catalog.getLocale(148))+strlen(oldname)+1);
396             sprintf(textstr,catalog.getLocale(148),oldname);
397 
398             std::string return_str;
399 
400             erg = m_copyorder->cowin->string_request( catalog.getLocale( 149 ),
401                                                     textstr,
402                                                     newname,
403                                                     buttonstr,
404                                                     return_str,
405                                                     Requester::REQUEST_SELECTALL );
406             _freesafe(buttonstr);
407             _freesafe(textstr);
408             if ( ! return_str.empty() ) {
409                 _freesafe(newname);
410                 newname = dupstring( return_str.c_str() );
411             }
412             if(erg==1) {
413                 skip=true;
414                 returnvalue=NM_NEWNAME_SKIP;
415                 break;
416             } else if(erg==2) {
417                 cancel=true;
418                 returnvalue=NM_NEWNAME_CANCEL;
419                 break;
420             }
421         }
422 
423         if ( strlen( newname ) < 1 ) {
424             // empty name->request
425             round++;  // not needed since empty names should only come from user input
426             // but perhaps there is (will be) a OS which allows empty names
427             continue;
428         }
429 
430         newdest=(char*)_allocsafe(strlen(dest)+1+strlen(newname)+1);
431         strcpy(newdest,dest);
432         if(strlen(dest)>1) strcat(newdest,"/");
433         strcat(newdest,newname);
434         lse=Datei::lfileExistsExt(newdest);
435 
436         switch(lse) {
437             case Datei::D_FE_DIR:
438                 //        textstr="there is already a dir called %s";
439                 //        buttonstr="Enter new name|Skip|Cancel";
440 
441                 textstr = (char*)_allocsafe( strlen( catalog.getLocale( 190 ) ) + strlen( newdest ) + 1 );
442                 sprintf( textstr, catalog.getLocale( 190 ), newdest );
443 
444                 buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
445                                             strlen(catalog.getLocale(225))+1+
446                                             strlen(catalog.getLocale(8))+1);
447                 sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(230),
448                         catalog.getLocale(225),
449                         catalog.getLocale(8));
450 
451                 erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
452                 _freesafe(buttonstr);
453                 _freesafe(textstr);
454                 if(erg==1) {
455                     skip=true;
456                     returnvalue=NM_NEWNAME_SKIP;
457                 } else if(erg==2) {
458                     cancel=true;
459                     returnvalue=NM_NEWNAME_CANCEL;
460                 }
461                 break;
462             case Datei::D_FE_LINK:
463                 trydel=false;
464                 if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_ALWAYS)) {
465                     trydel=true;
466                 } else if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_NEVER)) {
467                     // skip
468                     skip=true;
469                     returnvalue=NM_NEWNAME_SKIP;
470                 } else {
471                     //          textstr="there is already a link named %s";
472                     //          buttonstr="Enter new name|Delete link|Skip|Cancel";
473 
474                     textstr = (char*)_allocsafe( strlen( catalog.getLocale( 278 ) ) + strlen( newdest ) + 1 );
475                     sprintf( textstr, catalog.getLocale( 278 ), newdest );
476                     buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
477                                                 strlen(catalog.getLocale(274))+1+
478                                                 strlen(catalog.getLocale(225))+1+
479                                                 strlen(catalog.getLocale(8))+1);
480                     sprintf(buttonstr,"%s|%s|%s|%s",catalog.getLocale(230),
481                             catalog.getLocale(274),
482                             catalog.getLocale(225),
483                             catalog.getLocale(8));
484 
485                     erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
486                     _freesafe(buttonstr);
487                     _freesafe(textstr);
488                     switch(erg) {
489                         case 1:
490                             trydel=true;
491                             break;
492                         case 2:
493                             skip=true;
494                             returnvalue=NM_NEWNAME_SKIP;
495                             break;
496                         case 3:
497                             cancel=true;
498                             returnvalue=NM_NEWNAME_CANCEL;
499                             break;
500                     }
501                 }
502                 if(trydel==true) {
503                     if ( worker_unlink( newdest ) == 0 ) {
504                         nameok=true;
505                         returnvalue=NM_NEWNAME_OK;
506                     } else {
507                         //            textstr="Failed to remove this file|Please enter new name!";
508                         //            buttonstr="Ok|Skip|Cancel";
509 
510                         textstr = dupstring( catalog.getLocale(276) );
511                         buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
512                                                     strlen(catalog.getLocale(225))+1+
513                                                     strlen(catalog.getLocale(8))+1);
514                         sprintf(buttonstr,"%s|%s|%s",catalog.getLocale(11),
515                                 catalog.getLocale(225),
516                                 catalog.getLocale(8));
517 
518                         erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
519                         _freesafe(buttonstr);
520                         _freesafe( textstr );
521                         if(erg==1) {
522                             skip=true;
523                             returnvalue=NM_NEWNAME_SKIP;
524                         } else if(erg==2) {
525                             cancel=true;
526                             returnvalue=NM_NEWNAME_CANCEL;
527                         }
528                     }
529                 }
530                 break;
531             case Datei::D_FE_FILE:
532                 if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_ALWAYS)) {
533                     nameok=true;
534                     returnvalue=NM_NEWNAME_OVERWRITE;
535                 } else if((do_rename==false)&&(m_copyorder->overwrite==m_copyorder->COPY_OVERWRITE_NEVER)) {
536                     skip=true;
537                     returnvalue=NM_NEWNAME_SKIP;
538                 } else {
539                     FileEntry *newfe=new FileEntry();
540                     if(newfe->name!=NULL) _freesafe(newfe->name);
541                     if(newfe->fullname!=NULL) _freesafe(newfe->fullname);
542                     newfe->name=dupstring(newname);
543                     newfe->fullname=dupstring(newdest);
544                     newfe->readInfos();
545 
546                     MakeLong2NiceStr( newfe->size(), newsize );
547                     MakeLong2NiceStr( oldfe->size(), oldsize );
548                     oldsizelen = oldsize.length();
549                     newsizelen = newsize.length();
550                     maxsizelen = ( newsizelen > oldsizelen ) ? newsizelen : oldsizelen;
551                     if ( maxsizelen > newsizelen )
552                         newsize.insert( newsize.begin(), maxsizelen - newsizelen, ' ' );
553                     if ( maxsizelen > oldsizelen )
554                         oldsize.insert( oldsize.begin(), maxsizelen - oldsizelen, ' ' );
555 
556                     tvar = newfe->lastmod();
557                     newtime = dupstring( ctime( &( tvar ) ) );
558                     tvar = oldfe->lastmod();
559                     oldtime = dupstring( ctime( &( tvar ) ) );
560                     newtime[strlen(newtime)-1]='\0';  // remove return
561                     oldtime[strlen(oldtime)-1]='\0';  // remove return
562 
563                     extrastr=(char*)_allocsafe(strlen(catalog.getLocale(137))+newsize.length()+oldsize.length()+
564                                                strlen(newtime)+strlen(oldtime)+1);
565                     sprintf(extrastr,catalog.getLocale(137),newsize.c_str(),oldsize.c_str(),newtime,oldtime);
566 
567                     _freesafe(newtime);
568                     _freesafe(oldtime);
569                     delete newfe;
570                     //          textstr="There is already a file named %s";
571                     //          buttonstr="Enter new name|Overwrite|Overwrite all|Overwrite none|Skip|Cancel";
572 
573                     textstr = (char*)_allocsafe( strlen( catalog.getLocale( 279 ) ) + strlen( newdest ) + 1 );
574                     sprintf( textstr, catalog.getLocale( 279 ), newdest );
575 
576                     tstr=catstring(textstr,"|");
577                     _freesafe(textstr);
578                     textstr=catstring(tstr,extrastr);
579                     _freesafe(tstr);
580                     _freesafe(extrastr);
581 
582                     buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(230))+1+
583                                                 strlen(catalog.getLocale(280))+1+
584                                                 strlen(catalog.getLocale(135))+1+
585                                                 strlen(catalog.getLocale(136))+1+
586                                                 strlen(catalog.getLocale(225))+1+
587                                                 strlen(catalog.getLocale(8))+1);
588                     sprintf(buttonstr,"%s|%s|%s|%s|%s|%s",catalog.getLocale(230),
589                             catalog.getLocale(280),
590                             catalog.getLocale(135),
591                             catalog.getLocale(136),
592                             catalog.getLocale(225),
593                             catalog.getLocale(8));
594 
595                     erg = m_copyorder->cowin->request( title/*catalog.getLocale(123)*/, textstr, buttonstr );
596                     _freesafe(buttonstr);
597                     _freesafe(textstr);
598                     switch(erg) {
599                         case 1:
600                             nameok=true;
601                             returnvalue=NM_NEWNAME_OVERWRITE;
602                             break;
603                         case 2:
604                             nameok=true;
605                             returnvalue=NM_NEWNAME_OVERWRITE;
606                             m_copyorder->overwrite=m_copyorder->COPY_OVERWRITE_ALWAYS;
607                             break;
608                         case 3:
609                             skip=true;
610                             returnvalue=NM_NEWNAME_SKIP;
611                             m_copyorder->overwrite=m_copyorder->COPY_OVERWRITE_NEVER;
612                             break;
613                         case 4:
614                             skip=true;
615                             returnvalue=NM_NEWNAME_SKIP;
616                             break;
617                         case 5:
618                             cancel=true;
619                             returnvalue=NM_NEWNAME_CANCEL;
620                             break;
621                     }
622                 }
623                 break;
624             default:
625                 nameok=true;
626                 returnvalue=NM_NEWNAME_OK;
627                 break;
628         }
629         _freesafe(newdest);
630         round++;
631     } while((nameok==false)&&(skip==false)&&(cancel==false));
632     if((skip==true)||(cancel==true)) {
633         _freesafe(newname);
634         *newname_return=NULL;
635     } else {
636         *newname_return=newname;
637     }
638     _freesafe( title );
639 
640     if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->conttimer();
641 
642     return returnvalue;
643 }
644 
645 CopyCore::nm_copy_t
copydir(const FileEntry * fe,NM_CopyOp_Dir * cod,bool acceptrename,const char * destdir,std::string * effective_target_fullname)646 CopyCore::copydir( const FileEntry *fe, NM_CopyOp_Dir *cod, bool acceptrename, const char *destdir,
647                    std::string *effective_target_fullname )
648 {
649     NM_CopyOp_Dir *cod1 = NULL;
650     bool nameok,createdir;
651     nm_newname_t e1;
652     char *destsubdir;
653     Datei::d_fe_t de1;
654     char *newname=NULL;
655     int id1;
656     FileEntry *subfe;
657     bool isdir,skip;
658     char *buttonstr,*textstr;
659     int erg;
660     CopyOpWin *cowin=m_copyorder->cowin;
661     bool docopy,failedmove=false;
662     struct utimbuf utb;
663     bool corrupt_entries_skipped = false;
664 
665     skip = false;
666     if ( cowin != NULL ) {
667         // call newfile without destination so the user
668         // can see the filename in the copyopwin when he will
669         // be asked for the new name
670         cowin->newfile( fe->name, "" );
671     }
672 
673     if ( m_cancel == false ) {
674         e1=getNewName4Dir(fe,destdir,&newname,acceptrename);
675         if((e1==NM_NEWNAME_OK)||(e1==NM_NEWNAME_USE)) {
676             // build new name
677             destsubdir=(char*)_allocsafe(strlen(destdir)+1+strlen(newname)+1);
678             strcpy(destsubdir,destdir);
679             if(strlen(destsubdir)>1) {
680                 if ( destsubdir[strlen(destsubdir) - 1] != '/' ) {
681                     strcat( destsubdir, "/" );
682                 }
683             }
684             strcat(destsubdir,newname);
685 
686             nameok=false;
687             createdir=true;
688             if(e1==NM_NEWNAME_USE) {
689                 // there is already such dir (or a symlink to a dir) -> use it
690                 nameok=true;
691                 createdir=false;
692             } else if(e1==NM_NEWNAME_OK) {
693                 // check for fileexists (should not)
694                 // if nethertheless then skip this dir because the rename-func wasn't able to remove it
695                 de1=Datei::lfileExistsExt(destsubdir);
696                 if(de1==Datei::D_FE_NOFILE) {
697                     // everything is ok
698                     nameok=true;
699                 } else if(de1==Datei::D_FE_DIR) {
700                     // dest is a dir
701                     // should not happend but ok
702                     nameok=true;
703                     createdir=false;
704                     e1=NM_NEWNAME_USE; // for moving it's important to set this, so it doesn't try to
705                     // move
706                 }
707             }
708             if(nameok==true) {
709                 docopy=true;
710                 failedmove=false;
711                 if(m_copyorder->move==true) {
712                     if(e1==NM_NEWNAME_USE) {
713                         // because of using the destination dir
714                         // we need to do as failed rename
715                         failedmove=true;
716                     } else {
717                         if ( m_copyorder->adjust_relative_symlinks != m_copyorder->COPY_ADJUST_SYMLINK_NEVER ) {
718                             // skip trying to rename dir so that we can handle symlinks within the dir
719 
720                             // assume that rename has failed so the source
721                             // dir is removed if copying was successful
722                             failedmove = true;
723                         } else if ( worker_rename( fe->fullname, destsubdir ) == 0 ) {
724                             // success
725                             docopy=false;
726                         } else {
727                             // failure
728                             failedmove=true;
729                         }
730                     }
731                 }
732 
733                 if(docopy==true) {
734                     if(cowin!=NULL) cowin->newfile(fe->name,destsubdir);
735                     if(createdir==true) {
736                         if ( worker_mkdir( destsubdir, 0700 ) != 0 ) {
737                             // failed to create dir -> skip
738                             //            textstr="Failed to create dir|I will skip this dir!";
739                             //            buttonstr="Ok|Cancel";
740 
741                             if(cowin!=NULL) cowin->stoptimer();
742                             textstr=(char*)_allocsafe(strlen(catalog.getLocale(126))+strlen(destsubdir)+1);
743                             sprintf(textstr,catalog.getLocale(126),destsubdir);
744                             buttonstr=(char*)_allocsafe(strlen(catalog.getLocale(11))+1+
745                                                         strlen(catalog.getLocale(8))+1);
746                             sprintf(buttonstr,"%s|%s",catalog.getLocale(11),
747                                     catalog.getLocale(8));
748                             erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
749                             _freesafe(buttonstr);
750                             _freesafe(textstr);
751                             if(erg==1) m_cancel=true;
752                             nameok=false;
753                             if(cowin!=NULL) cowin->conttimer();
754                         }
755                     }
756                     if(nameok==true) {
757                         // first copy all subdirs
758                         // then copy all files
759                         id1=cod->subdirs->initEnum();
760                         cod1=(NM_CopyOp_Dir*)cod->subdirs->getFirstElement(id1);
761                         while(cod1!=NULL) {
762                             nm_copy_t ce1;
763                             ce1=copydir(cod1->fileentry,cod1,false,destsubdir, NULL);
764                             if((ce1==NM_COPY_OK)||(ce1==NM_COPY_NEED_DELETE)) {
765                                 // success
766                                 cod->error_counter+=cod1->error_counter;
767                             } else {
768                                 cod->error_counter+=cod1->error_counter+1;
769                                 if(ce1==NM_COPY_CANCEL) {
770                                     m_cancel=true;
771                                     break;
772                                 }
773                             }
774                             cod1=(NM_CopyOp_Dir*)cod->subdirs->getNextElement(id1);
775                         }
776                         cod->subdirs->closeEnum(id1);
777 
778                         // next only if read this dir correctly
779                         if((cod->ok==true)&&(m_cancel==false)) {
780                             for ( Verzeichnis::verz_it subfe_it1 = cod->verz->begin();
781                                   subfe_it1 != cod->verz->end() && m_cancel == false;
782                                   subfe_it1++ ) {
783 
784                                 checkAskToCancel();
785 
786                                 subfe = *subfe_it1;
787 
788                                 if ( subfe->isCorrupt == true && subfe->isLink == false ) {
789                                     if ( cowin != NULL ) cowin->stoptimer();
790 
791                                     std::string text = AGUIXUtils::formatStringToString( catalog.getLocale( 909 ),
792                                                                                          subfe->fullname );
793                                     std::string button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
794                                     erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
795                                     if ( erg == 0 ) {
796                                     } else {
797                                         m_cancel = true;
798                                     }
799 
800                                     if ( cowin != NULL ) cowin->conttimer();
801 
802                                     corrupt_entries_skipped = true;
803                                     continue;
804                                 }
805 
806                                 if(strcmp(subfe->name,"..")!=0) {
807                                     isdir=false;
808                                     if(subfe->isDir()==true) {
809                                         if(subfe->isLink==false) isdir=true;
810                                         else if(m_copyorder->follow_symlinks==true) isdir=true;
811                                     }
812                                     if(isdir==false) {
813                                         nm_copy_t cf_res;
814                                         cf_res=copyfile(subfe,false,destsubdir, NULL);
815                                         if((cf_res==NM_COPY_OK)||(cf_res==NM_COPY_NEED_DELETE)) {
816                                             // success
817                                         } else {
818                                             cod->error_counter++;
819                                             if(cf_res==NM_COPY_CANCEL) {
820                                                 m_cancel=true;
821                                                 break;
822                                             }
823                                         }
824                                     }
825                                 }
826                             }
827                         }
828                         // finally change the permissions of the dir
829                         if((createdir==true)&&(m_copyorder->preserve_attr==true)) {
830                             // fe is a dir so if it is a link it is not corrupt
831                             mode_t m = ( fe->isLink == true ) ? fe->dmode() : fe->mode();
832                             m = getModeWithEnsureOption( m, *m_copyorder );
833 
834                             int res = worker_chown( destsubdir, ( fe->isLink == true ) ? fe->duserid() : fe->userid(),
835                                                     ( fe->isLink == true ) ? fe->dgroupid() : fe->groupid() );
836                             (void)res; // ignore result for now
837                             worker_chmod( destsubdir, m );
838                             utb.actime = ( fe->isLink == true ) ? fe->dlastaccess() : fe->lastaccess();
839                             utb.modtime = ( fe->isLink == true ) ? fe->dlastmod() : fe->lastmod();
840                             worker_utime(destsubdir,&utb);
841                         }
842                     } else skip=true;
843                 }
844             } else skip=true;
845 
846             if ( effective_target_fullname != NULL ) {
847                 *effective_target_fullname = destsubdir;
848             }
849 
850             _freesafe(destsubdir);
851         } else {
852             switch(e1) {
853                 case NM_NEWNAME_CANCEL:
854                     m_cancel=true;
855                 default:
856                     skip=true;
857                     break;
858             }
859         }
860     }
861     if((skip==true)&&(cowin!=NULL)) {
862         // dir skiped so dec CopyOpWin counter by the files/dirs skiped
863         cowin->dec_file_counter(cod->files);
864         cowin->dec_dir_counter(cod->dirs);
865         cowin->dec_byte_counter(cod->bytes);
866     }
867 
868     if ( corrupt_entries_skipped == true ) skip = true;
869 
870     if(cowin!=NULL) {
871         cowin->dir_finished();
872     }
873     if(newname!=NULL) _freesafe(newname);
874     if(m_cancel==true) return NM_COPY_CANCEL;
875     else if(skip==true) {
876         cod->error_counter++;
877         return NM_COPY_SKIP;
878     }
879     if(m_copyorder->move==true) {
880         if((cod->error_counter==0)&&(failedmove==true)) {
881             // put fe in list
882 
883             // Man koennte das FE auch also Kopie benutzen
884             // man muesste nur den Vergleich beim spaeteren Loeschen anpassen
885             pushForPostDelete( fe );
886             return NM_COPY_NEED_DELETE;
887         }
888     }
889     return NM_COPY_OK;
890 }
891 
892 CopyCore::nm_copy_t
copyfile(const FileEntry * fe,bool acceptrename,const char * destdir,std::string * effective_target_fullname)893 CopyCore::copyfile( const FileEntry *fe, bool acceptrename, const char *destdir, std::string *effective_target_fullname )
894 {
895     bool nameok;
896     nm_newname_t e1;
897     char *newfullname;
898     Datei::d_fe_t de1;
899     char *newname=NULL;
900     bool useregcopy, skip;
901     int erg;
902     CopyOpWin *cowin=m_copyorder->cowin;
903     bool docopy,failedmove=false,opok=false;
904     struct utimbuf utb;
905     const char *myerrstr;
906     loff_t bytesToCopy;
907     mode_t modeCheck;
908     bool useLinkDest;
909     std::string text, button;
910 
911     if ( fe->isLink == true && fe->isCorrupt == false && m_copyorder->follow_symlinks ) {
912         bytesToCopy = fe->dsize();
913     } else {
914         bytesToCopy = fe->size();
915     }
916 
917     skip = false;
918     if ( cowin != NULL ) {
919         // call newfile without destination so the user
920         // can see the filename in the copyopwin when he will
921         // be asked for the new name
922         cowin->newfile( fe->name, "" );
923     }
924 
925     if ( fe->isLink == false && fe->isCorrupt == true ) {
926         if ( cowin != NULL ) cowin->stoptimer();
927 
928         text = AGUIXUtils::formatStringToString( catalog.getLocale( 909 ),
929                                                  fe->fullname );
930         button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
931         erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
932         if ( erg == 0 ) {
933             skip = true;
934         } else {
935             m_cancel = true;
936         }
937 
938         if ( cowin != NULL ) cowin->conttimer();
939     }
940 
941     if ( m_cancel == false && skip == false ) {
942         e1=getNewName4File(fe,destdir,&newname,acceptrename);
943 
944         if((e1==NM_NEWNAME_OK)||(e1==NM_NEWNAME_OVERWRITE)) {
945             // build new name
946             newfullname=(char*)_allocsafe(strlen(destdir)+1+strlen(newname)+1);
947             strcpy(newfullname,destdir);
948             if(strlen(newfullname)>1) {
949                 if ( newfullname[strlen(newfullname) - 1] != '/' ) {
950                     strcat( newfullname, "/" );
951                 }
952             }
953             strcat(newfullname,newname);
954 
955             nameok=false;
956             skip=false;
957             if(e1==NM_NEWNAME_OVERWRITE) {
958                 nameok=true;
959             } else if(e1==NM_NEWNAME_OK) {
960                 // check for fileexists (should not)
961                 de1=Datei::lfileExistsExt(newfullname);
962                 if(de1==Datei::D_FE_NOFILE) {
963                     // everything is ok
964                     nameok=true;
965                 } else if(de1==Datei::D_FE_FILE) {
966                     // dest is a dir
967                     // should not happend but ok try to overwrite this
968                     nameok=true;
969                 }
970             }
971             if(nameok==true) {
972                 if ( fe->isSameFile( newfullname, m_copyorder->follow_symlinks ) == false ) {
973                     // wenn move true, dann rename probieren
974                     //        klappt rename nicht, muss flag gesetzt werden und normal weitergemacht
975                     //        werden und bei korrekten kopieren FE in liste gepackt werden und NEED_DELETE
976                     //        zurueckgeliefert werden
977                     //      also: 1.flag docopy am Anfang auf true setzen
978                     //            2.bei move==true rename probieren
979                     //            2.1.bei Erfolg docopy auf false, Rueckgabewert ist automatisch OK
980                     //            2.2.bei Misslingen failedmove auf true setzen
981                     //            3.bei docopy==true normal kopieren
982                     //            3.1.bei Misslingen kommt halt skip zurueck
983                     //            3.2.Bei Erfolg und failedmove==true muss FE in liste gepackt werden
984                     //                und NEED_DELETE zurueckgeliefert werden
985                     docopy=true;
986                     failedmove=false;
987 
988                     if ( cowin != NULL ) cowin->newfile( fe->name, newfullname );
989 
990                     if(m_copyorder->move==true) {
991                         if ( fe->isLink == true &&
992                              m_copyorder->adjust_relative_symlinks != m_copyorder->COPY_ADJUST_SYMLINK_NEVER ) {
993                             // skip trying to rename file for symlinks that possibly need to be adjusted
994 
995                             // assume that rename has failed so the source
996                             // file is removed if copying was successful
997                             failedmove = true;
998                         } else if ( worker_rename( fe->fullname, newfullname ) == 0 ) {
999                             // success
1000                             docopy=false;
1001                         } else {
1002                             // failure
1003                             failedmove=true;
1004                         }
1005                     }
1006 
1007                     if(docopy==true) {
1008                         // first try to open the file
1009                         // in case of failure give a requester with choose "Delete"
1010                         // then try to delete it and retry to open
1011                         // if all fails, skip
1012                         useregcopy = false;
1013                         if ( S_ISREG( fe->mode() ) ) useregcopy = true;
1014                         else if ( ( fe->isLink == true ) &&
1015                                   ( m_copyorder->follow_symlinks == true ) &&
1016                                   ( fe->isCorrupt == false ) &&
1017                                   ( S_ISREG( fe->dmode() ) ) ) useregcopy = true;
1018 
1019                         if ( useregcopy == true ) {
1020                             reg_copy_erg_t rce = copyfile_reg( fe->fullname,
1021                                                                newfullname,
1022                                                                destdir,
1023                                                                newname,
1024                                                                ( fe->isLink == true ) ? fe->dmode() : fe->mode(),
1025                                                                m_copyorder->cowin,
1026                                                                bytesToCopy );
1027                             if ( rce == REG_COPY_OK ) {
1028                                 opok = true;
1029                             } else if ( rce == REG_COPY_SKIP ) {
1030                                 skip = true;
1031                             } else {
1032                                 // everything else (including ERROR) is cancel
1033                                 m_cancel = true;
1034                             }
1035                         } else {
1036                             // no regular copy
1037                             if ( e1 == NM_NEWNAME_OVERWRITE ) {
1038                                 // there is already a file but because we cannot overwrite
1039                                 // with special file I will delete it
1040                                 // remember the user already choose to overwrite in getNewName!
1041                                 if ( worker_remove( newfullname ) != 0 ) {
1042                                     // remove failed => cannot continue
1043 
1044                                     if ( cowin != NULL ) cowin->stoptimer();
1045                                     myerrstr = strerror( errno );
1046                                     text = AGUIXUtils::formatStringToString( catalog.getLocale( 516 ), fe->fullname, newfullname, myerrstr );
1047                                     button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1048                                     erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1049                                     if ( erg == 0 ) {
1050                                         skip = true;
1051                                     } else {
1052                                         m_cancel = true;
1053                                     }
1054                                     if ( cowin != NULL ) cowin->conttimer();
1055                                 }
1056                             }
1057                             //TODO: mc will skip corrupt links when follow_symlinks is used
1058                             //      I could do this too if I set modeCheck = 0 so it will got to
1059                             //      the else part
1060                             if ( ( fe->isLink == true ) &&
1061                                  ( m_copyorder->follow_symlinks == true ) &&
1062                                  ( fe->isCorrupt == false ) ) {
1063                                 useLinkDest = true;
1064                                 modeCheck = fe->dmode();
1065                             } else {
1066                                 useLinkDest = false;
1067                                 modeCheck = fe->mode();
1068                             }
1069 
1070                             if ( ( skip == false ) && ( m_cancel == false ) ) {
1071                                 if ( ( S_ISCHR( modeCheck ) ) ||
1072 #ifdef S_ISSOCK
1073                                      ( S_ISSOCK( modeCheck ) ) ||
1074 #endif
1075                                      ( S_ISBLK( modeCheck ) ) ) {
1076                                     worker_struct_stat statbuf;
1077 
1078                                     if ( useLinkDest == true ) {
1079                                         erg = worker_stat( fe->fullname, &statbuf );
1080                                     } else {
1081                                         erg = worker_lstat( fe->fullname, &statbuf );
1082                                     }
1083                                     if ( erg == 0 ) {
1084                                         // mknod
1085                                         mode_t m = statbuf.st_mode;
1086                                         m = getModeWithEnsureOption( m, *m_copyorder );
1087 
1088                                         if ( worker_mknod( newfullname, m, statbuf.st_rdev ) != 0 ) {
1089                                             if ( cowin != NULL ) cowin->stoptimer();
1090                                             myerrstr = strerror( errno );
1091                                             text = AGUIXUtils::formatStringToString( catalog.getLocale( 1090 ), fe->fullname, newfullname, "mknod", myerrstr );
1092                                             button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1093                                             erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1094                                             if ( erg == 0 ) {
1095                                                 skip = true;
1096                                             } else {
1097                                                 m_cancel = true;
1098                                             }
1099                                             if ( cowin != NULL ) cowin->conttimer();
1100                                         } else opok = true;
1101                                     } else {
1102                                         if ( cowin != NULL ) cowin->stoptimer();
1103                                         myerrstr = strerror( errno );
1104                                         text = AGUIXUtils::formatStringToString( catalog.getLocale( 518 ), fe->fullname, newfullname, myerrstr );
1105                                         button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1106                                         erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1107                                         if ( erg == 0 ) {
1108                                             skip = true;
1109                                         } else {
1110                                             m_cancel = true;
1111                                         }
1112                                         if ( cowin != NULL ) cowin->conttimer();
1113                                     }
1114                                 } else if ( S_ISFIFO( modeCheck ) ) {
1115                                     // mkfifo
1116                                     mode_t m = modeCheck;
1117                                     m = getModeWithEnsureOption( m, *m_copyorder );
1118 
1119                                     if ( mkfifo( newfullname, m ) != 0 ) {
1120                                         if ( cowin != NULL ) cowin->stoptimer();
1121                                         myerrstr = strerror( errno );
1122                                         text = AGUIXUtils::formatStringToString( catalog.getLocale( 1090 ), fe->fullname, newfullname, "mkfifo", myerrstr );
1123                                         button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1124                                         erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1125                                         if ( erg == 0 ) {
1126                                             skip = true;
1127                                         } else {
1128                                             m_cancel = true;
1129                                         }
1130                                         if ( cowin != NULL ) cowin->conttimer();
1131                                     } else opok = true;
1132                                 } else if ( S_ISLNK( modeCheck ) ) {
1133                                     // it's a link so create a new symlink which points to the same destination
1134                                     char *linkdest = fe->getDestination();
1135                                     if ( linkdest != NULL ) {
1136                                         if ( linkdest[0] != '/' ) {
1137                                             bool adjust = false;
1138 
1139                                             if ( m_copyorder->adjust_relative_symlinks == m_copyorder->COPY_ADJUST_SYMLINK_ALWAYS ) {
1140                                                 adjust = true;
1141                                             } else if ( m_copyorder->adjust_relative_symlinks == m_copyorder->COPY_ADJUST_SYMLINK_OUTSIDE ) {
1142                                                 // relative symlink, try to determine new correct one
1143 
1144                                                 // that would be the new target directory of the link
1145                                                 std::string dirname_of_target = NWC::Path::get_real_path( NWC::Path::dirname( newfullname ) );
1146                                                 if ( dirname_of_target.empty() ) dirname_of_target = NWC::Path::dirname( newfullname );
1147 
1148                                                 std::string fullnewlink = NWC::Path::join( dirname_of_target,
1149                                                                                            linkdest );
1150                                                 fullnewlink = NWC::Path::normalize( fullnewlink );
1151 
1152                                                 // fullnewlink is the destination of the symlinks
1153 
1154                                                 std::string real_destdir = NWC::Path::get_real_path( m_copyorder->destdir );
1155                                                 if ( real_destdir.empty() ) real_destdir = m_copyorder->destdir;
1156 
1157                                                 // check if points outside the copy destination dir
1158                                                 // which means that the real_destdir must be a prefix of the link name
1159                                                 if ( ! NWC::Path::is_prefix_dir( real_destdir,
1160                                                                                  NWC::Path::dirname( fullnewlink ) ) ) {
1161                                                     adjust = true;
1162                                                 }
1163                                             }
1164 
1165                                             if ( adjust ) {
1166                                                 std::string dirname_of_source = NWC::Path::get_real_path( NWC::Path::dirname( fe->fullname ) );
1167                                                 if ( dirname_of_source.empty() ) dirname_of_source = NWC::Path::dirname( fe->fullname );
1168 
1169                                                 std::string fulloldlink = NWC::Path::join( dirname_of_source,
1170                                                                                            linkdest );
1171                                                 fulloldlink = NWC::Path::normalize( fulloldlink );
1172 
1173                                                 char *newrellinkdest = Datei::getRelativePathExt( fulloldlink.c_str(),
1174                                                                                                   NWC::Path::dirname( newfullname ).c_str(),
1175                                                                                                   true );
1176 
1177                                                 if ( newrellinkdest && strlen( newrellinkdest ) > 0 ) {
1178                                                     free( linkdest );
1179                                                     linkdest = newrellinkdest;
1180                                                 }
1181                                             }
1182                                         }
1183                                         if ( worker_symlink( linkdest, newfullname ) != 0 ) {
1184                                             if ( cowin != NULL ) cowin->stoptimer();
1185                                             myerrstr = strerror( errno );
1186                                             text = AGUIXUtils::formatStringToString( catalog.getLocale( 1090 ), fe->fullname, newfullname, "symlink", myerrstr );
1187                                             button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1188                                             erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1189                                             if ( erg == 0 ) {
1190                                                 skip = true;
1191                                             } else {
1192                                                 m_cancel = true;
1193                                             }
1194                                             if ( cowin != NULL ) cowin->conttimer();
1195                                         } else opok = true;
1196                                         _freesafe( linkdest );
1197                                     } else {
1198                                         if ( cowin != NULL ) cowin->stoptimer();
1199                                         myerrstr = strerror( errno );
1200                                         text = AGUIXUtils::formatStringToString( catalog.getLocale( 519 ), fe->fullname, newfullname, myerrstr );
1201                                         button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1202                                         erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1203                                         if ( erg == 0 ) {
1204                                             skip = true;
1205                                         } else {
1206                                             m_cancel = true;
1207                                         }
1208                                         if ( cowin != NULL ) cowin->conttimer();
1209                                     }
1210                                 } else {
1211                                     if ( cowin != NULL ) cowin->stoptimer();
1212                                     myerrstr = strerror( errno );
1213                                     text = AGUIXUtils::formatStringToString( catalog.getLocale( 520 ), fe->fullname );
1214                                     button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1215                                     erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1216                                     if ( erg == 0 ) {
1217                                         skip = true;
1218                                     } else {
1219                                         m_cancel = true;
1220                                     }
1221                                     if ( cowin != NULL ) cowin->conttimer();
1222                                 }
1223                             }
1224                             //TODO: muss hier nicht eher add_curbytes_copied aufgerufen werden?
1225                             //            if(cowin!=NULL) cowin->set_bytes_to_copy_curfile(fe->size);
1226                             if ( cowin != NULL ) cowin->add_curbytes_copied( fe->size() );
1227                         }
1228                     }
1229                 } else {
1230                     text = AGUIXUtils::formatStringToString( catalog.getLocale( 113 ), fe->fullname );
1231                     button = catalog.getLocale( 11 );
1232 
1233                     cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1234                 }
1235             } else skip=true;
1236 
1237             if ( opok == true ) {
1238                 // try to change the mode because open modify it with umask
1239                 // it doesn't matter if this fails
1240                 if(m_copyorder->preserve_attr==true) {
1241                     //TODO:currently deactivated
1242                     //     chown is necessary because link can have an owner
1243                     //     but chmod could be optional
1244                     // only apply new mode and time if it's not a link
1245                     // doing this would not do anything wrong
1246                     // but chown/chmod/utime would update the access/change time
1247                     // of the destination file although we don't have to touch it
1248                     //if ( ( fe->isLink == false ) || ( copyorder->follow_symlinks == true ) ) {
1249                     mode_t m = ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->dmode() : fe->mode();
1250                     m = getModeWithEnsureOption( m, *m_copyorder );
1251 
1252                     tryApplyOwnerPerm( newfullname,
1253                                        ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->duserid() : fe->userid(),
1254                                        ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->dgroupid() : fe->groupid(),
1255                                        m );
1256                     utb.actime = ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->dlastaccess() : fe->lastaccess();
1257                     utb.modtime = ( ( fe->isLink == true ) && ( fe->isCorrupt == false ) ) ? fe->dlastmod() : fe->lastmod();
1258                     worker_utime(newfullname,&utb);
1259                     //}
1260                 }
1261             }
1262 
1263             if ( effective_target_fullname != NULL ) {
1264                 *effective_target_fullname = newfullname;
1265             }
1266 
1267             _freesafe(newfullname);
1268         } else {
1269             switch(e1) {
1270                 case NM_NEWNAME_CANCEL:
1271                     m_cancel=true;
1272                 default:
1273                     skip=true;
1274                     break;
1275             }
1276         }
1277     }
1278 
1279     if(cowin!=NULL) {
1280         if ( skip == true ) {
1281             // the CopyOpWin need to substract bytesToCopy from global bytes sum
1282             // and already copied bytes from copyied bytes sum
1283             // if we called newfile() (which resets this counter)
1284             cowin->file_skipped( bytesToCopy, true );
1285         } else {
1286             cowin->file_finished();
1287         }
1288     }
1289     if(newname!=NULL) _freesafe(newname);
1290     if(m_cancel==true) return NM_COPY_CANCEL;
1291     else if(skip==true) return NM_COPY_SKIP;
1292     if(m_copyorder->move==true) {
1293         if((opok==true)&&(failedmove==true)) {
1294             // remember: failedmove is true when OS function rename failed
1295             //           in this case we have to copy the file and delete the source
1296             //           so add this entry only when copy is complete (opok) AND
1297             //           rename failed (failedmove)
1298             // put fe in list
1299             pushForPostDelete( fe );
1300             return NM_COPY_NEED_DELETE;
1301         }
1302     }
1303     return NM_COPY_OK;
1304 }
1305 
copyfile_reg(const char * sourcename,const char * destname,const char * destdirname,const char * destbasename,mode_t create_mode,CopyOpWin * cowin,loff_t bytesToCopy)1306 CopyCore::reg_copy_erg_t CopyCore::copyfile_reg( const char *sourcename,
1307                                                  const char *destname,
1308                                                  const char *destdirname,
1309                                                  const char *destbasename,
1310                                                  mode_t create_mode,
1311                                                  CopyOpWin *cowin,
1312                                                  loff_t bytesToCopy )
1313 {
1314     int fdw, fdr;
1315     std::string text, button;
1316     ssize_t readbytes;
1317     reg_copy_erg_t return_value = REG_COPY_ERROR;
1318     int erg;
1319     const char *myerrstr;
1320 
1321     create_mode = getModeWithEnsureOption( create_mode, *m_copyorder );
1322 
1323     // firstly open source file
1324     fdr = worker_open( sourcename, O_RDONLY, 0 );
1325     if ( fdr == -1 ) {
1326         // can't open inputfile
1327         if ( cowin != NULL ) cowin->stoptimer();
1328 
1329         text = AGUIXUtils::formatStringToString( catalog.getLocale( 281 ), sourcename );
1330         button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1331         erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1332 
1333         if ( erg == 1 ) return_value = REG_COPY_CANCEL;
1334         else return_value = REG_COPY_SKIP;
1335 
1336         if ( cowin != NULL ) cowin->conttimer();
1337         return return_value;
1338     }
1339 
1340     if ( cowin != NULL ) {
1341         cowin->set_bytes_to_copy_curfile( bytesToCopy );
1342     }
1343 
1344     // secondly try to open destination file
1345     // two runs as I first try to open an existing file and only remove it if it doesn't
1346     // work. This is because we can overwrite a file but not create it
1347     for ( int i = 0; i < 2; i++ ) {
1348         // fe is a regular file or a working symlink to a regular file so we can use dmode() without checking
1349         // for corrupt link
1350         fdw = worker_open( destname, O_CREAT | O_TRUNC | O_WRONLY, create_mode );
1351 
1352         if ( fdw != -1 ) break;
1353 
1354         // open failed
1355         if ( cowin != NULL ) cowin->stoptimer();
1356         if ( errno == ENOSPC ) {
1357             text = AGUIXUtils::formatStringToString( catalog.getLocale( 545 ), destbasename, destdirname );
1358             button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 41 ) + "|" + catalog.getLocale( 8 );
1359             erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1360 
1361             if ( erg == 2 ) {
1362                 return_value = REG_COPY_CANCEL;
1363             } else if ( erg == 1 ) {
1364                 // just try again
1365                 i--;
1366             } else return_value = REG_COPY_SKIP;
1367         } else if ( errno == EACCES ) {
1368             if ( Datei::lfileExistsExt( destname ) != Datei::D_FE_NOFILE ) {
1369                 /// error
1370                 // give request
1371                 // textstr="Can't overwrite the file|Shall I try to delete it?";
1372                 // buttonstr="Delete file|Skip|Cancel";
1373 
1374                 text = AGUIXUtils::formatStringToString( catalog.getLocale( 282 ), destname );
1375                 button = std::string( catalog.getLocale( 277 ) ) +
1376                     "|" +
1377                     catalog.getLocale( 225 ) +
1378                     "|" +
1379                     catalog.getLocale( 8 );
1380                 erg = cowin->request( catalog.getLocale( 123 ), text.c_str(), button.c_str() );
1381 
1382                 if ( erg == 1 ) return_value = REG_COPY_SKIP;
1383                 else if ( erg == 2 ) return_value = REG_COPY_CANCEL;
1384                 else {
1385                     if ( worker_remove( destname ) != 0 ) {
1386                         // textstr="Failed to remove this file|I will skip this file!";
1387                         // buttonstr="Ok|Cancel";
1388 
1389                         text = AGUIXUtils::formatStringToString( catalog.getLocale( 138 ), destname );
1390                         button = std::string( catalog.getLocale( 11 ) ) + "|" + catalog.getLocale( 8 );
1391                         erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1392 
1393                         if ( erg == 1 ) return_value = REG_COPY_CANCEL;
1394                         else return_value = REG_COPY_SKIP;
1395                     }
1396                 }
1397             } else {
1398                 // "Can't open dest file"
1399                 text = AGUIXUtils::formatStringToString( catalog.getLocale( 198 ), destname );
1400                 button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1401                 erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1402 
1403                 if ( erg == 1 ) return_value = REG_COPY_CANCEL;
1404                 else return_value = REG_COPY_SKIP;
1405             }
1406         } else {
1407             text = AGUIXUtils::formatStringToString( catalog.getLocale( 198 ), destname );
1408             button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1409             erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1410 
1411             if ( erg == 1 ) return_value = REG_COPY_CANCEL;
1412             else return_value = REG_COPY_SKIP;
1413         }
1414         if ( cowin != NULL ) cowin->conttimer();
1415 
1416         if ( ( return_value == REG_COPY_SKIP ) || ( return_value == REG_COPY_CANCEL ) ) break;
1417     }
1418 
1419     if ( fdw == -1 ) {
1420         worker_close( fdr );
1421         return return_value;
1422     }
1423 
1424     loff_t total_bytes = 0;
1425     bool asked_about_source_change = false;
1426 
1427     // now copy from fdr to fdw
1428     do {
1429         readbytes = worker_read( fdr, buf, BUFSIZE );
1430         if ( readbytes < 0 ) {
1431             // error while reading
1432 
1433             if ( cowin != NULL ) cowin->stoptimer();
1434 
1435             myerrstr = strerror( errno );
1436             text = AGUIXUtils::formatStringToString( catalog.getLocale( 525 ), sourcename, myerrstr );
1437             button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1438             erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1439 
1440             if ( cowin != NULL ) cowin->conttimer();
1441 
1442             if ( erg == 0 ) return_value = REG_COPY_SKIP;
1443             else return_value = REG_COPY_CANCEL;
1444             break;
1445         } else if ( readbytes == 0 ) {
1446             // end of file
1447             break;
1448         }
1449 
1450         ssize_t bytes_written = 0;
1451 
1452         for ( ; bytes_written < readbytes; ) {
1453             const ssize_t bytes_left_to_write = readbytes - bytes_written;
1454             ssize_t writebytes;
1455 
1456             writebytes = worker_write( fdw, ((const char *)buf) + bytes_written, bytes_left_to_write );
1457 
1458             if ( writebytes > 0 ) {
1459                 bytes_written += writebytes;
1460             }
1461 
1462             if ( writebytes > 0 && cowin != NULL ) {
1463                 cowin->add_curbytes_copied( writebytes );
1464             }
1465 
1466             checkAskToCancel();
1467 
1468             if ( m_cancel ) {
1469                 return_value = REG_COPY_CANCEL;
1470             }
1471 
1472             if ( writebytes != bytes_left_to_write ) {
1473                 // something went wrong
1474                 // let the user choose to cancel or skip this file
1475                 // Ask to delete the incomplete destfile
1476                 // Attention: This method also moves files so be sure to
1477                 //   NOT delete the source!
1478                 if( ( ( writebytes >= 0 ) && ( writebytes < bytes_left_to_write ) ) ||
1479                     ( errno == ENOSPC ) ) {
1480                     // ENOSPC isn't always reported so assume ENOSPC
1481                     text = AGUIXUtils::formatStringToString( catalog.getLocale( 545 ), destbasename, destdirname );
1482                     button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 41 ) + "|" + catalog.getLocale( 8 );
1483                     erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1484 
1485                     if ( erg == 0 ) {
1486                         return_value = REG_COPY_SKIP;
1487                     } else if ( erg == 1 ) {
1488                         // try again
1489                     } else {
1490                         return_value = REG_COPY_CANCEL;
1491                     }
1492                 } else {
1493                     if ( writebytes < 0 ) {
1494                         myerrstr = strerror( errno );
1495                         text = AGUIXUtils::formatStringToString( catalog.getLocale( 526 ), destname, myerrstr );
1496                     } else {
1497                         text = AGUIXUtils::formatStringToString( catalog.getLocale( 359 ), destname );
1498                     }
1499                     button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1500                     erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1501 
1502                     if ( erg == 0 ) return_value = REG_COPY_SKIP;
1503                     else return_value = REG_COPY_CANCEL;
1504                 }
1505             }
1506 
1507             if ( return_value == REG_COPY_CANCEL || return_value == REG_COPY_SKIP ) break;
1508         }
1509 
1510         total_bytes += bytes_written;
1511 
1512         if ( total_bytes > bytesToCopy ) {
1513             // more bytes read than initially known
1514             // probably the source file has changed
1515             // ask what to do
1516 
1517             if ( ! asked_about_source_change ) {
1518 
1519                 text = AGUIXUtils::formatStringToString( catalog.getLocale( 1042 ), sourcename );
1520                 button = std::string( catalog.getLocale( 629 ) ) + "|" + catalog.getLocale( 225 ) + "|" + catalog.getLocale( 8 );
1521                 erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1522 
1523                 if ( erg == 0 ) {
1524                 } else if ( erg == 1 ) {
1525                     return_value = REG_COPY_SKIP;
1526                 } else {
1527                     return_value = REG_COPY_CANCEL;
1528                 }
1529 
1530                 asked_about_source_change = true;
1531             }
1532         }
1533 
1534     } while ( ( return_value != REG_COPY_CANCEL ) && ( return_value != REG_COPY_SKIP ) );
1535 
1536     cowin->update_file_status();
1537 
1538     if ( m_cancel ) {
1539         return_value = REG_COPY_CANCEL;
1540     }
1541 
1542     worker_close( fdr );
1543     if ( worker_close( fdw ) != 0 ) {
1544         // oops, close error
1545         myerrstr = strerror( errno );
1546         text = AGUIXUtils::formatStringToString( catalog.getLocale( 526 ), destname, myerrstr );
1547         button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1548         erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1549 
1550         if ( erg == 0 ) return_value = REG_COPY_SKIP;
1551         else return_value = REG_COPY_CANCEL;
1552     }
1553 
1554     if ( ( return_value == REG_COPY_CANCEL ) || ( return_value == REG_COPY_SKIP ) ) {
1555         // error while copying!
1556         // ask to remove the incomplete destination file
1557 
1558         if ( cowin != NULL ) cowin->stoptimer();
1559 
1560         text = AGUIXUtils::formatStringToString( catalog.getLocale( 393 ), destname );
1561         button = std::string( catalog.getLocale( 394 ) ) + "|" + catalog.getLocale( 395 );
1562         erg = cowin->request( catalog.getLocale( 123 ), text.c_str(), button.c_str() );
1563         if ( erg == 1 ) {
1564             if ( worker_unlink( destname ) != 0 ) {
1565                 // textstr="Failed to remove the destination file|This file is probably incomplete!";
1566                 text = AGUIXUtils::formatStringToString( catalog.getLocale( 139 ), destname );
1567                 button = catalog.getLocale( 11 );
1568                 erg = cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1569             }
1570         }
1571         if ( cowin != NULL ) cowin->conttimer();
1572     } else {
1573         // copy complete
1574         return_value = REG_COPY_OK;
1575     }
1576     return return_value;
1577 }
1578 
deleteCopiedButNotMoved(std::function<void (const std::pair<std::string,bool> &)> processed_callback)1579 int CopyCore::deleteCopiedButNotMoved( std::function< void( const std::pair< std::string, bool > & ) > processed_callback )
1580 {
1581     // now delete remaining files/dirs when moving
1582     if ( m_copyorder->move == true ) {
1583         bool delcancel; // additional cancel so we don't need to
1584         // clear cancel for the special case when move
1585         // has to copy&delete and was aborted somewhere
1586         // I ask the user what to do with the correctly copied
1587         // files
1588         std::string str1;
1589 
1590         if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->setmessage( "", 1 );
1591 
1592         auto itfe1 = m_copy_deletefe_list.begin();
1593 
1594         delcancel = false;
1595 
1596         if ( m_cancel == true && itfe1 != m_copy_deletefe_list.end() ) {
1597             // copy was canceled
1598             // ask the user what to do with the correctly moved files
1599             // (exactly what to do with the sources!!)
1600             str1 = catalog.getLocale( 11 );
1601             str1 += "|";
1602             str1 += catalog.getLocale( 8 );
1603             int erg = m_copyorder->cowin->request( catalog.getLocale( 123 ),
1604                                                    catalog.getLocale( 528 ),
1605                                                    str1.c_str() );
1606             if ( erg != 0 ) {
1607                 delcancel = true;
1608             }
1609         }
1610 
1611         while ( itfe1 != m_copy_deletefe_list.end() && delcancel == false ) {
1612             if ( itfe1->second == true ) {
1613                 if ( m_copyorder->cowin != NULL ) {
1614                     char *tstr = (char*)_allocsafe( strlen( catalog.getLocale( 141 ) ) +
1615                                                     strlen( itfe1->first.c_str() ) + 1 );
1616                     sprintf( tstr, catalog.getLocale( 141 ), itfe1->first.c_str() );
1617                     m_copyorder->cowin->setmessage( tstr, 0 );
1618                     _freesafe( tstr );
1619                 }
1620             }
1621 
1622             int erg;
1623 
1624             // remove tfe
1625             if ( itfe1->second == true ) {
1626                 erg = worker_rmdir( itfe1->first.c_str() );
1627             } else {
1628                 erg = worker_unlink( itfe1->first.c_str() );
1629             }
1630 
1631             if ( erg == 0 ) {
1632                 // success
1633                 processed_callback( *itfe1 );
1634             } else {
1635                 char *textstr = (char*)_allocsafe( strlen( catalog.getLocale( 291 ) ) +
1636                                                    strlen( itfe1->first.c_str() ) + 1 );
1637                 sprintf( textstr, catalog.getLocale( 291 ), itfe1->first.c_str() );
1638                 char *buttonstr = (char*)_allocsafe( strlen( catalog.getLocale( 11 ) ) + 1 +
1639                                                      strlen( catalog.getLocale( 8 ) ) + 1 );
1640                 sprintf( buttonstr, "%s|%s", catalog.getLocale( 11 ),
1641                          catalog.getLocale( 8 ) );
1642                 erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), textstr, buttonstr );
1643                 _freesafe( buttonstr );
1644                 _freesafe( textstr );
1645                 if ( erg == 1 ) delcancel = true;
1646             }
1647             if ( itfe1 != m_copy_deletefe_list.end() ) itfe1++;
1648         }
1649         if ( delcancel == true ) m_cancel = true;
1650     }
1651 
1652     return 0;
1653 }
1654 
getCancel() const1655 bool CopyCore::getCancel() const
1656 {
1657     return m_cancel;
1658 }
1659 
setCancel(bool nv)1660 void CopyCore::setCancel( bool nv )
1661 {
1662     // disallow clearing the flag
1663     if ( nv == true ) {
1664         m_cancel = nv;
1665     }
1666 }
1667 
pushForPostDelete(const FileEntry * fe)1668 void CopyCore::pushForPostDelete( const FileEntry *fe )
1669 {
1670     if ( fe->isDir() == true && fe->isLink == false ) {
1671         m_copy_deletefe_list.push_back( std::make_pair( fe->fullname, true ) );
1672     } else {
1673         m_copy_deletefe_list.push_back( std::make_pair( fe->fullname, false ) );
1674     }
1675 }
1676 
registerFEToCopy(const FileEntry & fe,int row,const std::vector<NWC::OS::deep_dir_info> & file_base_segments)1677 void CopyCore::registerFEToCopy( const FileEntry &fe,
1678                                  int row,
1679                                  const std::vector< NWC::OS::deep_dir_info > &file_base_segments )
1680 {
1681     m_copy_list.push_back( copy_base_entry( fe, row, file_base_segments ) );
1682 }
1683 
copy_base_entry(const FileEntry & fe,int row,const std::vector<NWC::OS::deep_dir_info> & file_base_segments)1684 CopyCore::copy_base_entry::copy_base_entry( const FileEntry &fe,
1685                                             int row,
1686                                             const std::vector< NWC::OS::deep_dir_info > &file_base_segments ) :
1687     m_row( row ),
1688     m_cod( NULL ),
1689     m_fe( fe ),
1690     m_file_base_segments( file_base_segments )
1691 {
1692 }
1693 
~copy_base_entry()1694 CopyCore::copy_base_entry::~copy_base_entry()
1695 {
1696     delete m_cod;
1697 }
1698 
buildCopyDatabase()1699 int CopyCore::buildCopyDatabase()
1700 {
1701     unsigned long files = 0,
1702         dirs = 0,
1703         gf = 0,
1704         gd = 0;
1705     loff_t bytes = 0;
1706 
1707     CopyOpWin *cowin = m_copyorder->cowin;
1708 
1709     cowin->starttimer();
1710 
1711     if ( cowin->redraw() & 1 ) m_cancel = true;
1712 
1713     // create the NM_CopyOp_Dir for each dir in copylist
1714 
1715     for ( auto &cbe : m_copy_list ) {
1716         if ( m_cancel == true ) break;
1717 
1718         bool enter = false;
1719 
1720         if ( cbe.entry().isDir() == true ) {
1721             // fe is a dir, check if it is a link and take it only when follow_symlinks==true
1722             // entry is a dir so it cannot be a corrupt link so no need to check
1723             if ( cbe.entry().isLink == false ) enter = true;
1724             else if ( m_copyorder->follow_symlinks == true ) enter = true;
1725         }
1726 
1727         if ( enter == true ) {
1728             // fe is a dir so creating corresponding entry
1729             NM_CopyOp_Dir *cod1 = new NM_CopyOp_Dir( &cbe.entry() );
1730 
1731             if ( cod1->user_abort == false ) {
1732                 // recursive call
1733                 if ( cod1->createSubDirs( m_copyorder, &gf, &gd ) != 0 ) {
1734                     m_cancel = true;
1735                 }
1736             } else {
1737                 m_cancel = true;
1738             }
1739 
1740             // add the values from this subdir to this dir
1741             files += cod1->files;
1742             dirs += cod1->dirs;
1743             bytes += cod1->bytes;
1744 
1745             cbe.m_cod = cod1;
1746             // this is a dir so inc the counter
1747             dirs++;
1748             gd++;
1749 
1750             cowin->set_files_to_copy( gf );
1751             cowin->set_dirs_to_copy( gd );
1752             if ( cowin->redraw() & 1 ) m_cancel = true;
1753         } else {
1754             // is not dir (mostly a file but can also be links ...)
1755             files++;
1756             gf++;
1757 
1758             if ( cbe.entry().isLink == true &&
1759                  m_copyorder->follow_symlinks == true &&
1760                  cbe.entry().isCorrupt == false ) {
1761                 bytes += cbe.entry().dsize();
1762             } else {
1763                 bytes += cbe.entry().size();
1764             }
1765             cbe.m_cod = NULL;
1766         }
1767     }
1768 
1769     cowin->set_files_to_copy( files );
1770     cowin->set_dirs_to_copy( dirs );
1771     cowin->set_bytes_to_copy( bytes );
1772 
1773     m_bytes_to_copy = bytes;
1774 
1775     // reset start timer for better time predictions
1776     cowin->starttimer();
1777 
1778     return 0;
1779 }
1780 
executeCopy()1781 int CopyCore::executeCopy()
1782 {
1783     m_copy_thread = std::thread( [this]() { executeCopyThread(); } );
1784 
1785     return 0;
1786 }
1787 
prepare_destination_dir(copy_base_entry & cbe,std::string & destdir,bool post_run)1788 int CopyCore::prepare_destination_dir( copy_base_entry &cbe,
1789                                        std::string &destdir,
1790                                        bool post_run )
1791 {
1792     destdir = m_copyorder->destdir;
1793     int res = 0;
1794 
1795     if ( m_copyorder->vdir_preserve_dir_structure &&
1796          ! cbe.get_file_base_segments().empty() ) {
1797 
1798         if ( post_run == false ||
1799              m_copyorder->preserve_attr == true ) {
1800             res = NWC::OS::make_dirs( destdir,
1801                                       cbe.get_file_base_segments(),
1802                                       post_run );
1803 
1804             if ( res != 0 ) {
1805                 if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->stoptimer();
1806                 auto text = AGUIXUtils::formatStringToString( catalog.getLocale( 1210 ), destdir.c_str() );
1807                 auto button = std::string( catalog.getLocale( 225 ) ) + "|" + catalog.getLocale( 8 );
1808                 auto erg = m_copyorder->cowin->request( catalog.getLocale( 347 ), text.c_str(), button.c_str() );
1809                 if ( erg == 0 ) {
1810                 } else {
1811                     m_cancel = true;
1812                 }
1813                 if ( m_copyorder->cowin != NULL ) m_copyorder->cowin->conttimer();
1814             }
1815         }
1816 
1817         for ( auto &s : cbe.get_file_base_segments() ) {
1818             destdir = NWC::Path::join( destdir, s.get_segment_name() );
1819         }
1820     }
1821 
1822     return res;
1823 }
1824 
executeCopyThread()1825 int CopyCore::executeCopyThread()
1826 {
1827     for ( auto &cbe : m_copy_list ) {
1828         checkAskToCancel();
1829 
1830         if ( m_cancel ) break;
1831 
1832         NM_CopyOp_Dir *tcod;
1833 
1834         tcod = cbe.m_cod;
1835 
1836         if ( m_pre_cb ) {
1837             loff_t size = 0;
1838 
1839             if ( cbe.entry().isLink && ! cbe.entry().isCorrupt ) {
1840                 size = cbe.entry().dsize();
1841             } else {
1842                 size = cbe.entry().size();
1843             }
1844 
1845             m_pre_cb( size, cbe.m_row, cbe.m_cod != NULL ? true : false );
1846         }
1847 
1848         if ( tcod != NULL ) {
1849             nm_copy_t ce1;
1850             std::string target_fullname;
1851             std::string destdir;
1852 
1853             if ( prepare_destination_dir( cbe, destdir, false ) == 0 ) {
1854                 ce1 = copydir( &cbe.entry(), tcod, true, destdir.c_str(), &target_fullname );
1855                 if ( ce1 == NM_COPY_OK || ce1 == NM_COPY_NEED_DELETE ) {
1856                     // success
1857                     Worker::pushCommandLog( m_copyorder->move ? catalog.getLocale( 1420 ) : catalog.getLocale( 1419 ),
1858                                             target_fullname,
1859                                             AGUIXUtils::formatStringToString( catalog.getLocale( 1421 ),
1860                                                                               cbe.entry().fullname,
1861                                                                               destdir.c_str() ) );
1862                 } else if ( ce1 == NM_COPY_CANCEL ) {
1863                     m_cancel = true;
1864                 }
1865 
1866                 prepare_destination_dir( cbe, destdir, true );
1867             } else {
1868                 if ( m_cancel ) {
1869                     ce1 = NM_COPY_CANCEL;
1870                 } else {
1871                     ce1 = NM_COPY_SKIP;
1872                 }
1873             }
1874 
1875             if ( m_post_cb ) {
1876                 m_post_cb( cbe.entry().fullname, cbe.m_row,
1877                            ce1,
1878                            cbe.m_cod != NULL ? true : false,
1879                            cbe.m_cod != NULL ? cbe.m_cod->error_counter : 0,
1880                            target_fullname );
1881             }
1882         } else {
1883             nm_copy_t ce1;
1884             std::string target_fullname;
1885             std::string destdir;
1886 
1887             if ( prepare_destination_dir( cbe, destdir, false ) == 0 ) {
1888                 ce1 = copyfile( &cbe.entry(), true, destdir.c_str(), &target_fullname );
1889 
1890                 if ( ce1 == NM_COPY_OK || ce1 == NM_COPY_NEED_DELETE ) {
1891                     // success
1892                     Worker::pushCommandLog( m_copyorder->move ? catalog.getLocale( 1420 ) : catalog.getLocale( 1419 ),
1893                                             target_fullname,
1894                                             AGUIXUtils::formatStringToString( catalog.getLocale( 1421 ),
1895                                                                               cbe.entry().fullname,
1896                                                                               destdir.c_str() ) );
1897                 } else if ( ce1 == NM_COPY_CANCEL ) {
1898                     m_cancel = true;
1899                 }
1900 
1901                 prepare_destination_dir( cbe, destdir, true );
1902             } else {
1903                 if ( m_cancel ) {
1904                     ce1 = NM_COPY_CANCEL;
1905                 } else {
1906                     ce1 = NM_COPY_SKIP;
1907                 }
1908             }
1909 
1910             if ( m_post_cb ) {
1911                 m_post_cb( cbe.entry().fullname, cbe.m_row,
1912                            ce1,
1913                            cbe.m_cod != NULL ? true : false,
1914                            cbe.m_cod != NULL ? cbe.m_cod->error_counter : 0,
1915                            target_fullname );
1916             }
1917         }
1918     }
1919 
1920     setFinished();
1921 
1922     return 0;
1923 }
1924 
setPreCopyCallback(std::function<void (loff_t size,int row,bool is_dir)> cb)1925 void CopyCore::setPreCopyCallback( std::function< void( loff_t size, int row, bool is_dir ) > cb )
1926 {
1927     m_pre_cb = cb;
1928 }
1929 
setPostCopyCallback(std::function<void (const std::string & source_fullname,int row,nm_copy_t res,bool is_dir,unsigned long dir_error_counter,const std::string & target_fullname)> cb)1930 void CopyCore::setPostCopyCallback( std::function< void( const std::string &source_fullname, int row,
1931                                                          nm_copy_t res,
1932                                                          bool is_dir, unsigned long dir_error_counter,
1933                                                          const std::string &target_fullname ) > cb )
1934 {
1935     m_post_cb = cb;
1936 }
1937 
getBytesToCopy() const1938 loff_t CopyCore::getBytesToCopy() const
1939 {
1940     return m_bytes_to_copy;
1941 }
1942 
1943 /*
1944  * tryApplyOwnerPerm will try to set owner and permission
1945  *   it will apply SUID/SGID only if owner or root (as root only
1946  *   if chown don't fail)
1947  * this function don't care if chmod/chown fails
1948  *
1949  * returnvalue:
1950  *   0 okay
1951  *   -1 wrong args
1952  *   Bit 0 set: SUID cleared
1953  *   Bit 1 set: SGID cleared
1954  *   Bit 2 set: chown failed
1955  *   Bit 3 set: chmod failed
1956  */
tryApplyOwnerPerm(const char * filename,const uid_t wanted_user,const gid_t wanted_group,const mode_t wanted_mode)1957 int CopyCore::tryApplyOwnerPerm( const char *filename,
1958                                  const uid_t wanted_user,
1959                                  const gid_t wanted_group,
1960                                  const mode_t wanted_mode )
1961 {
1962     mode_t use_mode;
1963     int erg = 0, chownres, chmodres;
1964 
1965     if ( filename == NULL ) return -1;
1966 
1967     chownres = worker_chown( filename, wanted_user, wanted_group );
1968     if ( chownres != 0 ) {
1969         erg |= 1<<2;
1970     }
1971     use_mode = wanted_mode;
1972     if ( ( use_mode & S_ISUID ) != 0 ) {
1973         // SUID bit set, check if we still want it
1974         // root will apply it (when chown didn't failed and)
1975         // and euid/egid == ower
1976         if ( ! ( ( geteuid() == wanted_user ) ||
1977                  ( ( geteuid() == 0 ) && ( chownres == 0 ) ) ) ) {
1978             use_mode &= ~S_ISUID;
1979             erg |= 1<<0;
1980         }
1981     }
1982     if ( ( use_mode & S_ISGID ) != 0 ) {
1983         // SGID bit set, check if we still want it
1984         // root will apply it (when chown didn't failed and)
1985         // and euid/egid == ower
1986         if ( ! ( ( getegid() == wanted_group ) ||
1987                  ( ( geteuid() == 0 ) && ( chownres == 0 ) ) ) ) {
1988             use_mode &= ~S_ISGID;
1989             erg |= 1<<1;
1990         }
1991     }
1992     chmodres = chmod ( filename, use_mode );
1993     if ( chmodres != 0 ) {
1994         erg |= 1<<3;
1995     }
1996     if ( ( ( erg & 3 ) != 0 ) && ( m_copyorder != NULL ) ) {
1997         // removed SUID/SGUID bit, let the user know this
1998         if ( m_copyorder->ignoreLosedAttr != true ) {
1999             int choose;
2000             char *textstr;
2001             std::string str2;
2002 
2003             textstr = (char*)_allocsafe( strlen( catalog.getLocale( 536 ) ) + strlen( filename ) + 1 );
2004             sprintf( textstr, catalog.getLocale( 536 ), filename );
2005             str2 = catalog.getLocale( 11 );
2006             str2 += "|";
2007             str2 += catalog.getLocale( 537 );
2008             choose = m_copyorder->cowin->request( catalog.getLocale( 125 ),
2009                                                   textstr,
2010                                                   str2.c_str(),
2011                                                   Requester::REQUEST_CANCELWITHLEFT );
2012             _freesafe( textstr );
2013             if ( choose == 1 ) {
2014                 m_copyorder->ignoreLosedAttr = true;
2015             }
2016         }
2017     }
2018     return erg;
2019 }
2020 
getCopyOrder()2021 std::shared_ptr< struct copyorder > CopyCore::getCopyOrder()
2022 {
2023     return m_copyorder;
2024 }
2025 
finished()2026 bool CopyCore::finished()
2027 {
2028     std::unique_lock< std::mutex> l( m_finished_mutex );
2029 
2030     return m_finished;
2031 }
2032 
join()2033 void CopyCore::join()
2034 {
2035     m_copy_thread.join();
2036 }
2037 
checkAskToCancel()2038 void CopyCore::checkAskToCancel()
2039 {
2040     if ( m_ask_to_cancel ) {
2041         m_ask_to_cancel = false;
2042 
2043         std::string str1;
2044         int erg;
2045 
2046         // user canceled -> request to be sure
2047         str1 = catalog.getLocale( 11 );
2048         str1 += "|";
2049         str1 += catalog.getLocale( 544 );
2050         erg = m_copyorder->cowin->request( catalog.getLocale( 123 ),
2051                                            catalog.getLocale( 543 ),
2052                                            str1.c_str() );
2053         if ( erg == 0 ) {
2054             m_cancel = true;
2055         }
2056     }
2057 }
2058 
setAskToCancel()2059 void CopyCore::setAskToCancel()
2060 {
2061     m_ask_to_cancel = true;
2062 }
2063 
timed_wait_for_finished()2064 bool CopyCore::timed_wait_for_finished()
2065 {
2066     std::unique_lock< std::mutex> l( m_finished_mutex );
2067 
2068     if ( ! m_finished ) {
2069         m_finished_cond.wait_for( l,
2070                                   std::chrono::duration<long long, std::milli>( 1000 / 25 ) );
2071     }
2072 
2073     return m_finished;
2074 }
2075 
setFinished()2076 void CopyCore::setFinished()
2077 {
2078     std::unique_lock< std::mutex> l( m_finished_mutex );
2079 
2080     m_finished = true;
2081 
2082     m_finished_cond.notify_all();
2083 }
2084