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