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