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