1 /* ---------------------------------- executenow.cpp ---------------------------------------------------------------------------
2 do everything that deals with commands (rsync & others) execution
3 
4 ===============================================================================================================================
5 ===============================================================================================================================
6     This file is part of "luckyBackup" project
7     Copyright, Loukas Avgeriou
8     luckyBackup is distributed under the terms of the GNU General Public License
9     luckyBackup is free software: you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation, either version 3 of the License, or
12     (at your option) any later version.
13 
14     luckyBackup is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with luckyBackup.  If not, see <http://www.gnu.org/licenses/>.
21 
22 
23 project version	: Please see "main.cpp" for project version
24 
25 developer          : luckyb
26 last modified      : 22 May 2016
27 ===============================================================================================================================
28 ===============================================================================================================================
29 ********************************** DO NOT FORGET TO CHANGE "commandline.cpp:rsyncIT()" ********************************************************
30 */
31 
32 #include <iostream>     // cout
33 using namespace std;
34 #include <math.h>       // pow
35 
36 #include <QProcess>
37 #include <QTextCodec>
38 #include <QTextStream>
39 #include <QTime>
40 #include <QToolBar>
41 
42 #include "global.h"
43 #include "luckybackupwindow.h"
44 #include "operationClass.h"
45 
46 QProcess *syncProcess;
47 QTime StartTime(0,0,0,0);	//find out elapsed time from these;
48 
49 // executeNOW=================================================================================================
50 // executes rsync (& other commands )and displays progress.
executeNOW()51 void luckyBackupWindow::executeNOW ()
52 {
53     //Variables initialization
54     // do NOT intitialise specific task variables here. Use the executeBeforeTask() function instead
55 
56     NOWexecuting = true;		//this is mainly used if the window close button (or alt+F4) is pressed
57     ABORTpressed = false;		//becomes true if abort button pressed
58     //change gui to execute mode !!
59     guiModeNormal = false;
60 
61     (ui.rsyncOutput->document())->setMaximumBlockCount(50000);	// set the maximum block count for the output window to avoid freezing
62 
63     swapGUI("execution");
64     ui.AbortButton -> setVisible (true);
65     ui.DoneButton -> setVisible (false);
66     ui.rsyncOutput -> setText("");
67 
68     outputString = "";
69     outputError = "";
70     calculating = false;		//these 3 bools are used to diplay progress of rsync at the info window
71     transferring = false;
72     deleting = false;
73     ExecuteBefore=false;
74     ExecuteBeforeExitedError = false;
75     ProcReportedError = false;
76     StopTaskExecution = false;
77     ExecuteAfter=false;
78     writeToLog=false;
79     errorsFound = 0;		//total error founds during profile execution
80     filesTransfered = 0;    //total bytes transfered during profile execution
81     bytesTransfered = 0;    //total bytes transfered during profile execution
82     count = 0;
83     currentAfter = 0;
84     currentBefore = 0;
85     errorCount = 0;			//errors found during task execution
86     repeatOnFailTry = 0;
87     repeatOnFailMax = 0;
88 
89     //initiate tray icon
90     if (QSystemTrayIcon::isSystemTrayAvailable ())
91     {
92         createTrayIcon();	//create the tray icon
93         LBtray -> show();	// show the tray icon
94     }
95     //display a popup baloon
96     trayMessage = 	tr("execution of profile:","tray message - execution of profile: <PROFILENAME> in progress...") + " " +
97             profileName + " " + tr("in progress...","tray message - execution of profile: <PROFILENAME> in progress...");
98     if (DryRun)
99         trayMessage.append("\n(" + tr("simulation mode") + ")");
100     if ( (QSystemTrayIcon::isSystemTrayAvailable ()) && (QSystemTrayIcon::supportsMessages ()) )
101     {
102         if (KDErunning)
103         {
104             QProcess *dialogProcess;    dialogProcess = new QProcess(this);
105             QStringList dialogArgs;     dialogArgs << "--title" << appName << "--passivepopup" << trayMessage << "10";
106             dialogProcess -> start ("kdialog",dialogArgs);
107         }
108         else
109             LBtray -> showMessage (appName, trayMessage, QSystemTrayIcon::Information,3000);
110     }
111     //Initialize shhutdown button
112     ui.pushButton_shutdown -> setChecked(false);
113 
114     connect ( ui.AbortButton, SIGNAL( clicked() ), this, SLOT( abortPressed() ) );	//connect abort pushButton SLOT ----------------
115     connect ( ui.DoneButton, SIGNAL( clicked() ), this, SLOT( donePressed() ) );	//connect done pushButton SLOT ----------------
116 
117 /*    if (WINrunning)           // disable VSS until...
118     {
119       vssTimer= new QTimer(this);
120 
121         pipeVssFile =  new QFile(tempDirPath+SLASH+"qt_tempvss"+QString::number(qrand() % (999998) + 1));
122         if (pipeVssFile->open(QIODevice::ReadWrite))
123         {
124             pipeVssFile->close();
125 //            if (pipeVssFile->open(QIODevice::ReadOnly | QIODevice::Text))
126 //              connect(pipeVssFile,SIGNAL(readyRead()),this,SLOT(appendRsyncVssOutput()));
127         }
128         pipeVssErrFile =  new QFile(tempDirPath+SLASH+"qt_tempvsserr"+QString::number(qrand() % (999998) + 1));
129         if (pipeVssErrFile->open(QIODevice::ReadWrite)){
130             pipeVssErrFile->close();
131 //            if (pipeVssErrFile->open(QIODevice::ReadOnly | QIODevice::Text))
132 //              connect(pipeVssErrFile,SIGNAL(readyRead()),this,SLOT(appendRsyncVssOutput()));
133         }
134         if (Operation[currentOperation]->GetOptionsVss())
135         {
136             connect (vssTimer,SIGNAL(timeout()),this,SLOT(appendRsyncVssOutput()));
137             vssTimer->start(vssSleepTime);
138         }
139     }*/
140 
141     syncProcess = new QProcess(this);	//create a new qprocess (for rsync) & connect signals
142     connect(syncProcess, SIGNAL(readyReadStandardError()), this, SLOT(appendRsyncOutput()));
143     connect(syncProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(appendRsyncOutput()));
144     connect(syncProcess, SIGNAL (finished(int, QProcess::ExitStatus)), this, SLOT(procFinished()));
145     connect(syncProcess, SIGNAL (error(QProcess::ProcessError)), this, SLOT(procError()));
146 
147     if (silentMode)
148         minimizeTray();
149 
150     //rsync command & arguments initiation
151     command = rsyncCommandPath;
152     rsyncArguments.clear();
153     syncAB = true;
154 
155     StartTime.start();	//start the timer to measure elapsed time
156 
157     currentOperation = 0;
158 
159     //increase currentOperation until first operation to be executed (execute tasks with a "by-pass WARNING option too)
160     while ( (currentOperation < TotalOperations) && (!Operation[currentOperation] -> GetPerform()) )
161             currentOperation++;
162 
163     executeBeforeTask();				//execute pre-task commands if any
164 }
165 
166 // swapGUI=====================================================================================================
167 //swaps the gui mode from normal to execute
swapGUI(QString GUImode)168 void luckyBackupWindow::swapGUI(QString GUImode)
169 {
170     if (GUImode == "execution")	//change gui to execution mode
171     {
172         ui.pushButton_start	-> setVisible (false);
173         ui.listWidget_operations -> setVisible (false);
174         ui.textBrowser_info	-> setVisible (false);
175         ui.label_TaskList -> setText(tr("commands output","output window label"));
176         ui.frame_operations -> setToolTip(tr("rsync and pre/post task commands output is displayed here","tooltip"));
177 
178         ui.pushButton_up    -> setVisible (false);
179         ui.pushButton_down  -> setVisible (false);
180         ui.label_include    -> setVisible (false);
181         ui.menuFile         -> setEnabled (false);
182         ui.menu_Task        -> setEnabled (false);
183         languageMenu        -> setEnabled (false);
184         settingsMenu        -> setEnabled (false);
185         helpMenu            -> setEnabled (false);
186         if (!runImmediately)
187             IsVisibleProfileToolbar = profileToolbar -> isVisible();//hold the visibility state of this toolbar
188         profileToolbar      -> setVisible (false);
189         profileComboToolbar -> setEnabled (false);
190         ui.comboBox_profile -> setEnabled (false);
191         ui.groupBox_task    -> setVisible (false);
192         ui.checkBox_DryRun  -> setEnabled (false);
193         ui.pushButton_exit  -> setVisible (false);
194         ui.label            -> setVisible (false);
195 
196         ui.AbortButton              -> setVisible (true);
197         ui.DoneButton               -> setVisible (true);
198         ui.rsyncOutput              -> setVisible (true);
199         //ui.pushButton_InfoCollapse -> setChecked(!IsVisibleInfoWindow);
200         shutdownToolbar-> setVisible (true);
201         if ( (KDErunning) || (currentUser == "super user") )
202             shutdownToolbar-> setEnabled (true);
203         else
204             shutdownToolbar-> setEnabled (false);
205         ui.nowDoing                 -> setVisible (IsVisibleInfoWindow);
206         ui.OperationProgress        -> setVisible (true);
207         if (QSystemTrayIcon::isSystemTrayAvailable ())
208             ui.pushButton_minimizeToTray	-> setVisible (true);
209         else
210             ui.pushButton_minimizeToTray	-> setVisible (false);
211         errorsToolbar-> setVisible (true);
212         ui.pushButton_nextError	-> setVisible (true);
213         ui.pushButton_previousError	-> setVisible (true);
214         ui.pushButton_nextError	-> setEnabled (false);
215         ui.pushButton_previousError	-> setEnabled (false);
216         ui.checkBox_onlyShowErrors -> setVisible (true);
217     }
218     else		//change gui to normal mode !!
219     {
220         ui.AbortButton              -> setVisible (false);
221         ui.DoneButton               -> setVisible (false);
222         ui.rsyncOutput              -> setVisible (false);
223         ui.nowDoing                 -> setVisible (false);
224         ui.OperationProgress        -> setVisible (false);
225         ui.pushButton_minimizeToTray-> setVisible (false);
226         ui.pushButton_nextError     -> setVisible (false);
227         ui.pushButton_previousError -> setVisible (false);
228         ui.checkBox_onlyShowErrors  -> setVisible (false);
229 
230         ui.pushButton_start	-> setVisible (true);
231         ui.listWidget_operations -> setVisible (true);
232         ui.pushButton_InfoCollapse -> setChecked(!IsVisibleInfoWindow);
233         ui.textBrowser_info	-> setVisible (IsVisibleInfoWindow);
234         ui.label_TaskList -> setText(tr("Task list","task list label"));
235         ui.frame_operations -> setToolTip(tr("List of all the available tasks","task list tooltip - line1")+"\n"+
236                 tr("Use the 'include checkboxes' to include or not a selected task","task list tooltip - line2"));
237 
238         ui.pushButton_up    -> setVisible (true);
239         ui.pushButton_down  -> setVisible (true);
240         ui.label_include    -> setVisible (true);
241         ui.menuFile         -> setEnabled (true);
242         ui.menu_Task        -> setEnabled (true);
243         languageMenu        -> setEnabled (true);
244         settingsMenu        -> setEnabled (true);
245         helpMenu            -> setEnabled (true);
246         profileToolbar      -> setVisible (IsVisibleProfileToolbar);
247         profileComboToolbar -> setEnabled (true);
248         shutdownToolbar     -> setVisible (false);
249         errorsToolbar     -> setVisible (false);
250         ui.comboBox_profile -> setEnabled (true);
251         ui.groupBox_task    -> setVisible (true);
252         ui.checkBox_DryRun  -> setEnabled (true);
253         ui.pushButton_exit  -> setVisible (false);	// set this to true to make the exit button visible at normal mode
254         ui.label            -> setVisible (true);
255     }
256 }
257 
258 // done button pressed=====================================================================================================
donePressed()259 void luckyBackupWindow::donePressed()
260 {
261     ui.rsyncOutput -> setText("");
262     guiModeNormal = true;
263     swapGUI("normal");
264 
265     if (QSystemTrayIcon::isSystemTrayAvailable ())	// this prevents a segfault when system tray is NOT available
266         LBtray -> hide();	// hide the tray icon
267 
268     refreshList(); //refresh the listWidget_operations
269 
270     InfoData = QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate) + "<br>" +
271         tr("Execution of profile","full phrase: 'Execution of profile <PROFILENAME> finished'") +
272         " <b>" + profileName + "</b> " + tr("finished","full phrase: 'Execution of profile <PROFILENAME> finished'")+" !!<br>";
273     if (DryRun)
274         InfoData.append("(" + tr("simulation mode") +")");
275     else
276     {
277         if (!savedProfile)
278             InfoData.append(tr("Could not update last execution time of tasks") + "<br>" + profile.errorString());
279         else
280             InfoData.append(tr("Last execution time of tasks updated"));
281     }
282     ui.textBrowser_info -> setText(InfoData);
283 }
284 // abort button pressed=====================================================================================================
abortPressed()285 void luckyBackupWindow::abortPressed()
286 {
287     //if (syncProcess->state() == QProcess::NotRunning)	//if syncProcess is not Running (done pressed)
288 
289     // the next condition is used because for some wierd reason the abort button is presses multiple times
290     // if you launch LB, run a profile twice and abort both of them. The second run you abort, will emitt 2 abort clicked() signals !!!
291     if (ABORTpressed)
292         return;
293 
294     ui.rsyncOutput->append("<br><br><font color=red>" + tr("Aborting: Please wait for all processes to be killed") + "...</font>");
295     ExecuteBefore = false;
296     ExecuteAfter = false;
297     ABORTpressed = true;
298 
299     syncProcess -> kill();	//kill rsyncProcess
300     syncProcess -> waitForFinished();
301 
302     setNowDoing ();		//update Nowdoing textBrowser
303 
304 }
305 
306 // createTrayIcon =============================================================================================================================
307 // create the tray icon
308 // This is only called when system tray is available
createTrayIcon()309 void luckyBackupWindow::createTrayIcon()
310 {
311     //actions----------------------------------
312     LBtrayMenu = new QMenu(this);
313     actionAbort = new QAction(QIcon(":/luckyPrefix/abort.png"), tr("&Abort"), this);
314     minimizeToTray = new QAction(QIcon(":/luckyPrefix/window_minimize.png"), tr("&Minimize to tray","tray menu action"), this);
315     restoreFromTray = new QAction(QIcon(":/luckyPrefix/window_restore.png"), tr("&Restore","tray menu action"), this);
316 
317     connect( actionAbort, SIGNAL(triggered()), this, SLOT(abortPressed()));		//tray icon action ABORT
318     connect( minimizeToTray, SIGNAL(triggered()), this, SLOT(minimizeTray()));			//tray icon action minimize to Tray
319     connect( restoreFromTray, SIGNAL(triggered()), this, SLOT(restoreTray()));		//tray icon action restore from tray
320 
321     //context menu----------------------------
322     LBtrayMenu	-> addAction(minimizeToTray);
323     LBtrayMenu	-> addAction(minimizeToTray);
324     LBtrayMenu	-> addSeparator();
325     LBtrayMenu	-> addAction(actionAbort);
326 
327     //tray icon--------------------------------
328     LBtray = new QSystemTrayIcon(QIcon(":/luckyPrefix/luckybackup_96.png"),this);
329     LBtray -> setContextMenu(LBtrayMenu);
330     if (isMinimizedToTray == true)
331     {
332         minimizeToTray 	-> setVisible(false);
333         restoreFromTray	-> setVisible(true);
334     }
335     else
336     {
337         minimizeToTray 	-> setVisible(true);
338         restoreFromTray	-> setVisible(false);
339     }
340 
341     //connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(messageClicked()));
342     connect(LBtray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
343         this, SLOT(LBtrayActivated(QSystemTrayIcon::ActivationReason)));
344 }
345 
346 // minimizeTray =============================================================================================================================
347 // minimizes the gui to the tray
minimizeTray()348 void luckyBackupWindow::minimizeTray()
349 {
350     if (QSystemTrayIcon::isSystemTrayAvailable ())
351     {
352         minimizeToTray 	-> setVisible(false);
353         restoreFromTray	-> setVisible(true);
354     }
355     isMinimizedToTray = true;
356     this -> hide();
357 }
358 // restoreTray =============================================================================================================================
359 // restores the gui from the tray
restoreTray()360 void luckyBackupWindow::restoreTray()
361 {
362     if (QSystemTrayIcon::isSystemTrayAvailable ())
363     {
364         minimizeToTray 	-> setVisible(true);
365         restoreFromTray	-> setVisible(false);
366     }
367     isMinimizedToTray = false;
368     this -> showNormal();
369 }
370 // LBtrayActivated============================================================================================================
371 // LB tray icon activated SLOT
LBtrayActivated(QSystemTrayIcon::ActivationReason reason)372 void luckyBackupWindow::LBtrayActivated(QSystemTrayIcon::ActivationReason reason)
373 {
374     switch (reason)
375     {
376         case QSystemTrayIcon::Context:
377             break;
378         case QSystemTrayIcon::Trigger:
379             if (isMinimizedToTray == true)
380                 restoreTray();
381             else
382                 minimizeTray();
383             break;
384         default:
385             ;
386     }
387 
388     if (isMinimizedToTray == true)
389     {
390         minimizeToTray 	-> setVisible(false);
391         restoreFromTray	-> setVisible(true);
392     }
393     else
394     {
395         minimizeToTray 	-> setVisible(true);
396         restoreFromTray	-> setVisible(false);
397     }
398 }
399 //executes pre-task commands ===============================================================================================
executeBeforeTask()400 void luckyBackupWindow::executeBeforeTask()
401 {
402     if (ABORTpressed)		//better safe than sorry ;)
403         return;
404 
405     repeatOnFailMax = Operation[currentOperation] -> GetRepeatOnFail();    // This holds the number of re-runs to try if the task fails for a reason
406 
407     // logfile & older snapshots actions if real run is performed
408     if ( (!DryRun) && (currentBefore == 0) && (repeatOnFailTry == 0) )
409     {
410         int maxSnaps = Operation[currentOperation] -> GetKeepSnapshots();	// this is the number of snapshots to keep
411         if (maxSnaps < 1)
412             maxSnaps = 1;
413         int currentSnaps = Operation[currentOperation] -> GetSnapshotsListSize();	// this is the current number of snapshots
414         if (currentSnaps < 1)
415             currentSnaps = 1;
416 
417         // first remove the older logfiles and snapshots if max keep snapshots is reached
418 
419         bool RemoteDestUsed = (Operation[currentOperation] -> GetRemoteDestination()) && (Operation[currentOperation] -> GetRemote());
420 
421         // SNAPSHOTS REMOVAL - This is outside the (currentSnaps >= maxSnaps) condition, because it will eventually cause the snapshots directory to get created so that the profile will be backed up without problems later on
422         // First calculate the folder where snapshots go (tempDestination)
423         QStringList tempArguments = Operation[currentOperation] -> GetArgs();
424         QString tempSource = tempArguments[tempArguments.size()-2];
425         QString tempDestination     = tempArguments[tempArguments.size()-1];
426         QString tempDestinationOrig = tempArguments[tempArguments.size()-1];
427         QString tempDestinationOrig2;
428         QString sourceLast = tempSource;
429         if (!tempSource.endsWith(SLASH))    // this means task is of type "backup dir by name"
430         {
431             sourceLast = calculateLastPath(sourceLast); // This is the lowest dir of the source
432 
433             tempSource.append(SLASH);
434             if (WINrunning && RemoteDestUsed)
435                 tempDestination.append(sourceLast + XnixSLASH);
436             else
437                 tempDestination.append(sourceLast + SLASH);
438         }
439         tempDestinationOrig2=tempDestination;
440         if (RemoteDestUsed && WINrunning)
441             tempDestination.append (snapDefaultDir.replace(SLASH,XnixSLASH));
442         else
443             tempDestination.append (snapDefaultDir);
444 
445         QStringList remoteArgs; remoteArgs.clear();
446         //all remote arguments exactly as used at normal backup
447         if (RemoteDestUsed)
448         {
449             remoteArgs.append("--protect-args");
450             //if ( Operation[currentOperation] -> GetRemotePassword() != "")
451             if ( Operation[currentOperation]-> GetRemoteModule() && Operation[currentOperation] -> GetRemotePassword() != "")
452                 remoteArgs.append("--password-file=" + ( Operation[currentOperation] -> GetRemotePassword()) );
453             if ( Operation[currentOperation] -> GetRemoteSSH())
454             {
455                 QString sshOptions=(Operation[currentOperation] -> GetRemoteSSHOptions()).replace("\"","\\\"")+" -o \"StrictHostKeyChecking no\"  -o \"PasswordAuthentication no\" ";
456                 if (WINrunning)
457                 {
458                     if ( Operation[currentOperation] -> GetRemoteSSHPassword() != "")
459                         if ( Operation[currentOperation] -> GetRemoteSSHPort() != 0)
460                         remoteArgs.append("-e \""+Operation[currentOperation] -> GetSshCommand()+"\" "+sshOptions+" -i \"" +  Operation[currentOperation] -> GetRemoteSSHPassword() +"\" -p " +
461                                         countStr.setNum( Operation[currentOperation] -> GetRemoteSSHPort()) );
462                         else
463                         remoteArgs.append("-e \""+Operation[currentOperation] -> GetSshCommand()+"\" "+sshOptions+" -i \"" +  Operation[currentOperation] -> GetRemoteSSHPassword()+"\"");
464                     else
465                         if ( Operation[currentOperation] -> GetRemoteSSHPort() != 0)
466                         remoteArgs.append("-e \""+Operation[currentOperation] -> GetSshCommand()+"\" "+sshOptions+" -p " + countStr.setNum( Operation[currentOperation] -> GetRemoteSSHPort()) );
467                         else
468                             remoteArgs.append("-e \""+Operation[currentOperation] -> GetSshCommand()+"\" "+sshOptions+"");
469                 }
470                 else
471                 {
472                     if ( Operation[currentOperation] -> GetRemoteSSHPassword() != "")
473                         if ( Operation[currentOperation] -> GetRemoteSSHPort() != 0)
474                             remoteArgs.append("-e "+sshCommandPath+" -i " +  Operation[currentOperation] -> GetRemoteSSHPassword() +" -p " +
475                                         countStr.setNum( Operation[currentOperation] -> GetRemoteSSHPort()) );
476                         else
477                             remoteArgs.append("-e "+sshCommandPath+" -i " +  Operation[currentOperation] -> GetRemoteSSHPassword());
478                     else
479                         if ( Operation[currentOperation] -> GetRemoteSSHPort() != 0)
480                             remoteArgs.append("-e "+sshCommandPath+" -p " + countStr.setNum( Operation[currentOperation] -> GetRemoteSSHPort()) );
481                         else
482                             remoteArgs.append("-e "+sshCommandPath);
483                 }
484             }
485         }
486 
487 
488         if (currentSnaps >= maxSnaps)
489         {
490             outputInsert = "\n<font color=magenta>" +
491                 tr("Removing old snapshots and logfiles of task","info message displayed during ...data removal\nFull phrase: Removing old snapshots and logfiles of task: <TASKNAME>") +
492                 ": <b>" + Operation[currentOperation] -> GetName() +
493                 "</b></font><br>=====================================<br>";
494             ui.rsyncOutput->append(outputInsert);
495 
496             // **************Remove actual backup data ***************************************************
497 
498             // increase the remove limit to include the source.size() if "backup dir by name" is used
499             if (Operation[currentOperation] -> GetTypeDirName())
500                 removeCharLimit = 4 + sourceLast.size()+1;
501             else
502                 removeCharLimit = 4;
503 
504             //we will delete the snapshots directory by using an rsync command with an empty source:
505             QProcess *rmProcess;
506             rmProcess  = new QProcess(this);
507             QStringList rmArgs;
508             rmArgs << "--progress" << "-r" << "--delete-after";
509             int snapToKeep = currentSnaps-maxSnaps + 1;
510             while ( snapToKeep < currentSnaps )
511             {
512                 if (WINrunning && RemoteDestUsed)
513                     rmArgs.append("--filter=protect " + Operation[currentOperation] -> GetSnapshotsListItem(snapToKeep) + XnixSLASH);
514                 else
515                     rmArgs.append("--filter=protect " + Operation[currentOperation] -> GetSnapshotsListItem(snapToKeep) + SLASH);
516                 snapToKeep++;
517             }
518             // protect the backup profile dir too
519             if (WINrunning && RemoteDestUsed)
520                 rmArgs.append("--filter=protect " + profileName + ".profile" + XnixSLASH);
521             else
522                 rmArgs.append("--filter=protect " + profileName + ".profile" + SLASH);
523 
524             //also add all remote arguments exactly as used at normal backup
525             if (RemoteDestUsed)
526                 //rmArgs.append(remoteArgs);  // use operator << instead of append to maintain compatiiblity with debian 5
527                 rmArgs << remoteArgs;
528 
529             rmArgs.append(snapEmptyDir);
530             rmArgs.append(tempDestination);
531 
532             if (WINrunning)
533             {
534 
535                 //bool createWinRsyncCommand(tempDirPath,QFile command1,QFile command2,bool vss,QString rsyncArgs,QString source,QString dest);
536                 QString command2=createWinRsyncCommand(tempDirPath,false,rmArgs);
537                 ui.rsyncOutput->append("\n"+command2);
538                 if (command2=="")
539                     cout << "\nfailed to create bat file in rmProccess";
540                 else
541                     rmProcess -> start (command2);
542             }
543             else
544                 rmProcess -> start (command,rmArgs);
545             rmProcess -> waitForFinished();
546 
547             if ((rmProcess -> exitCode()) == 0)
548                 ui.rsyncOutput->append("\n" + tr("Removed all older snapshots data"));// +" " + tempDestination + Operation[currentOperation] -> GetSnapshotsListItem(0) + SLASH);
549             else
550                 ui.rsyncOutput->append("\n" + tr("failed to remove all older snapshots data"));// +" " + tempDestination + Operation[currentOperation] -> GetSnapshotsListItem(0) + SLASH);
551 
552             //******************************************************
553 
554             count = 0;
555             while ( count < (currentSnaps -maxSnaps + 1 ) )
556             {
557                 //remove the changes file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
558                 snapchangesfilename = snapChangesDir + profileName + "-" + Operation[currentOperation] -> GetName() + "-" +
559                             (Operation[currentOperation] -> GetSnapshotsListItem(0)) + ".changes.log";
560                 snapfile.setFileName(snapchangesfilename);
561                 if (snapfile.exists())
562                 {
563                     if (snapfile.remove())	// this is the old snapshot changes file
564                         ui.rsyncOutput->append("\n" + tr("Removing") +" " + snapchangesfilename);
565                     else
566                         ui.rsyncOutput->append("\n" + tr("failed to remove") +" " + snapchangesfilename);
567                 }
568 
569                 //remove the oldest logfile ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
570                 logfilename = logDir + profileName + "-" + Operation[currentOperation] -> GetName() + "-" +
571                             (Operation[currentOperation] -> GetSnapshotsListItem(0)) + ".log";
572                 logfile.setFileName(logfilename); // this is the old logfile
573                 if (logfile.exists())
574                 {
575                     if (logfile.remove())
576                         ui.rsyncOutput->append("\n" + tr("Removing") +" " + logfilename);
577                     else
578                         ui.rsyncOutput->append("\n" + tr("failed to remove") +" " + logfilename);
579                 }
580 
581                 //remove the oldest snapshot (0) from the list
582                 Operation[currentOperation] -> RemoveSnapshotsListItem (0);
583                 count++;
584             }
585         }
586         else        // this is just to create the .snapDefaultDir if it does not to exist so as to copy profile data later...
587         {
588             //we will create the snapshots default directory by using an rsync command with an empty source without --delete option
589             QProcess *mkdirProcess;
590             mkdirProcess  = new QProcess(this);
591             QStringList mkdirArgs;      mkdirArgs.clear();
592             //no needed any more
593             //if ( (WINrunning) && (RemoteDestUsed) )
594             //    mkdirArgs << "--mkdir";
595             //else
596             mkdirArgs << "--progress" << "-r";
597 
598             //add all remote arguments exactly as used at normal backup
599             if (RemoteDestUsed)
600                 //mkdirArgs.append(remoteArgs);   // use operator << instead of append to maintain compatiiblity with debian 5
601                 mkdirArgs << remoteArgs;
602 
603             mkdirArgs.append(snapEmptyDir);
604 
605             //rsync throws error if directory is not yet created, ..so create it first
606 
607             // Add the destination folder
608             mkdirArgs.append(tempDestinationOrig);  // this is actually an empty argument
609             if (WINrunning)
610             {
611                 //bool createWinRsyncCommand(tempDirPath,QFile command1,QFile command2,bool vss,QString rsyncArgs,QString source,QString dest);
612                 QString command2=createWinRsyncCommand(tempDirPath,false,mkdirArgs);
613                 ui.rsyncOutput->append("\n"+command2);
614                 if (command2=="")
615                     cout << "\nfailed to create bat file in mkdirProccess";
616                 else
617                     mkdirProcess -> start (command2);
618             }
619             else
620                 mkdirProcess -> start (command,mkdirArgs);
621 
622             mkdirProcess -> waitForFinished();
623             if ((mkdirProcess -> exitCode()) == 0)
624                 ui.rsyncOutput->append("\n!!");
625             else
626                 ui.rsyncOutput->append("\n!");
627             mkdirArgs.removeLast(); //remove the tempDestinationOrig argument
628 
629             // Add the destination folder [ + sourceLast +SLASH ]
630             mkdirArgs.append(tempDestinationOrig2);  // this is actually an empty argument
631             if (WINrunning)
632             {
633                 //bool createWinRsyncCommand(tempDirPath,QFile command1,QFile command2,bool vss,QString rsyncArgs,QString source,QString dest);
634                     QString command2=createWinRsyncCommand(tempDirPath,false,mkdirArgs);
635                     ui.rsyncOutput->append("\n"+command2);
636                 if (command2=="")
637                     cout << "\nfailed to create bat file in mkdirProccess";
638                 else
639                     mkdirProcess -> start (command2);
640             }
641             else
642                 mkdirProcess -> start (command,mkdirArgs);
643 
644             mkdirProcess -> waitForFinished();
645             if ((mkdirProcess -> exitCode()) == 0)
646                 ui.rsyncOutput->append("\n!!");
647             else
648                 ui.rsyncOutput->append("\n!");
649 
650             // Add the destination folder [ + sourceLast +SLASH ] + snapDefaultDir
651             mkdirArgs.removeLast(); //remove the tempDestinationOrig2 argument
652             mkdirArgs.append(tempDestination);
653             if (WINrunning)
654             {
655                 //bool createWinRsyncCommand(tempDirPath,QFile command1,QFile command2,bool vss,QString rsyncArgs,QString source,QString dest);
656                     QString command2=createWinRsyncCommand(tempDirPath,false,mkdirArgs);
657                     ui.rsyncOutput->append("\n"+command2);
658                 if (command2=="")
659                     cout << "\nfailed to create bat file in mkdirProccess";
660                 else
661                     mkdirProcess -> start (command2);
662             }
663             else
664                 mkdirProcess -> start (command,mkdirArgs);
665 
666             mkdirProcess -> waitForFinished();
667             if ((mkdirProcess -> exitCode()) == 0)
668                 ui.rsyncOutput->append("\n!!");
669             else
670                 ui.rsyncOutput->append("\n!");
671         }
672 
673         //set the current date and time as the operation's last execution date-time
674         Operation[currentOperation] -> SetLastExecutionTime (QDateTime::currentDateTime());
675 
676         // add a new snapshot with the last execution date-time
677         Operation[currentOperation] ->
678                     AddSnapshotsListItem ((Operation[currentOperation] -> GetLastExecutionTime()).toString("yyyyMMddhhmmss"));
679         currentSnaps = Operation[currentOperation] -> GetSnapshotsListSize();	// update the current number of snapshots
680 
681         // set a new changes file. This has a tag of the previous snapshot will include changes made to this snapshot
682         snapchangesfilename = snapChangesDir + profileName + "-" + Operation[currentOperation] -> GetName() + "-" +
683                     (Operation[currentOperation] -> GetSnapshotsListItem(currentSnaps - 1)) + ".changes.log";
684         snapfile.setFileName(snapchangesfilename);
685 
686         // set a new logfile
687         logfilename = logDir + profileName + "-" + Operation[currentOperation] -> GetName() + "-" +
688             ( Operation[currentOperation] -> GetSnapshotsListItem(currentSnaps - 1) ) + ".log";
689         logfile.setFileName(logfilename); // this is the logfile
690         if (logfile.open(QIODevice::WriteOnly | QIODevice::Text))	//create a new log file
691             writeToLog = true;				//& if it's ok set this to true
692         else
693             writeToLog = false;
694     }
695 
696     errorCount = 0;		// task starts, so set this to 0 no matter dry or real run
697 
698     // execute commands before task -----------------------------------------------------------------------------------------------------
699     // if there are no (more) pre-task commands to be executed ||
700                         // the previous pre-task command exited with an error(all repeatOnFail runs) and the stop checkbox is checked
701     if ( (Operation[currentOperation] -> GetExecuteBeforeListSize() == currentBefore) || (StopTaskExecution) )
702     {
703         currentBefore = 0;
704         ExecuteBefore=false;
705         repeatOnFailTry = 0;
706 
707         executeRsync();
708     }
709     else
710     {
711         ExecuteBefore=true;
712         ExecuteBeforeExitedError = false;
713         StopTaskExecution = false;
714         ProcReportedError = false;      // This might change as soon as syncprocess will start ()
715         outputInsert = logFileUpdate("pre-starting","",currentBefore);
716 
717         ui.rsyncOutput->append(outputInsert);
718 
719         syncProcess -> start (Operation[currentOperation] -> GetExecuteBeforeListItem(currentBefore));
720         syncProcess -> waitForStarted(5000);
721 
722         // The reason for the below jump is that when a process reports an error it does not emit finished() signals neither std output/errors
723         if (ProcReportedError)
724             procFinished();
725     }
726 }
727 
728 //executes post-task commands ===============================================================================================
executeAfterTask()729 void luckyBackupWindow::executeAfterTask()
730 {
731     if (ABORTpressed)		//better safe than sorry :)
732         return;
733 
734     // execute commands after task -----------------------------------------------------------------------------------------------------
735     //if there are no (more) post-task commands to be executed or we are here because a pre/post-task command exited with an error or destination could not be created
736     if ( (Operation[currentOperation] -> GetExecuteAfterListSize() == currentAfter)|| (DestCreateFail) || (StopTaskExecution) )
737     {
738         if (!DryRun)
739         {
740             logfile.close();	//close the logfile first (will create a new one for the next task)
741             Operation[currentOperation] -> SetLastExecutionErrors (errorCount);	// update the last execution errors
742         }
743 
744         currentAfter = 0;
745         ExecuteAfter=false;
746 
747         errorCount = 0;		// reset the current task error count
748 
749         currentOperation++;
750         //increase currentOperation until next operation to be executed or end of operations
751         while ( (currentOperation < TotalOperations) && (!Operation[currentOperation] -> GetPerform()) )
752             currentOperation++;
753         if (currentOperation < TotalOperations)
754         {
755             ExecuteBeforeExitedError = false;
756             StopTaskExecution = false;
757             executeBeforeTask();
758         }
759         else
760             setNowDoing();
761     }
762     else
763     {
764         ExecuteAfter=true;
765         ProcReportedError = false;  // This might change as soon as syncprocess will start ()
766         outputInsert = logFileUpdate("post-starting", "", currentAfter);
767         ui.rsyncOutput->append(outputInsert);
768 
769         syncProcess -> start (Operation[currentOperation] -> GetExecuteAfterListItem(currentAfter));
770         syncProcess -> waitForStarted(5000);
771 
772         // The reason for the below jump is that when a process reports an error it does not emit finished() signals neither std output/errors
773         if (ProcReportedError)
774             procFinished();
775     }
776 }
777 
778 //executes rsync  ===============================================================================================
executeRsync()779 void luckyBackupWindow::executeRsync()
780 {
781     if (ABORTpressed)		//better safe than sorry :)
782         return;
783 
784     if (StopTaskExecution)	// if a pre-task command exited with an error do not do anything
785     {
786         procFinished();
787         return;
788     }
789 
790     sync = Operation[currentOperation] -> GetTypeSync();		//set sync variable according to checkstate of radiobutton operation type
791     rsyncArguments = AppendArguments(Operation[currentOperation]);	//set rsync arguments
792 
793     bool RemoteDestUsed = (Operation[currentOperation] -> GetRemoteDestination()) && (Operation[currentOperation] -> GetRemote()); // Is remote dest used ?
794 
795     if (DryRun)
796         rsyncArguments.insert(rsyncArguments.size()-2,"--dry-run");
797 
798     dirA = rsyncArguments[rsyncArguments.size()-2];
799     dirB = rsyncArguments[rsyncArguments.size()-1];
800 
801     if (sync)	//execute rsync for syncing 2 dirs
802     {
803         if (syncAB)	//execute rsync A -> B
804             syncAB = false;
805 
806         else		//execute rsync B -> A
807         {
808             rsyncArguments.removeLast();
809             rsyncArguments.removeLast();
810             rsyncArguments.append(dirB);	// set SyncDirA & SyncDirB as Arguments
811             rsyncArguments.append(dirA);
812             syncAB = true;
813         }
814     }
815 
816     //display a couple of lines to inticate start of task
817     if ((sync) && (!syncAB))
818         outputInsert = logFileUpdate("rsync-starting-syncAB", "", 0);
819 
820     if ((sync) && (syncAB))
821         outputInsert = logFileUpdate("rsync-starting-syncBA", "", 0);
822 
823     DestCreateFail = false;	 // This will become true if destination does not exist and cannot be created
824 
825     if (!sync)
826     {
827         outputInsert = logFileUpdate("rsync-starting-backup", "", 0);
828 
829         // Create the destination if it does not exist
830         QDir destCreate (dirB);
831         if ( (!destCreate.exists()) && (!RemoteDestUsed) ) // local use ONLY
832         {
833             if (destCreate.mkpath(dirB))
834                 outputInsert.append(logFileUpdate("rsync-standard", "<br>" + tr("Successfully created destination directory"), 0));
835             else
836             {
837                 outputInsert.append(logFileUpdate("rsync-error", "<br>" +tr("Failed to create destination directory"), 0));
838                 ui.rsyncOutput->append(outputInsert);
839                 DestCreateFail = true;
840                 errorsFound++;
841                 errorCount++;
842                 procFinished();	// Do not proceed any further
843                 return;
844             }
845         }
846     }
847 
848     ui.rsyncOutput->append(outputInsert);
849     //set the progressbar to 0
850     ui.OperationProgress -> setRange(0,100);
851     ui.OperationProgress -> setValue (0);
852 
853     ProcReportedError = false;      // This might change as soon as syncprocess will start ()
854 
855     if (WINrunning)
856     {
857         //bool createWinRsyncCommand(tempDirPath,QFile command1,QFile command2,bool vss,QString rsyncArgs,QString source,QString dest);
858         QString command2=createWinRsyncCommand(tempDirPath,Operation[currentOperation]->GetOptionsVss(),rsyncArguments);
859         ui.rsyncOutput->append("\n"+command2);
860         if (command2=="")
861             cout << "\nfailed to create bat file for vss";
862         else
863             syncProcess -> start (command2);
864     }
865     else
866         syncProcess -> start (command,rsyncArguments);	// execute rsync command with rsyncArguments
867 
868     // The reason for the below jump is that when a process reports an error it does not emit finished() signals neither std output/errors
869     if (ProcReportedError)
870         procFinished();
871 }
872 
873 //when rsyncProcess emits finished signal execute another RsyncProcess if any left====================================================================
procFinished()874 void luckyBackupWindow::procFinished()
875 {
876     if (ABORTpressed) //this is to prevent segmentation fault when abort button pressed
877         return;
878 
879     /* Disable VSS until
880     if (doVss==1) //reads all log file in vss before finished
881     {
882         doVss=2;
883         return;
884     }*/
885 
886     bool RemoteDestUsed = (Operation[currentOperation] -> GetRemoteDestination()) && (Operation[currentOperation] -> GetRemote()); // Is remote dest used ?
887     if (ExecuteBefore)		// if the pre-task execution command (process) finished
888     {
889         outputInsert = logFileUpdate("pre-finished", "", currentBefore);
890         ui.rsyncOutput->append(outputInsert);
891 
892         // set this for a successful run of a command. They might change if there was an error
893         ExecuteBeforeExitedError = false;
894         StopTaskExecution = false;
895 
896         // if the pre-task command exited with an error
897         if ((syncProcess -> exitCode() != 0) || (ProcReportedError))
898         {
899             ExecuteBeforeExitedError = true;
900 
901             if (repeatOnFailTry == repeatOnFailMax) // if the last run of the command just ended
902             {
903                 if (Operation[currentOperation] -> GetExecuteBeforeListItemState(currentBefore) == true)
904                     StopTaskExecution = true;
905                 repeatOnFailTry = 0;    // reset the runs counter for a specific command
906                 currentBefore++;    //go to the next pre-task execution command
907             }
908             else
909             {
910                 outputInsert = logFileUpdate("repeat-on-fail", "", currentBefore);
911                 ui.rsyncOutput->append(outputInsert);
912                 repeatOnFailTry++;  // re-run this command, do not proceed to the next one
913             }
914         }
915         else        // if the process was successful, go to the next one
916         {
917             currentBefore++;    //go to the next pre-task execution command
918             repeatOnFailTry = 0;    // it should already be 0, but ...you never know
919         }
920 
921         executeBeforeTask();    //and executeBeforeTask again
922 
923         return;
924     }
925 
926     if (ExecuteAfter)		// if the post-task execution command (process) finished
927     {
928         outputInsert = logFileUpdate("post-finished", "", currentAfter);
929         ui.rsyncOutput->append(outputInsert);
930 
931         // set this for a successful run of a command. They might change if there was an error
932         StopTaskExecution = false;
933 
934         // if the post-task command exited with an error
935         if ((syncProcess -> exitCode() != 0) || (ProcReportedError))
936         {
937             if (repeatOnFailTry == repeatOnFailMax) // if the last run of the command just ended
938             {
939                 if (Operation[currentOperation] -> GetExecuteAfterListItemState(currentAfter) == true)
940                     StopTaskExecution = true;
941                 repeatOnFailTry = 0;     //rest the counter for a specific command
942                 currentAfter++;    //go to the next pre-task execution command
943             }
944             else
945             {
946                 outputInsert = logFileUpdate("repeat-on-fail", "", currentAfter);
947                 ui.rsyncOutput->append(outputInsert);
948                 repeatOnFailTry++;  // re-run this command, do not proceed to the next one
949             }
950         }
951         else        // if the process was successful, go to the next one
952             currentAfter++;    //go to the next post-task execution command
953 
954         executeAfterTask();		//and executeAfterTask again
955         return;
956     }
957 
958     //display a couple of lines to indicate end of task
959     if (StopTaskExecution)
960         outputInsert = logFileUpdate("pre-task-exited-with-error", "", 0);
961     else
962     {
963         if (!DryRun)
964         {
965             // strip unused lines from the snapshot changes file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
966             QString filesAdded = "", snapLine = "";
967             if (snapfile.open(QIODevice::ReadOnly | QIODevice::Text))
968             {
969                 ui.rsyncOutput->append(".");
970                 QTextStream in(&snapfile);
971                 while (!in.atEnd())
972                 {
973                     snapLine = in.readLine();
974                     if (snapLine.contains("+++++++", Qt::CaseInsensitive))
975                     {
976                         snapLine = snapLine.right(snapLine.size()-snapLine.lastIndexOf("[LB]")-removeCharLimit) + "\n";
977                         filesAdded.append(snapLine);
978                     }
979                 }
980                 snapfile.close();
981                 filesAdded.remove("[LB]", Qt::CaseSensitive);        // just to make sure cause sometimes there are [LB]'s left
982             }
983             if (snapfile.open(QIODevice::WriteOnly))
984             {
985                 ui.rsyncOutput->append(".");
986                 QTextStream out(&snapfile);
987                 out << filesAdded;
988                 snapfile.close();
989             }
990         }
991 
992         if ((!sync) || ((sync) && (syncAB)) )
993         {
994             // Backup profile + logs + snaps to destination ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
995 
996             QString exportProfileDir = "";  QString sourceLast=Operation[currentOperation] -> GetSource();
997             //calculate the last folder of source
998             if (!sourceLast.endsWith(SLASH))    // this means task is of type "backup dir by name"
999                 sourceLast = calculateLastPath(sourceLast); // This is the lowest dir of the source
1000             else
1001                 sourceLast = "";
1002 
1003             if (!rsyncArguments.isEmpty())      //rsyncArguments is calculated at executeRsync()
1004             {
1005                 if (WINrunning && RemoteDestUsed)
1006                     exportProfileDir = rsyncArguments.last() + sourceLast + XnixSLASH + snapDefaultDir + profileName + ".profile" + XnixSLASH;
1007                 else
1008                     exportProfileDir = rsyncArguments.last() + sourceLast + SLASH + snapDefaultDir + profileName + ".profile" + SLASH;
1009             }
1010 
1011             //QMessageBox::information(this, "LB",exportProfileDir);    //TESTING
1012 
1013             outputInsert = "";
1014 
1015             // If this is a backup task && not a dryrun, backup profile data to destination
1016             if ( (!sync) && (!DryRun) && (!rsyncArguments.isEmpty()) )
1017             {
1018                 // Create the export path if it does not exist
1019                 // local use
1020                 QDir exportPathCreate (exportProfileDir);
1021                 if ( (!exportPathCreate.exists()) && (!RemoteDestUsed) )
1022                     exportPathCreate.mkpath(exportProfileDir);
1023 
1024                 if (exportFullProfile(exportProfileDir,"ExportOnlyTask"))
1025                     outputInsert = logFileUpdate("backup-profile", " -> Ok", currentAfter);
1026                 else
1027                     outputInsert = logFileUpdate("backup-profile", " -> Fail", currentAfter);
1028 
1029             }
1030 
1031             outputInsert.append(logFileUpdate("rsync-finished", "", 0));
1032         }
1033         else
1034             outputInsert = logFileUpdate("rsync-finished-sync1", "", 0);
1035     }
1036 
1037     ui.rsyncOutput->append(outputInsert);
1038 
1039     /* If there is an error repeat the rsync command. Errors:
1040                                 5 - Error starting client-server protocol
1041                                 12 - Error in rsync protocol data stream
1042                                 23 - Partial transfer due to error -> I took this out
1043                                 30 - Timeout in data send/receive
1044                                 35 - Timeout waiting for daemon connection
1045                                 255 - unexplained error */
1046     if ((repeatOnFailTry < repeatOnFailMax) &&
1047           ( (syncProcess -> exitCode()==30) || (syncProcess -> exitCode()==35) || (syncProcess -> exitCode()==255) ||
1048             (syncProcess -> exitCode()==12) || (syncProcess -> exitCode()==5) || (ProcReportedError) ))
1049     {
1050         outputInsert = logFileUpdate("repeat-on-fail", "", 0);
1051         ui.rsyncOutput->append(outputInsert);
1052         repeatOnFailTry++;
1053         executeRsync();
1054         return;
1055     }
1056     else
1057         repeatOnFailTry = 0;    // reset the runs counter
1058 
1059     if ( (sync) && (!syncAB) )	//sync A->B is finished. Do the opposite now before proceeding to next included operation or post-task commands
1060         executeRsync();
1061     else
1062         executeAfterTask();	//execute post-task commands (if any)
1063 }
1064 
1065 // A process reported an error (eg failed to start or crashed etc)
procError()1066 void luckyBackupWindow::procError()
1067 {
1068     ProcReportedError = true;
1069     QProcess::ProcessError commandError = syncProcess -> error();
1070     QString errorText = tr("Unknown error");
1071     switch (commandError)
1072     {
1073         case QProcess::FailedToStart: errorText = tr("Failed to start","this refers to a process");
1074         break;
1075         case QProcess::Crashed      : errorText = tr("Crashed","this refers to a process");
1076         break;
1077         case QProcess::Timedout     : errorText = tr("Timed out","this refers to a process");
1078         break;
1079         case QProcess::WriteError   : errorText = tr("Write error","this refers to a process");
1080         break;
1081         case QProcess::ReadError    : errorText = tr("Read error","this refers to a process");
1082         break;
1083         case QProcess::UnknownError : errorText = tr("Unknown error","this refers to a process");
1084         break;
1085     }
1086 
1087     errorsFound++;
1088     errorCount++;
1089     ui.rsyncOutput->append(logFileUpdate("process-reported-error", errorText, 0));
1090     setNowDoing();
1091 }
1092 //update dialog with new data (text - progressbar) - also update logfile =======================================================================
appendRsyncOutput()1093 void luckyBackupWindow::appendRsyncOutput()
1094 {
1095     if (ABORTpressed)		//better safe than sorry :)
1096         return;
1097 
1098     setNowDoing ();		//update Nowdoing textBrowser
1099 
1100     //update textBrowser ------------------------------------------------------------------------------------------------------
1101     QTextCodec *codec = QTextCodec::codecForName("UTF-8");
1102     outputString = codec->toUnicode(syncProcess -> readAllStandardOutput());
1103     outputError = codec->toUnicode(syncProcess -> readAllStandardError());
1104 
1105     //if (!showOnlyErrors)
1106     if (!ui.checkBox_onlyShowErrors -> isChecked())
1107         ui.rsyncOutput->append(outputString);
1108 
1109 
1110     logFileUpdate("rsync-standard", outputString, 0);
1111 
1112     if (outputError !="")
1113     {
1114         errorsFound++;
1115         errorCount++;
1116         ui.rsyncOutput->append(logFileUpdate("rsync-error", outputError, 0));
1117     }
1118 
1119     //update progressbar--------------------------------------------------------------------------------------------------------
1120     bool ok;
1121     if ( (outputString.contains("to-check")) || (outputString.contains("to-chk")) )	//we will calculate how many files have been proccessed so far
1122     {
1123         //DoneToTotal_Ref & DoneToTotal_String hold a e.g. "17/84"
1124         QStringRef DoneToTotal_Ref;
1125         if (outputString.contains("to-check"))  // if rsync uses "to-check="
1126             DoneToTotal_Ref = outputString.midRef(outputString.indexOf("check=")+6,outputString.indexOf(")")-outputString.indexOf("check=")-6);
1127         else                                    // if rsync uses "to-chk="
1128             DoneToTotal_Ref = outputString.midRef(outputString.indexOf("o-chk=")+6,outputString.indexOf(")")-outputString.indexOf("o-chk=")-6);
1129         QString DoneToTotal_String = DoneToTotal_Ref.toString();
1130 
1131         //Total no files
1132         QStringRef ref_temp = DoneToTotal_String.rightRef(DoneToTotal_String.size() - DoneToTotal_String.indexOf(SLASH) -1);
1133         QString string_temp = ref_temp.toString();
1134         progress_total = string_temp.toInt(&ok,10);
1135         ui.OperationProgress -> setRange(0,progress_total);	//set the range of the progressbar to the no of files to consider
1136 
1137         //No of files processed so far
1138         ref_temp = DoneToTotal_String.leftRef(DoneToTotal_String.indexOf(SLASH));
1139         string_temp = ref_temp.toString();
1140         progress_done = string_temp.toInt(&ok,10);
1141         progress_done = progress_total - progress_done;
1142         ui.OperationProgress -> setValue (progress_done);	//set the current progressbar value
1143     }
1144     if (outputString.contains("speedup is"))	//the process has finished, so if we're back fill it to 100%
1145     {
1146         ui.OperationProgress -> setRange(0,100);
1147         ui.OperationProgress -> setValue (100);
1148     }
1149     if (outputString.contains("building file list"))
1150     {
1151         calculating = true;
1152         transferring = false;
1153         deleting = false;
1154     }
1155     if (outputString.contains("files to consider"))
1156     {
1157         calculating = false;
1158         transferring = true;
1159         deleting = false;
1160     }
1161     if (outputString.contains("deleting"))
1162     {
1163         calculating = false;
1164         transferring = false;
1165         deleting = true;
1166     }
1167 
1168     // Extraxt some data from the "stats" lines at the end of rsync
1169     if ( (outputString.contains("Number of files transferred")) || (outputString.contains("Total bytes sent:")) || (outputString.contains("Number of regular files")) )
1170     {
1171         // split the outputString to lines
1172         QString FilesTransferedString="",BytesTransferedString="";
1173         QStringList lines = outputString.split( "\n", QString::SkipEmptyParts );
1174         foreach( QString line, lines )
1175         {
1176             if (line.contains("Number of files transferred:"))
1177                 FilesTransferedString = line.remove("Number of files transferred: ");          // old rsync report;
1178             if (line.contains("Number of regular files transferred:"))
1179                 FilesTransferedString = line.remove("Number of regular files transferred: ");  // newer rsync report
1180             if (line.contains("Total transferred file size:"))
1181                 BytesTransferedString = line.remove("Total transferred file size: ");           // newer rsync report
1182             if (line.contains("Total bytes sent:"))
1183                 BytesTransferedString = line.remove("Total bytes sent: ");             // old rsync report
1184         }
1185 
1186         filesTransfered = filesTransfered + FilesTransferedString.toInt(&ok, 10);
1187 
1188         BytesTransferedString = convertBytes(BytesTransferedString,false);
1189         bytesTransfered = bytesTransfered + BytesTransferedString.toULongLong(&ok, 10);
1190     }
1191 }
1192 
1193 /*  disable vss until...
1194 void luckyBackupWindow::appendRsyncVssOutput()
1195 {
1196     appendRsyncVssOutput(vssReadSize);
1197 }
1198 
1199 //Read size lines, if size=-1 read all file
1200 void luckyBackupWindow::appendRsyncVssOutput(int size)
1201 {
1202     if (ABORTpressed)       //better safe than sorry :)
1203         return;
1204     if (doVss==0)
1205     return;
1206     setNowDoing ();     //update Nowdoing textBrowser
1207 
1208 
1209 
1210     QString s,se;
1211     if (pipeVssFile->open(QIODevice::ReadOnly | QIODevice::Text))
1212     {
1213         int i=size;
1214         QTextStream stream(pipeVssFile);
1215         stream.setCodec("UTF-8");
1216         stream.seek(vssPos);
1217         outputString="";
1218         s=stream.readLine();
1219         while(!s.isNull()&&i!=0){
1220             outputString=outputString+s+"\n";
1221             s=stream.readLine();
1222             //if (!showOnlyErrors)
1223             i--;
1224         };
1225         vssPos=stream.pos();
1226         pipeVssFile->close();
1227         if (outputString !="")
1228         {
1229             if (!ui.checkBox_onlyShowErrors -> isChecked())
1230                 ui.rsyncOutput->append(outputString);
1231             logFileUpdate("rsync-standard", outputString, 0);
1232             //update progressbar--------------------------------------------------------------------------------------------------------
1233             bool ok;
1234             if (outputString.contains("to-check"))  //we will calculate how many files have been proccessed so far
1235             {
1236                 //DoneToTotal_Ref & DoneToTotal_String hold a e.g. "17/84"
1237                 QStringRef DoneToTotal_Ref = outputString.midRef(outputString.indexOf("check=")+6,outputString.indexOf(")")-outputString.indexOf("check=")-6);
1238                 QString DoneToTotal_String = DoneToTotal_Ref.toString();
1239 
1240                 //Total no files
1241                 QStringRef ref_temp = DoneToTotal_String.rightRef(DoneToTotal_String.size() - DoneToTotal_String.indexOf(SLASH) -1);
1242                 QString string_temp = ref_temp.toString();
1243                 progress_total = string_temp.toInt(&ok,10);
1244                 ui.OperationProgress -> setRange(0,progress_total); //set the range of the progressbar to the no of files to consider
1245 
1246                 //No of files processed so far
1247                 ref_temp = DoneToTotal_String.leftRef(DoneToTotal_String.indexOf(SLASH));
1248                 string_temp = ref_temp.toString();
1249                 progress_done = string_temp.toInt(&ok,10);
1250                 progress_done = progress_total - progress_done;
1251                 ui.OperationProgress -> setValue (progress_done);   //set the current progressbar value
1252             }
1253             if (outputString.contains("speedup is"))    //the process has finished, so if we're back fill it to 100%
1254             {
1255                 ui.OperationProgress -> setRange(0,100);
1256                 ui.OperationProgress -> setValue (100);
1257             }
1258             if (outputString.contains("building file list"))
1259             {
1260                 calculating = true;
1261                 transferring = false;
1262                 deleting = false;
1263             }
1264             if (outputString.contains("files to consider"))
1265             {
1266                 calculating = false;
1267                 transferring = true;
1268                 deleting = false;
1269             }
1270             if (outputString.contains("deleting"))
1271             {
1272                 calculating = false;
1273                 transferring = false;
1274                 deleting = true;
1275             }
1276         }
1277     }
1278     if (pipeVssErrFile->open(QIODevice::ReadOnly | QIODevice::Text))
1279     {
1280             int i=size;
1281         QTextStream stream(pipeVssErrFile);
1282         stream.setCodec("UTF-8");
1283         stream.seek(vssErrPos);
1284         outputError="";
1285         se=stream.readLine();
1286         while(!se.isNull()&&i!=0){
1287             outputError=outputError+se+"\n";
1288             se=stream.readLine();
1289             i--;
1290         }
1291 
1292         vssErrPos=stream.pos();
1293         pipeVssErrFile->close();
1294         if (outputError !="")
1295         {
1296             errorsFound++;
1297             errorCount++;
1298             ui.rsyncOutput->append(logFileUpdate("rsync-error", outputError, 0));
1299         }
1300     }
1301     if (doVss == 2 && se.isNull() && s.isNull())
1302     {
1303         vssTimer->stop();
1304         doVss = 0;
1305         pipeVssFile->remove();
1306         pipeVssErrFile->remove();
1307         procFinished();
1308 
1309     }
1310 }
1311 */
1312 //updates Now Doing textBrowser ===============================================================================================================
setNowDoing()1313 void luckyBackupWindow::setNowDoing()
1314 {
1315     //calculate elapsed time since all operations start
1316     QTime DifTime(0,0,0,0);
1317     int elapsedMsec = StartTime.elapsed();
1318     DifTime = DifTime.addMSecs(elapsedMsec);
1319 
1320     if ( (currentOperation < TotalOperations) && (!ABORTpressed) )
1321     {
1322         if (ExecuteBefore)
1323             nowDoingText = 	"<p align=\"center\">"+tr("Elapsed time")+" : <b><font color=red>" + DifTime.toString("hh:mm:ss") +
1324                     "</font></b><br>"+tr("pre-task execution of command")+"	: <b>" +
1325                     Operation[currentOperation] -> GetExecuteBeforeListItem(currentBefore) + "</b>";
1326 
1327         if (ExecuteAfter)
1328             nowDoingText = 	"<p align=\"center\">"+tr("Elapsed time")+" : <b><font color=red>" + DifTime.toString("hh:mm:ss") +
1329                     "</font></b><br>"+tr("post-task execution of command")+"	: <b>" +
1330                     Operation[currentOperation] -> GetExecuteAfterListItem(currentAfter) + "</b>";
1331 
1332         if ( (sync) && (!ExecuteAfter) && (!ExecuteBefore) )	//if a sync operation is executed
1333         {
1334             nowDoingText = 	"<p align=\"center\">"+tr("Elapsed time")+" : <b><font color=red>" + DifTime.toString("hh:mm:ss") +
1335                     "</font></b><br>"+tr("Now performing task")+"	: <b>" + Operation[currentOperation] -> GetName() +
1336                     "</b>";
1337 
1338             if (DryRun)
1339                 nowDoingText.append(" <b><font color=magenta>(" + tr("simulation mode") + ")</font>");
1340 
1341             nowDoingText.append("</p>");
1342             nowDoingText.append(tr("Directory")+" A	: <b><font color=blue>" + dirA +
1343                     "</font></b><br>"+tr("Directory")+" B	: <b><font color=blue>" + dirB + "</font></b><br>");
1344 
1345             if (calculating)
1346                 nowDoingText.append(tr("calculating","info message displayed during ...calculations")+": " + outputString);
1347             if (transferring)
1348                 nowDoingText.append(tr("transferring files","info message displayed during ...file transfers")+": " + outputString);
1349             if (deleting)
1350                 nowDoingText.append(tr("deleting files","info message displayed during ...file deletions")+": " + outputString);
1351         }
1352 
1353         if ( (!sync) && (!ExecuteAfter) && (!ExecuteBefore) ) //if a backup operation is executed
1354         {
1355             nowDoingText = 	"<p align=\"center\">"+tr("Elapsed time")+" : <b><font color=red>" + DifTime.toString("hh:mm:ss") +
1356                     "</font></b><br>"+tr("Now performing task")+"	: <b>" + Operation[currentOperation] -> GetName() +
1357                     "</b>";
1358 
1359             if (DryRun)
1360                 nowDoingText.append(" <b><font color=magenta>(" + tr("simulation mode") + ")</font>");
1361 
1362             nowDoingText.append("</p>");
1363             nowDoingText.append(tr("Source")+"	: <b><font color=blue>" + dirA +
1364                     "</font></b><br>"+tr("Destination")+"	: <b><font color=blue>" + dirB + "</font></b><br>");
1365 
1366             if (calculating)
1367                 nowDoingText.append(tr("calculating")+": " + outputString);
1368             if (transferring)
1369                 nowDoingText.append(tr("transferring files")+": " + outputString);
1370             if (deleting)
1371                 nowDoingText.append(tr("deleting files")+": " + outputString);
1372         }
1373     }
1374 
1375     if ( (currentOperation == TotalOperations) && (!ABORTpressed))	//if all operations finished normally - not aborted
1376     {
1377         NOWexecuting = false;		//this is mainly used if the window close button (or alt+F4) is pressed
1378 
1379         if (filesTransfered == 0)
1380             bytesTransfered = 0;
1381 
1382         nowDoingText = 	"<p align=\"center\">"+tr("Elapsed time")+" : <b><font color=red>" + DifTime.toString("hh:mm:ss") +
1383                 "</font></b><br>" +
1384                 tr("Total files transferred") + " : <b>" + countStr.setNum(filesTransfered) + "</b> (" + convertBytes(QString::number(bytesTransfered),true) +")<br>"
1385                 "========================================="
1386                 "<b><br><font color=blue>"+tr("All tasks completed")+" </font></b>";
1387         trayMessage =	tr("All tasks completed");
1388         if (DryRun)
1389         {
1390             nowDoingText.append(" <b><font color=magenta>(" + tr("simulation mode") + ")</b></font>");
1391             trayMessage.append(" (" + tr("simulation mode") + ")");
1392         }
1393         if (errorsFound == 0)
1394         {
1395             nowDoingText.append("<br><font color=green>" + tr("No errors found") + "</font><br>");
1396             trayMessage.append("\n" + tr("No errors found"));
1397         }
1398         else
1399         {
1400             nowDoingText.append("<br><font color=green>" + tr("errors found") + ": " + countStr.setNum(errorsFound) +"</font><br>");
1401             trayMessage.append("\n" + tr("errors found"));
1402 
1403             // initialize jump to next error button
1404             firstScroll=true;
1405             errorCount = 0;
1406             ui.pushButton_nextError	-> setEnabled (true);
1407         }
1408         if (!DryRun)
1409             nowDoingText.append(tr("logfile(s) have been created under directory: ")+ logDir +"<br>");
1410         nowDoingText.append("=========================================</p>");
1411         ui.AbortButton -> setVisible (false);
1412         ui.DoneButton -> setVisible (true);
1413         ui.pushButton_minimizeToTray	-> setVisible (false);
1414 
1415         //update tray baloon
1416         if ( (QSystemTrayIcon::isSystemTrayAvailable ()) && (QSystemTrayIcon::supportsMessages ()) )
1417         {
1418             if (KDErunning)
1419             {
1420                 QProcess *dialogProcess;    dialogProcess = new QProcess(this);
1421                 QStringList dialogArgs;
1422                 dialogArgs << "--title" << appName + " - " + tr("execution of profile:") + " " + profileName + " " + tr("finished") << "--passivepopup" << trayMessage << "10";
1423                 dialogProcess -> start ("kdialog",dialogArgs);
1424             }
1425             else
1426                 LBtray -> showMessage (appName + " - " + tr("execution of profile:") + " " + profileName + " " + tr("finished") , trayMessage,
1427                                                     QSystemTrayIcon::Information,3000);
1428             actionAbort	-> setVisible(false);
1429         }
1430 
1431         finishUp();
1432 
1433         // bring the system down if the relevant button is pressed
1434         if (ui.pushButton_shutdown -> isChecked())
1435             shutDownSystem();
1436 
1437         if ( (silentMode) && (isMinimizedToTray == true) )		// if --silent is given as argument and the gui is not shown exit the app
1438         {
1439             //delay the app exit for 3 seconds
1440             QTime StartSleep(0,0,0,0);
1441             StartSleep.start();
1442             int elapsedSleepMsec = 0;
1443 
1444             while (elapsedSleepMsec < 3000)
1445                 elapsedSleepMsec = StartSleep.elapsed();
1446 
1447             exit(0);	//quit
1448         }
1449     }
1450 
1451     if (ABORTpressed)	//if operations were terminated by user
1452     {
1453         NOWexecuting = false;		//this is mainly used if the window close button (or alt+F4) is pressed
1454         nowDoingText = 	"<p align=\"center\">"+tr("Elapsed time")+" : <b><font color=red>" + DifTime.toString("hh:mm:ss") +
1455                 "</font></b><br>========================================="
1456                 "<br><b><font color=blue>"+tr("Execution of tasks were terminated violently by user")+"</font></b><br>";
1457         trayMessage = 	tr("Execution of tasks were terminated violently by user");
1458 
1459         if (!DryRun)
1460             nowDoingText.append(tr("logfile(s) have been created under directory: ")+ logDir +"<br>");
1461         nowDoingText.append("=========================================</p>");
1462         ui.AbortButton -> setVisible (false);
1463         ui.DoneButton -> setVisible (true);
1464         ui.pushButton_minimizeToTray	-> setVisible (false);
1465         ui.rsyncOutput->append("<br><font color=red><b>" + tr("ABORTED") + " !!</b></font>");
1466 
1467         //update tray baloon
1468         if ( (QSystemTrayIcon::isSystemTrayAvailable ()) && (QSystemTrayIcon::supportsMessages ()) )
1469         {
1470             if (KDErunning)
1471             {
1472                 QProcess *dialogProcess;    dialogProcess = new QProcess(this);
1473                 QStringList dialogArgs;
1474                 dialogArgs << "--title" << appName + " - " + tr("execution of profile:") + " " + profileName + " " + tr("finished") << "--passivepopup" << trayMessage << "10";
1475                 dialogProcess -> start ("kdialog",dialogArgs);
1476             }
1477             else
1478                 LBtray -> showMessage (appName + " - " + tr("execution of profile:") + " " + profileName + " " + tr("finished") , trayMessage,
1479                                                             QSystemTrayIcon::Information,3000);
1480             actionAbort	-> setVisible(false);
1481         }
1482 
1483         if  (errorsFound > 0)// initialize jump to next error button
1484         {
1485             firstScroll=true;
1486             ui.pushButton_nextError	-> setEnabled (true);
1487         }
1488 
1489         finishUp();
1490         if (!DryRun)
1491         {
1492             // strip unused lines from the snapshot changes file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
1493             QString filesAdded = "", snapLine = "";
1494             if (snapfile.open(QIODevice::ReadOnly | QIODevice::Text))
1495             {
1496                 QTextStream in(&snapfile);
1497                 while (!in.atEnd())
1498                 {
1499                     snapLine = in.readLine();
1500                     if (snapLine.contains("+++++++", Qt::CaseInsensitive))
1501                     {
1502                         snapLine = snapLine.right(snapLine.size()-snapLine.lastIndexOf("[LB]")-removeCharLimit) + "\n";
1503                         filesAdded.append(snapLine);
1504                     }
1505                 }
1506                 snapfile.close();
1507                 filesAdded.remove("[LB]", Qt::CaseSensitive);    // just to make sure cause sometimes there are [LB]'s left
1508             }
1509             if (snapfile.open(QIODevice::WriteOnly))
1510             {
1511                 QTextStream out(&snapfile);
1512                 out << filesAdded;
1513                 snapfile.close();
1514             }
1515         }
1516 
1517         if ( (silentMode) && (isMinimizedToTray == true) )		// if --silent is given as argument and the gui is not shown, exit the app
1518             exit(0);	//quit
1519     }
1520     ui.nowDoing -> setText (nowDoingText);
1521 }
1522 
1523 // function finishUp=====================================================================================================
1524 // finish up some stuff when all tasks finish either normally or aborted
finishUp()1525 void luckyBackupWindow::finishUp()
1526 {
1527     if (!DryRun)
1528     {
1529         //save the profile to update last execution times & no of errors
1530         if (!saveProfile(currentProfile))
1531         {
1532             savedProfile = false;
1533             ui.actionSave -> setEnabled(true);
1534         }
1535         else
1536         {
1537             savedProfile = true;			//change profile status to "saved"
1538             ui.actionSave -> setEnabled(false);
1539         }
1540 
1541         logfile.close();			// close the logfile
1542 
1543         // send an email
1544         if ( (!ABORTpressed) && (!emailNever) )
1545         {
1546             bool send = true;
1547             if ( ((emailError) && (errorsFound == 0))   // do not send if the condition "error" is checked and there are no errors
1548                     ||
1549                 ((emailSchedule) && (!silentMode)) )  // do not send if the condition "scheduled" is checked and profile is not run in silent mode
1550 
1551                 send = false;
1552 
1553             // ***** console mode and "schedule" is covered in commandline.cpp ******
1554 
1555             if (send)
1556             {
1557                 ui.rsyncOutput->append("<font color=green>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~</font>");
1558                 ui.rsyncOutput->append(tr(  "trying to send an email"));
1559                 ui.rsyncOutput->append(     "       . . .");
1560                 ui.rsyncOutput->append(sendEmailNow(false));
1561             }
1562         }
1563     }
1564 
1565     shutdownToolbar-> setEnabled (false);
1566 
1567     // TESTING-TESTING-TESTING-TESTING-TESTING-TESTING
1568     //QMessageBox::information(this, "LB","QtextDocument title= **" + ui.rsyncOutput->documentTitle() +"**");
1569 }
1570 
1571 // function shutDownSystem=====================================================================================================
1572 // shutdown the system if the relevant button is pressed, when all tasks are finished
shutDownSystem()1573 void luckyBackupWindow::shutDownSystem()
1574 {
1575     QProcess *shutdownProcess;
1576     shutdownProcess = new QProcess(this);
1577     QString shutdownCommand="";    QStringList shutdownArgs;    shutdownArgs.clear();
1578 
1579     if (KDErunning)
1580     {
1581         shutdownCommand = "/usr/local/bin/qdbus";
1582         shutdownArgs << "org.kde.ksmserver" << "/KSMServer" << "org.kde.KSMServerInterface.logout" << "1" << "2" << "2";
1583     }
1584     else if (currentUser == "super user")
1585     {
1586         shutdownCommand = "/sbin/shutdown";
1587         shutdownArgs << "-h" << "1";
1588         if ( (QSystemTrayIcon::isSystemTrayAvailable ()) && (QSystemTrayIcon::supportsMessages ()) )
1589             LBtray -> showMessage (appName + " - " + tr("WARNING"), tr("The system will shutdown in 1 minute"), QSystemTrayIcon::Information,3000);
1590     }
1591 
1592     shutdownProcess -> start (shutdownCommand,shutdownArgs);
1593 
1594     //delay the app exit for 3 seconds
1595     QTime StartSleep(0,0,0,0);
1596     StartSleep.start();
1597     int elapsedSleepMsec = 0;
1598     while (elapsedSleepMsec < 3000)
1599         elapsedSleepMsec = StartSleep.elapsed();
1600     exit(0);    //quit
1601 }
1602 
1603 // previous error button pressed=====================================================================================================
previousErrorJump()1604 void luckyBackupWindow::previousErrorJump()
1605 {
1606     errorCount--;		//decrease the current error by one
1607 
1608     if (errorCount == 0 )		// if the current error is the first within the logfile disable the previous button
1609         ui.pushButton_previousError -> setEnabled(false);
1610 
1611     if (errorCount < errorsFound-1)	//if the current error is less than the last one within the logfile enable the next button
1612         ui.pushButton_nextError -> setEnabled(true);
1613 
1614     ui.rsyncOutput -> scrollToAnchor("error" + countStr.setNum(errorCount+1));
1615 }
1616 
1617 // next error button pressed=====================================================================================================
nextErrorJump()1618 void luckyBackupWindow::nextErrorJump()
1619 {
1620     if (!firstScroll)
1621         errorCount++;	// increase the current error by one
1622     firstScroll = false;
1623 
1624     if (errorCount == errorsFound-1)		// If the current error is the last within the logfile disable the next button
1625         ui.pushButton_nextError -> setEnabled(false);
1626 
1627     if (errorCount > 0)				// if the current error is greater than the first one within the logfile enable the previous button
1628         ui.pushButton_previousError -> setEnabled(true);
1629 
1630     ui.rsyncOutput -> scrollToAnchor("error" + countStr.setNum(errorCount+1));
1631 }
1632 
1633 // convertBytes (QString,bool)
1634 // Converts a string of the form 67M to bytes and vice versa (eg 1024 -> 1KB)
1635 // if bool=false then conversion string->bytes. If bool=true then conversion bytes->string
1636 // =====================================================================================================
convertBytes(QString byteLine,bool toWhat)1637 QString luckyBackupWindow::convertBytes (QString byteLine,bool toWhat)
1638 {
1639     QString returnThis = "";
1640     bool ok;
1641 
1642     if (toWhat)           // convert bytes to string
1643     {
1644         QString multi = " bytes";
1645         double bytesFLOAT = byteLine.toDouble(&ok);
1646 
1647         if (bytesFLOAT >=  1024)
1648         {
1649             bytesFLOAT = bytesFLOAT / 1024;
1650             multi = "KB";
1651         }
1652         if (bytesFLOAT >= 1024)
1653         {
1654             bytesFLOAT = bytesFLOAT / 1024;
1655             multi = "MB";
1656         }
1657         if (bytesFLOAT >= 1024)
1658         {
1659             bytesFLOAT = bytesFLOAT / 1024;
1660             multi = "GB";
1661         }
1662         if (bytesFLOAT >= 1024)
1663         {
1664             bytesFLOAT = bytesFLOAT / 1024;
1665             multi = "TB";
1666         }
1667         returnThis = (QString::number(bytesFLOAT));
1668         if ( (returnThis.contains(".")) && ((returnThis.lastIndexOf(".") + 3) < returnThis.size()) )
1669             returnThis.chop(returnThis.size() - returnThis.lastIndexOf(".") - 3);   // leave only 3 decimal points
1670         returnThis = returnThis + multi;
1671     }
1672     else            // convert string to bytes
1673     {
1674         unsigned long long int multiply = 1;
1675 
1676         if (byteLine.endsWith("K"))
1677             multiply = 1024;
1678         if (byteLine.endsWith("M"))
1679             multiply = pow(1024,2);
1680         if (byteLine.endsWith("G"))
1681             multiply = pow(1024,3);
1682         if (byteLine.endsWith("T"))
1683             multiply = pow(1024,4);
1684 
1685         if (    (byteLine.endsWith("K")) || (byteLine.endsWith("M")) ||
1686                 (byteLine.endsWith("G")) || (byteLine.endsWith("T")) )
1687             byteLine.chop(1);
1688 
1689         unsigned long long int returnThisNo = byteLine.toDouble(&ok) * multiply;
1690         returnThis = QString::number( returnThisNo);
1691     }
1692 
1693     return returnThis;
1694 
1695 }
1696 // end of executenow.cpp ---------------------------------------------------------------------------
1697 
1698