added app list backup, helper shell scripts
[fapman] / aaptinterface.cpp
1 /*
2         This file is part of Faster Application Manager.
3
4         Faster Application Manager is free software: you can redistribute it and/or modify
5         it under the terms of the GNU General Public License as published by
6         the Free Software Foundation, either version 3 of the License, or
7         (at your option) any later version.
8
9         Faster Application Manager is distributed in the hope that it will be useful,
10         but WITHOUT ANY WARRANTY; without even the implied warranty of
11         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12         GNU General Public License for more details.
13
14         You should have received a copy of the GNU General Public License
15         along with Faster Application Manager.  If not, see <http://www.gnu.org/licenses/>.
16
17         (C) Heikki Holstila 2010
18 */
19
20 #include <QtGui>
21 #include <QtNetwork>
22
23 #include "aaptinterface.h"
24 #include "package.h"
25 #include "dimmer.h"
26 #include "repository.h"
27 #include "mainwindow.h"
28 #include "settings.h"
29
30 AAptInterface::AAptInterface(QObject* parent = 0) : QObject(parent)
31 {
32         iMode = ModeNone;
33         iMainWindow = dynamic_cast<MainWindow*>(parent);
34         iUiDimmer = 0;
35         iSettings = 0;
36         iTerminated = false;
37         iCanCancel = false;
38         iNumSelectedPackages = 0;
39         iErrorDone = false;
40         iNeedRepoRefresh = false;
41         iNeedListRefresh = true;
42         iNeedDateRefresh = true;
43         iNeedDpkgRefresh = true;
44         iSkipRefreshListAndDates = false;
45         iLastListUpdate.setTime_t(0);
46         iLastDpkgUpdate.setTime_t(0);
47
48         iDataReadBuffer = new char[KDataReadBufferSize];
49
50         iProcAptGetUpdate = new QProcess(this);
51         iProcAptGetSimulate = new QProcess(this);
52         iProcAptGetInstall = new QProcess(this);
53         iProcAptGetClean = new QProcess(this);
54
55         iProcAptGetUpdate->setProcessChannelMode(QProcess::MergedChannels);
56         iProcAptGetSimulate->setProcessChannelMode(QProcess::MergedChannels);
57         iProcAptGetInstall->setProcessChannelMode(QProcess::MergedChannels);
58         iProcAptGetClean->setProcessChannelMode(QProcess::MergedChannels);
59
60         connect(iProcAptGetUpdate,SIGNAL(error(QProcess::ProcessError)),this,SLOT(errorAptGetUpdate(QProcess::ProcessError)));
61         connect(iProcAptGetUpdate,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(finishedAptGetUpdate(int,QProcess::ExitStatus)));
62         connect(iProcAptGetUpdate,SIGNAL(readyRead()),this,SLOT(uiUpdaterAptGetUpdate()));
63
64         connect(iProcAptGetInstall,SIGNAL(error(QProcess::ProcessError)),this,SLOT(errorAptGetInstall(QProcess::ProcessError)));
65         connect(iProcAptGetInstall,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(finishedAptGetInstall(int,QProcess::ExitStatus)));
66         connect(iProcAptGetInstall,SIGNAL(readyRead()),this,SLOT(uiUpdaterAptGetInstall()));
67
68         connect(iProcAptGetSimulate,SIGNAL(error(QProcess::ProcessError)),this,SLOT(errorAptGetSimulate(QProcess::ProcessError)));
69         connect(iProcAptGetSimulate,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(finishedAptGetSimulate(int,QProcess::ExitStatus)));
70
71         connect(iProcAptGetClean,SIGNAL(error(QProcess::ProcessError)),this,SLOT(errorAptGetClean(QProcess::ProcessError)));
72         connect(iProcAptGetClean,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(finishedAptGetClean(int,QProcess::ExitStatus)));
73
74         iProcessPackages.clear();
75         iProcessPackagesOrig.clear();
76         iProcessPackageVersions.clear();
77
78         QDir logdir(KFapmanDir);
79         logdir.mkpath(KFapmanDir);
80
81         // create custom apt cache dirs if they don't exist
82         QDir d1(KAptListDir + "/partial");
83         d1.mkpath(KAptListDir + "/partial");
84
85         // clear log on startup
86         QFile logfile(KLogFile);
87         logfile.remove();
88
89         readRepositoryInfo();
90 }
91
92 AAptInterface::~AAptInterface()
93 {
94         if( iProcAptGetUpdate->state() != QProcess::NotRunning )
95                 iProcAptGetUpdate->kill();
96         if( iProcAptGetSimulate->state() != QProcess::NotRunning )
97                 iProcAptGetSimulate->kill();
98         if( iProcAptGetInstall->state() != QProcess::NotRunning )
99                 iProcAptGetInstall->kill();
100         if( iProcAptGetClean->state() != QProcess::NotRunning )
101                 iProcAptGetClean->kill();
102
103         // qprocesses are automatically deleted by their parent
104
105         QHashIterator<QString, Package*> a( iPackagesAvailable );
106         while (a.hasNext())
107         {
108                 a.next();
109                 delete a.value();
110         }
111         iPackagesAvailable.clear();
112
113         QHashIterator<QString, Package*> i( iPackagesInstalled );
114         while (i.hasNext())
115         {
116                 i.next();
117                 delete i.value();
118         }
119         iPackagesInstalled.clear();
120
121         for(int x=0; x<iRepositories.count(); x++) {
122                 if( iRepositories.at(x) )
123                         delete iRepositories.at(x);
124         }
125         iRepositories.clear();
126
127         delete[] iDataReadBuffer;
128         iDataReadBuffer=0;
129 }
130
131 void AAptInterface::addQueuedOperation(interfaceMode mode_)
132 {
133         iOperationsQueue.append( mode_ );
134 }
135
136 void AAptInterface::setNeedRefresh(int repos, int lists, int dpkg, int dates)
137 {
138         if( repos==0 || repos==1 )
139                 iNeedRepoRefresh = (bool)repos;
140         if( lists==0 || lists==1 )
141                 iNeedListRefresh = (bool)lists;
142         if( dpkg==0 || dpkg==1 )
143                 iNeedDpkgRefresh = (bool)dpkg;
144         if( dates==0 || dates==1 )
145                 iNeedDateRefresh = (bool)dates;
146 }
147
148 bool AAptInterface::needListOrDateRefresh()
149 {
150         if( iNeedListRefresh || iNeedDpkgRefresh || iNeedDateRefresh )
151                 return true;
152
153         return false;
154 }
155
156 bool AAptInterface::run(dimmer* uiDimmer)
157 {
158         if( iMode != ModeNone ) {
159                 //qDebug() << "Can't run: not ModeNone";
160                 return false;
161         }
162
163         if( iOperationsQueue.count() == 0 ) {
164                 qDebug() << "Can't run: Queue empty";
165                 return false;
166         }
167
168         iUiDimmer = uiDimmer;
169         iQueueMessages.clear();
170         iModeLog.clear();
171
172         runNext();
173
174         return true;
175 }
176
177 void AAptInterface::runNext()
178 {
179         if( iOperationsQueue.count()==0 ) {
180                 cleanAfterRunAll();
181                 return;
182         }
183         if( iTerminated ) {
184                 cleanAfterError();
185                 return;
186         }
187
188         cleanAfterRunEach();
189
190         iMode = iOperationsQueue.takeAt(0);
191         iModeLog.append(iMode);
192
193         if( iMode == ModeAptGetUpdate ) {
194                 if( !startAptGetUpdate() )
195                         errorAptGetUpdate( QProcess::FailedToStart );
196         }
197         if( iMode == ModeAptGetInstall ) {
198                 if( !startAptGetInstall() )
199                         errorAptGetInstall( QProcess::FailedToStart );
200         }
201         if( iMode == ModeAptGetSimulate ) {
202                 if( !startAptGetSimulate() )
203                         errorAptGetSimulate( QProcess::FailedToStart );
204         }
205         if( iMode == ModeAptGetClean ) {
206                 if( !startAptGetClean() )
207                         errorAptGetClean( QProcess::FailedToStart );
208         }
209         if( iMode == ModeFetchDates )
210                 startFetchDates();
211
212         if( iMode == ModeReadPackages )
213                 startPkgListRead();
214 }
215
216 void AAptInterface::cleanAfterRunEach()
217 {
218         iMode = ModeNone;
219         iTerminated = false;
220         iErrorDone = false;
221         iCanCancel = false;
222 }
223
224 void AAptInterface::cleanAfterRunAll()
225 {
226         cleanAfterRunEach();
227         iUiDimmer = 0;
228         iSkipRefreshListAndDates = false;
229 }
230
231 void AAptInterface::cleanAfterError()
232 {
233         cleanAfterRunAll();
234         iOperationsQueue.clear();
235         iProcessPackages.clear();
236         iProcessPackagesOrig.clear();
237         iProcessPackageVersions.clear();
238 }
239
240 bool AAptInterface::running() const
241 {
242         if( iMode == ModeNone )
243                 return false;
244         return true;
245 }
246
247 bool AAptInterface::cancel()
248 {
249         // should return false if can't cancel, or terminate the running process (and clear queue) otherwise
250         if( iMode == ModeNone )
251                 return false;
252
253         if( !iCanCancel ) {
254                 return false;
255         }
256
257         if( iMode == ModeAptGetUpdate ) {
258                 if( iProcAptGetUpdate->state() == QProcess::Running )
259                         iProcAptGetUpdate->terminate();
260                 else
261                         return false;
262                 cleanAfterError();
263                 iTerminated = true;
264                 iNeedRepoRefresh = true;
265                 return true;
266         }
267         if( iMode == ModeAptGetSimulate ) {
268                 if( iProcAptGetSimulate->state() == QProcess::Running )
269                         iProcAptGetSimulate->terminate();
270                 else
271                         return false;
272                 cleanAfterError();
273                 iTerminated = true;
274                 return true;
275         }
276         if( iMode == ModeAptGetInstall ) {
277                 if( iProcAptGetInstall->state() == QProcess::Running )
278                         iProcAptGetInstall->terminate();
279                 else
280                         return false;
281                 cleanAfterError();
282                 iTerminated = true;
283                 return true;
284         }
285         if( iMode == ModeReadPackages ) {
286                 iTerminated = true;
287                 return true;
288         }
289         if( iMode == ModeFetchDates ) {
290                 iTerminated = true;
291                 return true;
292         }
293         return false;
294 }
295
296 bool AAptInterface::startAptGetUpdate()
297 {
298         iCanCancel = true;
299         iProcAptGetUpdateOutput.clear();
300
301         if( !this->writeRepositories() )
302                 return false;
303         iNeedListRefresh = true;
304         iNeedDpkgRefresh = true;
305         iNeedDateRefresh = true;
306
307         if( iUiDimmer ) {
308                 iUiDimmer->setProgress(0);
309                 iUiDimmer->updateText("Updating catalogs");
310         }
311
312         iCatalogCounter = 0;
313         iCatalogsTotal = 0;
314         for( int i=0; i<iRepositories.count(); i++ ) {
315                 if( iRepositories.at(i) && iRepositories.at(i)->enabled() ) {
316                         iCatalogsTotal += 2;
317                         QStringList comp = iRepositories.at(i)->components().split(' ');
318                         iCatalogsTotal += comp.count();
319                 }
320         }
321
322         bool useproxy = iSettings->qsettings()->value("use_proxies",false).toBool();
323         QString http_proxy = iSettings->qsettings()->value("http_proxy","").toString();
324         QString https_proxy = iSettings->qsettings()->value("https_proxy","").toString();
325         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
326         if( useproxy ) {
327                 if( http_proxy != "" )
328                         env.insert("http_proxy", http_proxy);
329                 if( https_proxy != "" )
330                         env.insert("https_proxy", https_proxy);
331         }
332         iProcAptGetUpdate->setProcessEnvironment(env);
333
334         QString runBinary = "/usr/bin/apt-get";
335         QStringList runParameters;
336         runParameters << "-q" << "update"
337                         << "-o" << "Dir::Etc::sourcelist=" + KAptSourceList
338                         << "-o" << "Dir::State::lists=" + KAptListDir
339                         << "-o" << "Dir::Etc::sourceparts=\"\"";
340         logToFile( runBinary + " " + runParameters.join(" ") );
341         iProcAptGetUpdate->start(runBinary,runParameters);
342
343         return true;
344 }
345
346 bool AAptInterface::startAptGetSimulate()
347 {
348         if( iProcessPackages.count()==0 )
349                 return false;
350         iCanCancel = true;
351
352         if( iUiDimmer ) {
353                 iUiDimmer->updateText("Reading dependencies");
354         }
355
356         QString runBinary = "/usr/bin/apt-get";
357         QStringList runParameters;
358         runParameters << "-qsy" << "--allow-unauthenticated"
359                         << "-o" << "Dir::Etc::sourcelist=" + KAptSourceList
360                         << "-o" << "Dir::State::lists=" + KAptListDir
361                         << "-o" << "Dir::Etc::sourceparts=\"\"";
362         if( iSettings->qsettings()->value("enable_autoremove", true).toBool() )
363                 runParameters << "--auto-remove";
364         runParameters << "install";
365         runParameters << iProcessPackages;
366
367         logToFile( runBinary + " " + runParameters.join(" ") );
368         iProcAptGetSimulate->start(runBinary,runParameters);
369
370         return true;
371 }
372
373 bool AAptInterface::startAptGetInstall()
374 {
375         if( iProcessPackages.count()==0 )
376                 return false;
377
378         iNeedListRefresh = true;
379         iNeedDpkgRefresh = true;
380
381         iProcAptGetInstallOutput.clear();
382
383         qDebug() << "running apt-get install";
384
385         QString runBinary = "/usr/bin/apt-get";
386         QStringList runParameters;
387         runParameters << "-qy" << "--allow-unauthenticated"
388                         << "-o" << "DPkg::options::=--force-confnew"
389                         << "-o" << "Dir::Etc::sourcelist=" + KAptSourceList
390                         << "-o" << "Dir::State::lists=" + KAptListDir
391                         << "-o" << "Dir::Etc::sourceparts=\"\"";
392         if( iSettings->qsettings()->value("enable_autoremove", true).toBool() )
393                 runParameters << "--auto-remove";
394         runParameters << "install";
395         runParameters << iProcessPackagesOrig;
396
397         int inst_count = 0;
398         int remv_count = 0;
399         for( int i=0; i<iProcessPackages.count(); i++) {
400                 if( iProcessPackages.at(i).endsWith('-') )
401                         remv_count++;
402                 else
403                         inst_count++;
404         }
405
406         iAptGetDownloadCount = 0;
407         iAptGetInstallCount = 0;
408         iAptGetRemoveCount = 0;
409         iAptGetInstallTotal = inst_count;
410         iAptGetRemoveTotal = remv_count;
411         iAptGetCurrentFileDownloadSize = 0;
412         iAptGetCurrentFileTotalSize = 0;
413         iAptGetCurrentDownloadFileName = "";
414         iProgressCheckTimer = 0;
415         iSpeedKbps = 0;
416         iSpeedKbpsPrev = -1;
417         iUpdateSpeed = false;
418
419         bool useproxy = iSettings->qsettings()->value("use_proxies",false).toBool();
420         QString http_proxy = iSettings->qsettings()->value("http_proxy","").toString();
421         QString https_proxy = iSettings->qsettings()->value("https_proxy","").toString();
422         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
423         if( useproxy ) {
424                 if( http_proxy != "" )
425                         env.insert("http_proxy", http_proxy);
426                 if( https_proxy != "" )
427                         env.insert("https_proxy", https_proxy);
428         }
429         env.insert("DEBIAN_FRONTEND", "noninteractive");
430         iProcAptGetUpdate->setProcessEnvironment(env);
431
432         logToFile( runBinary + " " + runParameters.join(" ") );
433         iProcAptGetInstall->start(runBinary,runParameters);
434
435         return true;
436 }
437
438 bool AAptInterface::startAptGetClean()
439 {
440         QString runBinary = "/usr/bin/apt-get";
441         QStringList runParameters;
442         runParameters
443                         << "-o" << "Dir::Etc::sourcelist=" + KAptSourceList
444                         << "-o" << "Dir::State::lists=" + KAptListDir
445                         << "-o" << "Dir::Etc::sourceparts=\"\""
446                         << "clean";
447
448         if( iUiDimmer ) {
449                 iUiDimmer->updateText("Cleaning package cache");
450         }
451
452         logToFile( runBinary + " " + runParameters.join(" ") );
453         iProcAptGetClean->start(runBinary,runParameters);
454
455         return true;
456 }
457
458 QString AAptInterface::setQProcessErrorMessage(QProcess::ProcessError error)
459 {
460         if( iTerminated ) {
461                 logToFile(QString("Cancelled by user - terminating process"));
462                 return "Cancelled by user";
463         }
464         else if( error == QProcess::FailedToStart )
465                 return "Process failed to start";
466         else if( error == QProcess::Crashed )
467                 return "Process crashed";
468         else if( error == QProcess::ReadError )
469                 return "QProcess read error";
470         else if( error == QProcess::WriteError )
471                 return "QProcess write error";
472         else if( error == QProcess::Timedout )
473                 return "QProcess timeout error";
474         else if( error == QProcess::UnknownError )
475                 return "QProcess unknown error";
476
477         return "Unknown error";
478 }
479
480 void AAptInterface::errorAptGetUpdate(QProcess::ProcessError error)
481 {
482         QString msg = setQProcessErrorMessage(error);
483
484         if( iUiDimmer )
485                 iUiDimmer->setProgress(-1);
486
487         communicateStatusToUi(false, "Error", msg);
488         cleanAfterError();
489         iNeedRepoRefresh = true;
490         iErrorDone = true;
491 }
492
493 void AAptInterface::errorAptGetSimulate(QProcess::ProcessError error)
494 {
495         QString msg = setQProcessErrorMessage(error);
496         iProcessPackages.clear();
497         iProcessPackagesOrig.clear();
498         iProcessPackageVersions.clear();
499
500         communicateStatusToUi(false, "Error", msg);
501         cleanAfterError();
502         iErrorDone = true;
503 }
504
505 void AAptInterface::errorAptGetInstall(QProcess::ProcessError error)
506 {
507         QString msg = setQProcessErrorMessage(error);
508         iProcessPackages.clear();
509         iProcessPackagesOrig.clear();
510         iProcessPackageVersions.clear();
511
512         if( iProgressCheckTimer ) {
513                 iProgressCheckTimer->stop();
514                 delete iProgressCheckTimer;
515                 iProgressCheckTimer = 0;
516         }
517         if( iUiDimmer ) {
518                 iUiDimmer->setProgress(-1);
519         }
520
521         communicateStatusToUi(false, "Error", msg);
522         cleanAfterError();
523         iErrorDone = true;
524 }
525
526 void AAptInterface::errorAptGetClean(QProcess::ProcessError error)
527 {
528         QString msg = setQProcessErrorMessage(error);
529
530         communicateStatusToUi(false, "Error", msg);
531         cleanAfterError();
532         iErrorDone = true;
533 }
534
535 QString AAptInterface::finishProcessCommonErrorMessages(QByteArray& output)
536 {
537         QString msg = "Unknown error - see the log for details";
538
539         if( output.contains("Could not get lock") || output.contains("Could not open lock file") ) {
540                 msg = "The package management system is locked by another process or permission was denied";
541         } else if( output.contains("E: Unable to fetch some archives") ) {
542                 msg = "Failed to fetch packages - Your network connection might be down or your catalogs could be out of date";
543         } else if( output.contains("E: Couldn't find package") ) {
544                 msg = "Missing package, see the log for details - Your catalogs might be out of date";
545         } else if( output.contains("E: Broken packages") ) {
546                 msg = "Your system has broken packages or you are trying to install conflicting packages. See the log for details.";
547         } else if( output.contains("E: Handler silently failed") ) {
548                 msg = "Handler silently failed - This can happen if you try to install from the forbidden user/hidden category. Disabling OVI store repositories might help.";
549         } else if( iTerminated ) {
550                 msg = "The operation was cancelled by user";
551         } else if( output.contains("Temporary failure resolving") || output.contains("Could not resolve host") ) {
552                 msg = "DNS errors were reported, check your network connection and/or repository configuration";
553         } else if( output.contains("E: dpkg was interrupted") ) {
554                 msg = "Your system has partially installed or broken packages. You'll have to fix this manually. Try dpkg --configure -a";
555         } else if( output.contains("dpkg: error processing") || output.contains("Errors were encountered while processing:") ) {
556                 msg = "dpkg reported errors while processing a package - see the log for details";
557         } else if( output.contains("E: Unmet dependencies") ) {
558                 msg = "Some of your packages have unmet dependencies which could not be fixed. See the log for details.";
559         } else if( output.contains("E: The method driver") ) {
560                 msg = "Apt failed to find a suitable method driver. One or more of your repositories might have an invalid URL.";
561         } else if( output.contains("E: Invalid record in the preferences file") ) {
562                 msg = "Invalid record in the apt preferences file.";
563         } else if( output.contains("E: Malformed line") && output.contains("in source list") ) {
564                 msg = "Malformed line in sources list. Check your repository settings.";
565         }
566
567         return msg;
568 }
569
570 void AAptInterface::finishedAptGetUpdate(int exitCode, QProcess::ExitStatus exitStatus)
571 {
572         //QByteArray output = iProcAptGetUpdate->readAllStandardOutput();
573         //logToFile( "Output from last process:\n---\n"+output );
574
575         if( iErrorDone ) {
576                 iErrorDone = false;
577                 iProcAptGetUpdate->close();
578                 return;
579         }
580
581         bool success = true;
582         QString title = "Operation finished";
583         QString msg = "Catalogs updated";
584         if( exitCode != 0 || exitStatus == QProcess::CrashExit )
585         {
586                 success = false;
587                 title = "Error";
588                 msg = finishProcessCommonErrorMessages(iProcAptGetUpdateOutput);
589         }
590
591         if( iUiDimmer )
592                 iUiDimmer->setProgress(-1);
593
594         if( iProcAptGetUpdateOutput.contains("Could not resolve ") || iProcAptGetUpdateOutput.contains("W: Failed to fetch") ||
595                 iProcAptGetUpdateOutput.contains("Temporary failure resolving") ) {
596                 success = false;
597                 title = "Error";
598                 msg = "Failed to update some or all of the catalogs. Check your network connection and/or repository configuration";
599         }
600
601         if( success ) {
602                 iNeedRepoRefresh = false;
603
604                 QFile lastupdate(KLastUpdateFile);   // create an empty file and/or update the modification time
605                 if( lastupdate.open(QIODevice::WriteOnly) )
606                         lastupdate.close();
607
608                 int pos = iProcAptGetUpdateOutput.indexOf("\nFetched ");
609                 if( pos!=-1 ) {
610                         msg += "<br>apt-get: ";
611                         msg += iProcAptGetUpdateOutput.mid(pos+1, iProcAptGetUpdateOutput.indexOf('\n', pos+1)-pos ).trimmed();
612                 }
613
614         } else {
615                 cleanAfterError();
616         }
617
618         iProcAptGetUpdate->close();
619         communicateStatusToUi(success, title, msg);
620         runNext();
621 }
622
623 void AAptInterface::finishedAptGetSimulate(int exitCode, QProcess::ExitStatus exitStatus)
624 {
625         QByteArray output = iProcAptGetSimulate->readAllStandardOutput();
626         logToFile( "Output from last process:\n---\n"+output );
627
628         if( iErrorDone ) {
629                 iErrorDone = false;
630                 iProcAptGetSimulate->close();
631                 return;
632         }
633
634         bool success = true;
635         QString title = "Operation finished";
636         QString msg = "";
637         if( exitCode != 0 || exitStatus == QProcess::CrashExit )
638         {
639                 success = false;
640                 title = "Error";
641                 msg = finishProcessCommonErrorMessages(output);
642         }
643
644         iProcessPackages.clear();
645         iProcessPackageVersions.clear();
646         if( success )
647         {
648                 QList<QByteArray> lines = output.split('\n');
649
650                 for( int i=0; i<lines.count(); i++)
651                 {
652                         QString s = lines.at(i);
653                         if( s.startsWith("Inst ") )
654                         {
655                                 iProcessPackages <<  s.section(' ',1,1);
656
657                                 QString vs="";
658                                 /*
659                                 int a1 = s.indexOf('[');
660                                 int a2 = s.indexOf(']');
661                                 if( a1!=-1 && a2!=-1 && a2>a1) {
662                                         vs = s.mid(a1+1, a2-a1-1) + " -> ";
663                                 }*/
664                                 int b1 = s.indexOf('(');
665                                 int b2 = s.indexOf(' ',b1);
666                                 if( b1!=-1 && b2!=-1 && b2>b1) {
667                                         vs += s.mid(b1+1, b2-b1-1);
668                                 }
669                                 //qDebug() << vs;
670                                 iProcessPackageVersions << vs;
671                         }
672                         if( s.startsWith("Remv ") )
673                         {
674                                 iProcessPackages << s.section(' ',1,1) + "-";
675
676                                 QString vs="";
677                                 int a1 = s.indexOf('[');
678                                 int a2 = s.indexOf(']');
679                                 if( a1!=-1 && a2!=-1 && a2>a1) {
680                                         vs = s.mid(a1+1, a2-a1-1);
681                                 }
682                                 //qDebug() << vs;
683                                 iProcessPackageVersions << vs;
684                         }
685                 }
686         }
687         if( !success )
688                 cleanAfterError();
689
690         iProcAptGetSimulate->close();
691         communicateStatusToUi(success, title, msg);
692         runNext();
693 }
694
695 void AAptInterface::finishedAptGetInstall(int exitCode, QProcess::ExitStatus exitStatus)
696 {
697         //QByteArray output = iProcAptGetInstall->readAllStandardOutput();
698         //logToFile( "Output from last process:\n---\n"+output );
699
700         if( iErrorDone ) {
701                 iProcAptGetInstall->close();
702                 iErrorDone = false;
703                 return;
704         }
705
706         bool success = true;
707         QString title = "Operation finished";
708         QString msg = "Package operations finished successfully";
709         if( exitCode != 0 || exitStatus == QProcess::CrashExit || iTerminated )
710         {
711                 success = false;
712                 title = "Error";
713                 msg = finishProcessCommonErrorMessages(iProcAptGetInstallOutput);
714         }
715
716         if( iProgressCheckTimer ) {
717                 iProgressCheckTimer->stop();
718                 delete iProgressCheckTimer;
719                 iProgressCheckTimer = 0;
720         }
721         if( iUiDimmer ) {
722                 iUiDimmer->setProgress(-1);
723         }
724         iProcessPackages.clear();
725         iProcessPackagesOrig.clear();
726         iProcessPackageVersions.clear();
727
728         if( !success )
729                 cleanAfterError();
730
731         iProcAptGetInstall->close();
732         communicateStatusToUi(success, title, msg);
733         runNext();
734 }
735
736 void AAptInterface::finishedAptGetClean(int exitCode, QProcess::ExitStatus exitStatus)
737 {
738         QByteArray output = iProcAptGetClean->readAllStandardOutput();
739         // this should produce no output
740         //logToFile( "Output from last process:\n---\n"+output );
741
742         if( iErrorDone ) {
743                 iErrorDone = false;
744                 iProcAptGetClean->close();
745                 return;
746         }
747
748         bool success = true;
749         QString title = "Operation finished";
750         QString msg = "Package cache cleaned";
751         if( exitCode != 0 || exitStatus == QProcess::CrashExit )
752         {
753                 success = false;
754                 title = "Error";
755                 msg = finishProcessCommonErrorMessages(output);
756         }
757         if( !success )
758                 cleanAfterError();
759
760         iProcAptGetClean->close();
761         communicateStatusToUi(success, title, msg);
762         runNext();
763 }
764
765
766 void AAptInterface::uiUpdaterAptGetUpdate()
767 {
768         QByteArray data = iProcAptGetUpdate->read( iProcAptGetUpdate->bytesAvailable() );
769         logToFile( data, false );
770         iProcAptGetUpdateOutput.append(data);
771
772         if( !iUiDimmer )
773                 return;
774
775         QStringList lines = QString( data.trimmed() ).split('\n');
776         QString infoline;
777
778         for( int i=0; i<lines.count(); i++ )
779         {
780                 if( lines.at(i).startsWith("Get:") || lines.at(i).startsWith("Hit ") )
781                 {
782                         iCatalogCounter++;                      
783                 }
784                 infoline = lines.at(i).trimmed();
785         }
786         //if( infoline.isEmpty() )
787                 //infoline = "arstus";
788
789         //iUiDimmer->updateText( QString("Updating catalogs (%1)").arg(iCatalogCounter) );
790         //qDebug() << iCatalogCounter << iCatalogsTotal;
791
792         //iUiDimmer->updateText("Updating catalogs<br><font size=\"-1\">" + infoline + "</font>");
793
794         if( iCatalogsTotal > 0 )
795                 iUiDimmer->setProgress( iCatalogCounter*100/iCatalogsTotal );
796 }
797
798 void AAptInterface::uiUpdaterAptGetInstall()
799 {
800         QByteArray data = iProcAptGetInstall->read( iProcAptGetInstall->bytesAvailable() );
801         logToFile( data, false );
802         iProcAptGetInstallOutput.append(data);
803
804         if( !iUiDimmer )
805                 return;
806
807         QStringList lines = QString( data.trimmed() ).split('\n');
808
809         bool update = false;
810         bool resetprogress = true;
811         QString oper = "";
812         QString pkgname = "";
813         iCanCancel = false;
814         for( int i=0; i<lines.count(); i++ )
815         {
816                 QStringList l = lines.at(i).split(' ');
817
818                 if( l.count()>=4 && l.at(0).startsWith("Get:") ) {
819                         oper = "Downloading";
820                         iCanCancel = true;
821                         pkgname = l.at(3);
822                         Package* pkg = iPackagesAvailable.value(pkgname,0);
823                         if( pkg!=0 ) {
824                                 iAptGetCurrentDownloadFileName = pkg->fileName();
825                                 iAptGetCurrentFileTotalSize = pkg->size()/1024;
826                                 pkgname += QString(" (%L1 kB)").arg(iAptGetCurrentFileTotalSize);
827                         }
828                         iAptGetDownloadCount++;
829                         oper += QString(" %1/%2").arg(iAptGetDownloadCount).arg(iAptGetInstallTotal);
830                         update = true;
831                         if( !iProgressCheckTimer ) {
832                                 iProgressCheckTimer = new QTimer(this);
833                                 connect(iProgressCheckTimer,SIGNAL(timeout()),this,SLOT(progressCheckTimerCallback()));
834                                 iProgressCheckTimer->start(500);
835                         }
836                         resetprogress = false;
837                 } else if( l.count()>=2 && l.at(0)=="Unpacking") {
838                         oper = "Installing";
839                         if( l.count()>=3 && l.at(1)=="replacement" )
840                                 pkgname = l.at(2);
841                         else
842                                 pkgname = l.at(1);
843                         iAptGetInstallCount++;
844                         oper += QString(" %1/%2").arg(iAptGetInstallCount).arg(iAptGetInstallTotal);
845                         update = true;
846                 } else if( l.count()>=3 && l.at(0)=="Setting" && l.at(1)=="up") {
847                         oper = "Setting up";
848                         pkgname = l.at(2);
849                 } else if( l.count()>=2 && l.at(0)=="Removing") {
850                         oper = "Removing";
851                         pkgname = l.at(1);
852                         iAptGetRemoveCount++;
853                         oper += QString(" %1/%2").arg(iAptGetRemoveCount).arg(iAptGetRemoveTotal);
854                         update = true;
855                 } else if( l.count()>=1 && l.at(0)=="Done!") {
856                         oper = "Setting up...";
857                         pkgname = "";
858                         update = true;
859                 }
860
861                 /*      // this does not seem to work, dpkg always dies first
862                 if( lines.at(i).startsWith("***") && lines.at(i).contains("(Y/I/N/O/D/Z)") ) {
863                         if( iMainWindow->confirmDialog("Overwrite configuration file?","la la la") )
864                         {
865                                 iProcAptGetInstall->write("Y\n");
866                         } else {
867                                 iProcAptGetInstall->write("N\n");
868                         }
869                 }
870                 */
871         }
872
873         if( update && iUiDimmer && iUiDimmer->busy() ) {
874                 iUiDimmer->updateText( oper + "<br>" + pkgname );
875                 if( resetprogress ) {
876                         iUiDimmer->setProgress(-1);
877                         if( iProgressCheckTimer ) {
878                                 iProgressCheckTimer->stop();
879                                 delete iProgressCheckTimer;
880                                 iProgressCheckTimer = 0;
881                         }
882                 }
883         }
884 }
885
886 void AAptInterface::progressCheckTimerCallback()
887 {
888         if( iAptGetCurrentDownloadFileName.isEmpty() )
889                 return;
890
891         qint64 prevsize = iAptGetCurrentFileDownloadSize;
892         QFile pkgfile(KAptArchivePartialDir + "/" + iAptGetCurrentDownloadFileName);
893         iAptGetCurrentFileDownloadSize = pkgfile.size()/1024;
894
895         if( iAptGetCurrentFileDownloadSize >= prevsize ) {
896                 iSpeedKbpsPrev = iSpeedKbps;
897                 iSpeedKbps = (iAptGetCurrentFileDownloadSize-prevsize)*2;
898         } else {
899                 iSpeedKbpsPrev = -1;
900         }
901         if( iUpdateSpeed )
902                 iUpdateSpeed = false;
903         else
904                 iUpdateSpeed = true;
905
906         if( iUiDimmer && iUiDimmer->busy() ) {
907                 int p = iAptGetCurrentFileDownloadSize*100/iAptGetCurrentFileTotalSize;
908                 if( iAptGetDownloadCount > 0 && iAptGetCurrentFileDownloadSize==0 )
909                         p = 100;
910                 iUiDimmer->setProgress( p );
911                 if( iSpeedKbps>=0 && iSpeedKbpsPrev>=0 && iUpdateSpeed ) {
912                         iUiDimmer->setDownloadSpeed( (iSpeedKbps+iSpeedKbpsPrev)/2 );
913                 }
914         }
915 }
916
917
918 void AAptInterface::communicateStatusToUi(bool success, QString title, QString msg)
919 {
920         qDebug() << title << msg;
921         iQueueMessages.append(msg);
922
923         if( iMainWindow && iOperationsQueue.count()==0 )
924         {
925                 // title comes from the last finished operation only
926                 iMainWindow->operationQueueFinished(iModeLog, success, title, iQueueMessages);
927         }
928 }
929
930 QByteArray AAptInterface::readLogFile()
931 {
932         QByteArray log = "";
933
934         QFile own(KLogFile);
935         if( own.open(QIODevice::ReadOnly | QIODevice::Text ) )
936         {
937                 while(!own.atEnd())
938                 {
939                         QByteArray line = own.readLine();
940                         log.append( line );
941                 }
942                 own.close();
943         }
944
945         if( log.isEmpty() )
946                 log = "The log is empty";
947
948         return log;
949 }
950
951 void AAptInterface::logToFile( QString data, bool logtime )
952 {
953         logToFile( data.toAscii(), logtime );
954 }
955
956 void AAptInterface::logToFile( QByteArray data, bool logtime )
957 {
958         QFile f(KLogFile);
959
960         if( f.open( QIODevice::Append | QIODevice::WriteOnly | QIODevice::Text ) )
961         {
962                 QTextStream out(&f);
963                 if( logtime )
964                         out << "--- " << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << " ---\n";
965
966                 out << data;
967
968                 if( logtime )
969                         out << "\n";
970                 f.close();
971         }
972 }
973
974 void AAptInterface::readRepositoryInfo()
975 {
976         for(int i=0; i<iRepositories.count(); i++) {
977                 if( iRepositories.at(i) )
978                         delete iRepositories.at(i);
979         }
980         iRepositories.clear();
981         bool ownFound = false;
982
983         QFile own( KOwnRepoFile );
984         if( own.open(QIODevice::ReadOnly | QIODevice::Text ) )
985         {
986                 Repository* r = 0;
987                 while(!own.atEnd())
988                 {
989                         QString line = own.readLine().trimmed();
990                         if( line.startsWith("deb ") || line.startsWith("#deb ") )
991                         {
992                                 r = new Repository();
993                                 if( r->setFromString(line) ) {
994                                         iRepositories.append(r);
995                                         //qDebug() << r->toListFileNames();
996                                 } else {
997                                         delete r;
998                                         r=0;
999                                 }
1000                         }
1001                 }
1002                 own.close();
1003                 if( iRepositories.count() > 0 )
1004                         ownFound = true;
1005         }
1006
1007         QFile names( KOwnRepoNamesFile );
1008         if( names.open(QIODevice::ReadOnly | QIODevice::Text ) )
1009         {
1010                 int c=0;
1011                 while(!names.atEnd() && c<iRepositories.count())
1012                 {
1013                         QString line = names.readLine().trimmed();
1014                         if( !line.trimmed().isEmpty() )
1015                         iRepositories.at(c)->setName( line.trimmed() );
1016                         c++;
1017                 }
1018                 names.close();
1019                 return;
1020         }
1021
1022         if( ownFound ) {
1023                 qDebug() << "own repo lists found";
1024                 return;
1025         }
1026
1027         QFile ham( KHamRepoListFile );
1028         if( ham.open(QIODevice::ReadOnly | QIODevice::Text ) )
1029         {
1030                 while(!ham.atEnd())
1031                 {
1032                         QString line = ham.readLine();
1033                         Repository* r = new Repository();
1034                         if( r->setFromString(line) ) {
1035                                 iRepositories.append(r);
1036
1037 #ifdef Q_WS_MAEMO_5
1038                                 // disable the ovi repository by default
1039                                 if( line.contains("https://downloads.maemo.nokia.com/fremantle1.2/ovi/") ) {
1040                                         r->setEnabled(false);
1041                                 }
1042 #endif
1043                         } else {
1044                                 delete r;
1045                                 r=0;
1046                         }
1047                 }
1048                 ham.close();
1049                 qDebug() << "system repo list loaded";
1050         }
1051
1052 }
1053
1054 bool AAptInterface::writeRepositories()
1055 {
1056         iNeedListRefresh = true;
1057
1058         QFile own( KOwnRepoFile );
1059         if( own.open(QIODevice::WriteOnly | QIODevice::Text ) )
1060         {
1061                 QTextStream out(&own);
1062                 for( int i=0; i<iRepositories.count(); i++ )
1063                         out << iRepositories.at(i)->toString() << "\n";
1064                 own.close();
1065         }
1066
1067         QFile names( KOwnRepoNamesFile );
1068         if( names.open(QIODevice::WriteOnly | QIODevice::Text ) )
1069         {
1070                 QTextStream out(&names);
1071                 for( int i=0; i<iRepositories.count(); i++ )
1072                         out << iRepositories.at(i)->name() << "\n";
1073                 names.close();
1074         }
1075
1076         QFile ham( KAptSourceList );
1077         if( ham.open(QIODevice::WriteOnly | QIODevice::Text ) )
1078         {
1079                 QTextStream out(&ham);
1080                 for( int i=0; i<iRepositories.count(); i++ )
1081                         out << iRepositories.at(i)->toString() << "\n";
1082                 ham.close();
1083
1084                 return true;
1085         } else {
1086                 qDebug() << "Failed to write repository list!";
1087         }
1088
1089         return false;
1090 }
1091
1092 bool AAptInterface::needRepoRefresh()
1093 {
1094         if( iNeedRepoRefresh || iSettings->qsettings()->value("need_repo_refresh", false).toBool() ) {
1095                 iNeedRepoRefresh = false;
1096                 iSettings->qsettings()->setValue("need_repo_refresh", false);
1097                 qDebug() << "repo update required, debug 1";
1098                 return true;
1099         }
1100
1101         QFile own(KAptSourceList);
1102
1103         if( !own.exists() )
1104         {
1105                 qDebug() << "repo update required, debug 2";
1106                 return true;
1107         }
1108
1109         QFileInfo a(KLastUpdateFile);
1110         QDateTime aDate;
1111         aDate.setTime_t(0);
1112         if( a.exists() ) {
1113                 aDate = a.lastModified();
1114         } else {
1115                 qDebug() << "repo update required, debug 3";
1116                 return true;
1117         }
1118         aDate = aDate.addSecs(24*60*60); //24h
1119
1120         if( aDate < QDateTime::currentDateTime() ) {
1121                 qDebug() << "repo update required, debug 4";
1122                 return true;
1123         }
1124
1125         qDebug() << "repo update not required";
1126         return false;
1127 }
1128
1129
1130
1131 void AAptInterface::startPkgListRead()
1132 {
1133         logToFile( QString("Start reading package lists") );
1134         qDebug() << "reading package list files";
1135
1136         if( !iNeedListRefresh && !iNeedDpkgRefresh ) {
1137                 qDebug() << "no need to refresh package lists";
1138                 logToFile( QString("No need to read package lists") );
1139                 communicateStatusToUi(true, "Operation finished", "Package data already up to date");
1140                 runNext();
1141                 return;
1142         }
1143
1144         if( iUiDimmer ) {
1145                 iUiDimmer->updateText("Reading package lists<br>");
1146                 iUiDimmer->setProgress(0);
1147         }
1148
1149 // clear packages lists
1150         if( iNeedListRefresh && !iSkipRefreshListAndDates )
1151         {
1152                 QHashIterator<QString, Package*> a( iPackagesAvailable );
1153                 while (a.hasNext())
1154                 {
1155                         a.next();
1156                         delete a.value();
1157                 }
1158                 iPackagesAvailable.clear();
1159         }
1160
1161         if( iNeedDpkgRefresh )
1162         {
1163                 QHashIterator<QString, Package*> i( iPackagesInstalled );
1164                 while (i.hasNext())
1165                 {
1166                         i.next();
1167                         delete i.value();
1168                 }
1169                 iPackagesInstalled.clear();
1170         }
1171
1172         // read apt database (available packages)
1173         QTime time_aptread;
1174         time_aptread.start();
1175
1176         int pkgcount_apt = 0;
1177         QDir dir( KAptListDir );
1178         QFileInfoList files = dir.entryInfoList();
1179
1180         quint64 totaldatasize = 0;
1181         quint64 currentreaddata = 0;
1182         quint64 lastupdatedata = 0;
1183         quint64 updateinterval = 2000000;
1184         if( iNeedListRefresh && !iSkipRefreshListAndDates ) {
1185                 for( int i=0; i<files.count(); i++ )
1186                 {
1187                         if( files.at(i).fileName().endsWith("_Packages"))
1188                                 totaldatasize += files.at(i).size();
1189                 }
1190         }
1191         if( iNeedDpkgRefresh ) {
1192                 QFileInfo dbinfo( KDpkgStatusFile );
1193                 totaldatasize += dbinfo.size();
1194         }
1195         int filecount = 0;
1196
1197         if( iNeedListRefresh && !iSkipRefreshListAndDates )
1198         {
1199                 iCanCancel = true;
1200
1201                 for( int i=0; i<files.count(); i++ )
1202                 {
1203                         Repository* currentRepo = 0;
1204                         if( files.at(i).absoluteFilePath().endsWith("_Packages") )
1205                         {
1206                                 filecount++;
1207                                 for(int x=0; x<iRepositories.count(); x++) {
1208                                         if( iRepositories.at(x)->toListFileNames().contains( files.at(i).fileName() ) ) {
1209                                                 currentRepo = iRepositories.at(x);
1210                                         }
1211                                 }
1212
1213                                 if( iUiDimmer && currentRepo ) {
1214                                         iUiDimmer->updateText("Reading package lists<br><font size=\"-1\">" + currentRepo->name() + "</font>");
1215                                 }
1216
1217                                 //qDebug() << files.at(i).fileName();
1218
1219                                 QFile db( files.at(i).absoluteFilePath() );
1220                                 if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) {
1221                                         qDebug() << "FAIL: Unable to read apt database";
1222                                         communicateStatusToUi(false, "Error", "Unable to read package lists");
1223                                         cleanAfterError();
1224                                         return;
1225                                 }
1226
1227                                 while (!db.atEnd() && !iTerminated) {
1228                                         Package* newpkg = ReadNextPackage(db, currentreaddata);
1229                                         if( iUiDimmer && currentreaddata >= lastupdatedata+updateinterval ) {
1230                                                 iUiDimmer->setProgress( currentreaddata*100/totaldatasize );
1231                                                 lastupdatedata = currentreaddata;
1232                                                 QApplication::processEvents();
1233                                         }
1234                                         pkgcount_apt++;
1235                                         if( newpkg )
1236                                         {
1237                                                 newpkg->addRepository( currentRepo );
1238
1239                                                 Package* exists = iPackagesAvailable.value(newpkg->name(),0);
1240                                                 if( !exists ) {
1241                                                         iPackagesAvailable.insert(newpkg->name(), newpkg);
1242                                                 } else {
1243                                                         if( Package::versionCompare(newpkg->version(),exists->version()) )
1244                                                         {
1245                                                                 iPackagesAvailable.remove(exists->name());
1246                                                                 delete exists;
1247                                                                 exists=0;
1248                                                                 iPackagesAvailable.insert(newpkg->name(), newpkg);
1249                                                         } else {
1250                                                                 if( newpkg->version() == exists->version() ) {
1251                                                                         exists->addRepository( currentRepo );
1252                                                                         if( newpkg->fullFileNames().count()>0 )
1253                                                                                 exists->addFullFileName( newpkg->fullFileNames().at(0) );
1254                                                                         else
1255                                                                                 exists->addFullFileName("unknown_dir/unknown_filename");
1256                                                                 }
1257                                                                 delete newpkg;
1258                                                                 newpkg=0;
1259                                                         }
1260                                                 }
1261                                         }
1262                                 }
1263                                 db.close();
1264                         }
1265                 }
1266
1267                 qDebug() << "apt database read took" << time_aptread.elapsed() << "ms";
1268                 qDebug() << "Processed" << filecount << "package list files";
1269
1270                 if( iTerminated ) {
1271                         if( iUiDimmer )
1272                                 iUiDimmer->setProgress(-1);
1273                         cleanAfterError();
1274                         communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1275                         return;
1276                 }
1277
1278                 iNeedListRefresh = false;
1279                 iNeedDateRefresh = true;
1280                 iLastListUpdate = QDateTime::currentDateTime();
1281         }
1282
1283 // read dpkg database (installed packages)
1284         if( iNeedDpkgRefresh )
1285         {
1286                 iCanCancel = true;
1287                 QTime time_dpkgread;
1288                 time_dpkgread.start();
1289
1290                 int pkgcount_dpkg = 0;
1291                 QFile db( KDpkgStatusFile );
1292                 if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) {
1293                         qDebug() << "FAIL: Unable to read dpkg database";
1294                         communicateStatusToUi(false, "Error", "Unable to read package database");
1295                         cleanAfterError();
1296                         return;
1297                 }
1298
1299                 if( iUiDimmer ) {
1300                         iUiDimmer->updateText("Reading package lists<br><font size=\"-1\">dpkg database</font>");
1301                 }
1302
1303                 while (!db.atEnd() && !iTerminated) {
1304                         Package* newpkg = ReadNextPackage(db, currentreaddata);
1305                         if( iUiDimmer && currentreaddata >= lastupdatedata+updateinterval ) {
1306                                 iUiDimmer->setProgress( currentreaddata*100/totaldatasize );
1307                                 lastupdatedata = currentreaddata;
1308                                 QApplication::processEvents();
1309                         }
1310                         pkgcount_dpkg++;
1311                         if( newpkg ) {
1312                                 if( newpkg->isInstalled() && !newpkg->name().isEmpty() ) {
1313                                         iPackagesInstalled.insert(newpkg->name(), newpkg);
1314                                 } else {
1315                                         delete newpkg;
1316                                         newpkg=0;
1317                                 }
1318                         }
1319                 }
1320                 db.close();
1321
1322                 qDebug() << "dpkg database read took" << time_dpkgread.elapsed() << "ms";
1323
1324                 qDebug() << "Processed" << pkgcount_apt << "(apt) and" << pkgcount_dpkg << "(dpkg) package entries";
1325                 qDebug() << "In DB:" << iPackagesAvailable.count() << "packages available,"
1326                                 << iPackagesInstalled.count() << "installed";
1327
1328                 if( iTerminated ) {
1329                         cleanAfterError();
1330                         communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1331                         return;
1332                 }
1333                 iNeedDpkgRefresh = false;
1334                 iLastDpkgUpdate = QDateTime::currentDateTime();
1335         }
1336
1337         logToFile( QString("Finished reading package lists") );
1338         if( iUiDimmer ) {
1339                 iUiDimmer->updateText("Reading package lists<br><font size=\"-1\">Creating package view</font>");
1340                 QApplication::processEvents();
1341                 iUiDimmer->setProgress(-1);
1342         }
1343
1344         readBlacklist();
1345         readPinnedPackages();
1346
1347         communicateStatusToUi(true, "Operation finished", "Package data read");
1348         iCanCancel = false;
1349         runNext();
1350 }
1351
1352 Package* AAptInterface::ReadNextPackage(QFile& f, quint64& currentreaddata)
1353 {
1354         iMultiLine=MultiLineNone;
1355
1356         Package* pkg = new Package("", this);
1357
1358         bool pkgready = false;
1359
1360         // this is faster than directly reading to QByteArray...
1361         QByteArray line;
1362         while( !pkgready && !f.atEnd() ) {
1363                 f.readLine(iDataReadBuffer,KDataReadBufferSize);
1364                 line = iDataReadBuffer;
1365                 currentreaddata += line.size();
1366                 if( processPackageDataLine(pkg,line) ) {
1367                         pkgready = true;
1368                 }
1369         }
1370
1371         if( !pkg->name().isEmpty() && pkg->isInstalled() ) {
1372                 QFileInfo f( KDpkgInfoDir + "/" + pkg->name() + ".list" );
1373                 if( f.exists() )
1374                         pkg->setDate( f.lastModified() );
1375         }
1376
1377         pkg->updateStatus();
1378
1379         if( pkg->name().isEmpty() ) {
1380                 qDebug() << "null name package!";
1381                 delete pkg;
1382                 pkg = 0;
1383         }
1384         return pkg;
1385 }
1386
1387 bool AAptInterface::processPackageDataLine(Package*& pkg, QByteArray& line)
1388 {
1389         if( line.isEmpty() || line=="\n" )
1390         {
1391                 return true;
1392         }
1393
1394         else if( iMultiLine == MultiLineDesc ) {
1395                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1396                         if( line.trimmed()!="." )
1397                                 pkg->appendDescLong( line.trimmed() + "\n" );
1398                         else
1399                                 pkg->appendDescLong( "\n" );
1400                 } else {
1401                         iMultiLine = MultiLineNone;
1402                 }
1403         }
1404         else if( iMultiLine == MultiLineIcon ) {
1405                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1406                         pkg->appendIconData( line.trimmed() );
1407                 } else {
1408                         iMultiLine = MultiLineNone;
1409                 }
1410         }
1411         else if( iMultiLine == MultiLineUpgradeDesc ) {
1412                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1413                         pkg->appendUpgradeDescription( line.trimmed() + "\n" );
1414                 } else {
1415                         iMultiLine = MultiLineNone;
1416                 }
1417         }
1418
1419         else if( line.startsWith("Package:") )
1420         {
1421                 pkg->setName( line.mid(8).trimmed() );
1422                 iMultiLine=MultiLineNone;
1423         }
1424         else if( line.startsWith("Status:") )
1425         {
1426                 if( line.mid(7).trimmed() == "install ok installed" )
1427                         pkg->setInstalled(true);
1428                 else
1429                         pkg->setInstalled(false);
1430         }
1431         else if( line.startsWith("Section:") )
1432         {
1433                 pkg->setSection( line.mid(8).trimmed() );
1434         }
1435         else if( line.startsWith("Version:") )
1436         {
1437                 pkg->setVersion( line.mid(8).trimmed() );
1438         }
1439         else if( line.startsWith("Filename:") )
1440         {
1441                 pkg->addFullFileName( line.mid(9).trimmed() );
1442         }
1443         else if( line.startsWith("Size:") )
1444         {
1445                 pkg->setSize( line.mid(5).trimmed().toInt() );
1446         }
1447         else if( line.startsWith("Installed-Size:") )
1448         {
1449                 pkg->setInstalledSize( line.mid(15).trimmed().toInt() );
1450         }
1451         else if( line.startsWith("Maemo-Display-Name:") )
1452         {
1453                 pkg->setMaemoDisplayName( line.mid(19).trimmed() );
1454         }
1455         else if( line.startsWith("Depends:") )
1456         {
1457                 pkg->appendDepends( line.mid(8).trimmed() );
1458         }
1459         else if( line.startsWith("Conflicts:") )
1460         {
1461                 pkg->appendConflicts( line.mid(10).trimmed() );
1462         }
1463         else if( line.startsWith("Pre-Depends:") )
1464         {
1465                 pkg->appendPreDepends( line.mid(12).trimmed() );
1466         }
1467         else if( line.startsWith("Replaces:") )
1468         {
1469                 pkg->appendReplaces( line.mid(9).trimmed() );
1470         }
1471         else if( line.startsWith("Recommends:") )
1472         {
1473                 pkg->appendRecommends( line.mid(11).trimmed() );
1474         }
1475         else if( line.startsWith("Suggests:") )
1476         {
1477                 pkg->appendSuggests( line.mid(9).trimmed() );
1478         }
1479         else if( line.startsWith("Provides:") )
1480         {
1481                 pkg->appendProvides( line.mid(9).trimmed() );
1482         }
1483         else if( line.startsWith("Breaks:") )
1484         {
1485                 pkg->appendBreaks( line.mid(7).trimmed() );
1486         }
1487         else if( line.startsWith("Maintainer:") )
1488         {
1489                 pkg->setMaintainer( line.mid(11).trimmed() );
1490         }
1491
1492         else if( line.startsWith("Description:") )
1493         {
1494                 pkg->setDescShort( line.mid(12).trimmed() );
1495                 iMultiLine = MultiLineDesc;
1496         }
1497         else if( line.startsWith("Maemo-Icon-26:") )
1498         {
1499                 if( line.mid(15).trimmed() != "" ) {
1500                         pkg->appendIconData( line.mid(15).trimmed() );
1501                 }
1502                 iMultiLine = MultiLineIcon;
1503         }
1504         else if( line.startsWith("Maemo-Upgrade-Description:") )
1505         {
1506                 pkg->appendUpgradeDescription( line.mid(26).trimmed() + "\n" );
1507                 iMultiLine = MultiLineUpgradeDesc;
1508         }
1509
1510         return false;
1511 }
1512
1513
1514
1515 void AAptInterface::writeBlacklist()
1516 {
1517         QHashIterator<QString, Package*> i( iPackagesAvailable );
1518         while (i.hasNext())
1519         {
1520                 i.next();
1521
1522                 if( i.value()->blacklisted() == BlacklistSelect::BlacklistAll ) {
1523                         iBlacklist << i.value()->name();
1524                 }
1525                 else if( i.value()->blacklisted() == BlacklistSelect::BlacklistThis ) {
1526                         iBlacklist << (i.value()->name() + " " + i.value()->version());
1527                 }
1528         }
1529
1530         QHashIterator<QString, Package*> j( iPackagesInstalled );
1531         while (j.hasNext())
1532         {
1533                 j.next();
1534
1535                 if( j.value()->blacklisted() == BlacklistSelect::BlacklistAll ) {
1536                         iBlacklist << j.value()->name();
1537                 }
1538                 else if( j.value()->blacklisted() == BlacklistSelect::BlacklistThis ) {
1539                         iBlacklist << (j.value()->name() + " " + j.value()->version());
1540                 }
1541         }
1542
1543         iBlacklist.removeDuplicates();
1544
1545         QFile f( KBlacklistFile );
1546         if( f.open(QIODevice::WriteOnly | QIODevice::Text ) )
1547         {
1548                 QTextStream out(&f);
1549                 for( int i=0; i<iBlacklist.count(); i++ )
1550                         out << iBlacklist.at(i) << "\n";
1551                 f.close();
1552         }
1553
1554         qDebug() << "blacklist: wrote" << iBlacklist.count() << "entries";
1555 }
1556
1557 void AAptInterface::readBlacklist()
1558 {
1559         QFile f( KBlacklistFile );
1560         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1561         {
1562                 while( !f.atEnd() ) {
1563                         QString line = f.readLine().trimmed();
1564                         if( line != "" )
1565                                 iBlacklist.append(line);
1566                 }
1567                 f.close();
1568         }
1569
1570         iBlacklist.removeDuplicates();
1571
1572         qDebug() << "blacklist: read" << iBlacklist.count() << "entries";
1573
1574         for( int i=0; i<iBlacklist.count(); i++ )
1575         {
1576                 QStringList parts = iBlacklist.at(i).split(' ');
1577                 Package* pkg = iPackagesAvailable.value(parts.at(0).trimmed(), 0);
1578                 Package* pkg2 = iPackagesInstalled.value(parts.at(0).trimmed(), 0);
1579                 if( parts.count()==1 ) {
1580                         if( pkg )
1581                                 pkg->setBlacklisted(BlacklistSelect::BlacklistAll);
1582                         if( pkg2 )
1583                                 pkg2->setBlacklisted(BlacklistSelect::BlacklistAll);
1584                 } else if( parts.count()==2 ) {
1585                         if( pkg && pkg->version()==parts.at(1) )
1586                                 pkg->setBlacklisted(BlacklistSelect::BlacklistThis);
1587                         if( pkg2 && pkg2->version()==parts.at(1) )
1588                                 pkg2->setBlacklisted(BlacklistSelect::BlacklistThis);
1589                 } else {
1590                         qDebug() << "Warning: invalid blacklist entry:" << iBlacklist.at(i);
1591                 }
1592         }
1593 }
1594
1595 void AAptInterface::removeFromBlacklist(Package *pkg, BlacklistSelect::blackList oldstate)
1596 {
1597         if( !pkg ) {
1598                 qDebug() << "Warning: trying to remove null package from blacklist";
1599                 return;
1600         }
1601
1602         bool removed = false;
1603
1604         for( int i=0; i<iBlacklist.count(); i++ )
1605         {
1606                 if( oldstate == BlacklistSelect::BlacklistAll )
1607                 {
1608                         if( iBlacklist.at(i) == pkg->name() ) {
1609                                 iBlacklist.removeAt(i);
1610                                 i--;
1611                                 removed = true;
1612                         }
1613                 } else if( oldstate == BlacklistSelect::BlacklistThis ) {
1614                         if( iBlacklist.at(i) == (pkg->name()+" "+pkg->version()) ) {
1615                                 iBlacklist.removeAt(i);
1616                                 i--;
1617                                 removed = true;
1618                         }
1619                 }
1620         }
1621
1622         if( removed )
1623                 qDebug() << "blacklist: removed" << pkg->name();
1624         else
1625                 qDebug() << "blacklist:" << pkg->name() << "not in saved list";
1626
1627 }
1628
1629
1630 void AAptInterface::startFetchDates()
1631 {
1632         logToFile( QString("Start fetching package dates") );
1633         qDebug() << "start fetching package dates";
1634
1635         if( !iNeedDateRefresh || iSkipRefreshListAndDates ) {
1636                 qDebug() << "no need to fetch dates";
1637                 logToFile( QString("No need to fetch dates") );
1638                 communicateStatusToUi(true, "Operation finished", "Date information already up to date");
1639                 runNext();
1640                 return;
1641         }
1642
1643         if( iUiDimmer ) {
1644                 iUiDimmer->updateText("Reading date cache");
1645                 iUiDimmer->setProgress(0);
1646                 QApplication::processEvents();
1647         }
1648
1649         readDateCache();
1650
1651         if( iUiDimmer ) {
1652                 iUiDimmer->updateText("Fetching package date information");
1653                 QApplication::processEvents();
1654         }
1655
1656         QNetworkAccessManager* nam = new QNetworkAccessManager(this);
1657
1658         if( iSettings->qsettings()->value("use_proxies").toBool() && !iSettings->qsettings()->value("http_proxy").toString().isEmpty() )
1659         {
1660                  QNetworkProxy proxy = Settings::createProxyFromString( iSettings->qsettings()->value("http_proxy").toString() );
1661                  nam->setProxy(proxy);
1662         }
1663
1664         iCanCancel = true;
1665
1666         int count = 0;
1667         int updProgress = 0;
1668
1669         QHash<QString, Package*> fetchable;
1670         QHashIterator<QString, Package*> i( iPackagesAvailable );
1671         while (i.hasNext() )
1672         {
1673                 i.next();
1674                 if( !i.value()->date().isValid() && i.value()->section().startsWith("user/") && !i.value()->isBlacklisted() )
1675                 {
1676                         const Repository* repo = 0;
1677                         for( int x=0; x<i.value()->repositories().count(); x++ ) {
1678                                 if( i.value()->repositories().at(x) && i.value()->repositories().at(x)->url().startsWith("http://repository.maemo.org") )
1679                                 {
1680                                         repo = i.value()->repositories().at(x);
1681                                         break;
1682                                 }
1683                         }
1684                         if( repo ) {
1685                                 fetchable.insert(i.value()->name(), i.value());
1686                         }
1687                 }
1688         }
1689
1690         connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(dateFetchNetworkReply(QNetworkReply*)));
1691         iDateRequestsWaiting = 0;
1692         iDateRequestsSent = 0;
1693         iDateRequestsReceived = 0;
1694         iNetworkError = QNetworkReply::NoError;
1695
1696         QString dbgcount = QString("need to fetch date for %1 packages").arg(fetchable.count());
1697         qDebug() << dbgcount;
1698         logToFile(dbgcount);
1699
1700         QHashIterator<QString, Package*> fe( fetchable );
1701         while (fe.hasNext() && !iTerminated)
1702         {
1703                 fe.next();
1704
1705                 if( updProgress >=20 ) {
1706                         iUiDimmer->setProgress( count*100/fetchable.count() );
1707                         updProgress=0;
1708                 }
1709
1710                 if( !fe.value()->date().isValid() && fe.value()->section().startsWith("user/") && !fe.value()->isBlacklisted() )
1711                 {
1712                         QString url;
1713                         const Repository* repo = 0;
1714                         QString fullFilename = "unknown_filename";
1715                         for( int x=0; x<fe.value()->repositories().count(); x++ ) {
1716                                 if( fe.value()->repositories().at(x) && fe.value()->repositories().at(x)->url().startsWith("http://repository.maemo.org") )
1717                                 {
1718                                         repo = fe.value()->repositories().at(x);
1719                                         if( repo && fe.value()->fullFileNames().count()>x )
1720                                                 fullFilename = fe.value()->fullFileNames().at(x);
1721                                         break;
1722                                 }
1723                         }
1724
1725                         if( repo )
1726                         {
1727                                 url = repo->url() + repo->dir() + fullFilename + "#" + fe.value()->name();
1728
1729                                 //qDebug() << "getting date for" << fe.value()->name();
1730                                 //qDebug() << url;
1731
1732                                 QUrl u(url);
1733                                 QNetworkRequest r(u);
1734
1735                                 if( iDateRequestsReceived == 0 ) {
1736                                         while( iDateRequestsWaiting>0 ) {
1737                                                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1738                                         }
1739                                 } else {
1740                                         while( iDateRequestsWaiting>5 ) {
1741                                                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1742                                         }
1743                                 }
1744
1745                                 if( iDateRequestsReceived>0 && iNetworkError != QNetworkReply::NoError &&
1746                                         iNetworkError != QNetworkReply::QNetworkReply::ContentAccessDenied && iNetworkError != QNetworkReply::QNetworkReply::ContentNotFoundError )
1747                                 {
1748                                         qDebug() << "fatal network error, aborting fetch";
1749                                         logToFile(QString("Fatal network error, date fetch aborted"));
1750                                         break;
1751                                 } else {
1752                                         nam->head(r);
1753                                         iDateRequestsSent++;
1754                                         iDateRequestsWaiting++;
1755                                 }
1756                         }
1757                         count = iDateRequestsReceived;
1758                         updProgress++;
1759                 }
1760         }
1761         while( iDateRequestsWaiting>0 ) {
1762                 if( updProgress >=20 ) {
1763                         iUiDimmer->setProgress( count*100/fetchable.count() );
1764                         updProgress=0;
1765                 }
1766                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1767                 count = iDateRequestsReceived;
1768                 updProgress++;
1769         }
1770         delete nam;
1771         nam=0;
1772
1773         if( iDateRequestsReceived>0 && iNetworkError != QNetworkReply::NoError &&
1774                 iNetworkError != QNetworkReply::QNetworkReply::ContentAccessDenied && iNetworkError != QNetworkReply::QNetworkReply::ContentNotFoundError )
1775         {
1776                 // don't stop on this error, only inform the user
1777                 iMainWindow->notifyDialog("Network error", "There was a network error while fetching date information, the fetch was aborted");
1778         }
1779
1780         if( iTerminated ) {
1781                 if( iUiDimmer )
1782                         iUiDimmer->setProgress(-1);
1783                 cleanAfterError();
1784                 communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1785                 return;
1786         }
1787         iCanCancel = false;
1788
1789         if( iUiDimmer ) {
1790                 iUiDimmer->setProgress(100);
1791                 QApplication::processEvents();
1792         }
1793
1794         QString dbgstr = QString("sent %1 requests, received %2 replies with %3 errors").arg(iDateRequestsSent).arg(iDateRequestsReceived).arg(iDateRequestErrors);
1795
1796         qDebug() << dbgstr;
1797         logToFile(dbgstr);
1798
1799         logToFile( QString("Finished fetching package dates") );
1800
1801         if( fetchable.count()>0 || !dateCacheExists() ) {
1802                 iUiDimmer->updateText("Writing date cache");
1803                 QApplication::processEvents();
1804                 writeDateCache();
1805         }
1806
1807         if( iUiDimmer ) {
1808                 iUiDimmer->updateText("Creating package view");
1809                 QApplication::processEvents();
1810                 iUiDimmer->setProgress(-1);
1811         }
1812
1813         communicateStatusToUi(true, "Operation finished", "Package dates fetched");
1814         iNeedDateRefresh = false;
1815         runNext();
1816 }
1817
1818 void AAptInterface::dateFetchNetworkReply(QNetworkReply* reply)
1819 {
1820         iDateRequestsWaiting--;
1821         iDateRequestsReceived++;
1822         iNetworkError = reply->error();
1823         QDateTime dateModified = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
1824
1825         if( reply->error() == QNetworkReply::NoError && dateModified.isValid() )
1826         {
1827                 QString pkgname = reply->url().fragment();
1828                 //qDebug() << pkgname;
1829
1830                 if( pkgname.isEmpty() )
1831                         qDebug() << "warning: empty packagename in reply";
1832
1833                 Package* pkg = iPackagesAvailable.value(pkgname, 0);
1834                 if( pkg ) {
1835                         pkg->setDate( dateModified );
1836                 } else {
1837                         qDebug() << "warning: unknown packagename in reply:" << pkgname;
1838                 }
1839         }
1840
1841         if( reply->error() != QNetworkReply::NoError ) {
1842                 QString dbg = reply->url().fragment() + QString(": error %1: ").arg(reply->error()) + reply->errorString();
1843                 qDebug() << dbg;
1844                 logToFile( dbg );
1845                 iDateRequestErrors++;
1846         }
1847         reply->deleteLater();
1848 }
1849
1850 void AAptInterface::writeDateCache()
1851 {
1852         qDebug() << "writing date cache";
1853
1854         QFile f(KDateCacheFile);
1855         if( f.open(QIODevice::WriteOnly | QIODevice::Text ) )
1856         {
1857                 QTextStream out(&f);
1858                 QHashIterator<QString, Package*> i( iPackagesAvailable );
1859                 while (i.hasNext() )
1860                 {
1861                         i.next();
1862                         if( i.value()->date().isValid() && i.value()->section().startsWith("user/") )
1863                                 out << i.value()->name() << " " << i.value()->version() << " " << i.value()->date().toString(Qt::ISODate) << "\n";
1864                 }
1865                 f.close();
1866         } else {
1867                 qDebug() << "Warning: failed to write date cache";
1868                 logToFile(QString("Failed to write date cache"));
1869         }
1870 }
1871
1872 void AAptInterface::readDateCache()
1873 {
1874         qDebug() << "reading date cache";
1875
1876         QFile f(KDateCacheFile);
1877         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1878         {
1879                 while( !f.atEnd() ) {
1880                         QString line = f.readLine().trimmed();
1881                         QStringList parts = line.split(' ');
1882                         if( parts.count()==3 ) {
1883                                 Package* pkg = iPackagesAvailable.value(parts.at(0),0);
1884                                 if( pkg && pkg->section().startsWith("user/") && pkg->version()==parts.at(1) )
1885                                 {
1886                                         QDateTime dt = QDateTime::fromString( parts.at(2), Qt::ISODate);
1887                                         if( dt.isValid() ) {
1888                                                 pkg->setDate( dt );
1889                                         } else {
1890                                                 qDebug() << "Warning: Invalid date in date cache";
1891                                                 logToFile(QString("Invalid date in date cache"));
1892                                         }
1893                                 }
1894                         } else {
1895                                 qDebug() << "Warning: error in date cache:" << line;
1896                                 logToFile(QString("Error in date cache"));
1897                         }
1898                 }
1899                 f.close();
1900         } else {
1901                 qDebug() << "date cache does not exist";
1902         }
1903 }
1904
1905 bool AAptInterface::dateCacheExists()
1906 {
1907         QFileInfo f(KDateCacheFile);
1908         return f.exists();
1909 }
1910
1911 void AAptInterface::readPinnedPackages()
1912 {
1913         QFile f(KAptPreferencesFile);
1914         if( !f.exists() )
1915                 return;
1916
1917         bool warnAllPinned = false;
1918         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1919         {
1920                 qDebug() << "apt preferences exist: reading pinned packages";
1921                 int pinned_packages = 0;
1922                 while( !f.atEnd() )
1923                 {
1924                         QString line = f.readLine().trimmed();
1925
1926                         if( line=="Package: *" || line=="Package:*")
1927                                 warnAllPinned = true;
1928
1929                         if( line.startsWith("Package:") ) {
1930                                 pinned_packages++;
1931                                 QString pkg = line.mid(8).trimmed();
1932                                 Package* pkg_i = iPackagesInstalled.value(pkg,0);
1933                                 if( pkg_i ) {
1934                                         pkg_i->setPinned(true);
1935                                 }
1936                                 Package* pkg_a = iPackagesAvailable.value(pkg,0);
1937                                 if( pkg_a ) {
1938                                         pkg_a->setPinned(true);
1939                                 }
1940                         }
1941                 }
1942                 f.close();
1943                 qDebug() << "read" << pinned_packages << "pinned packages";
1944         }
1945
1946         if( warnAllPinned ) {
1947                 iMainWindow->notifyDialog("Warning","You have pinned packages with '*' in apt preferences. It is strongly recommended to "
1948                                                                   "remove such settings as they can result in unexpected behavior of Faster Application Manager.");
1949         }
1950 }
1951
1952 bool AAptInterface::loadInstallFiles(QStringList files_)
1953 {
1954         qDebug() << files_;
1955
1956         return false;
1957 }