3d63feb3486ce0928f34acfccece56707c7b0694
[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.isEmpty() )
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.isEmpty() )
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().isEmpty() )
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 = 2000000;
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                                         if( iUiDimmer && currentreaddata >= lastupdatedata+updateinterval ) {
1187                                                 iUiDimmer->setProgress( currentreaddata*100/totaldatasize );
1188                                                 lastupdatedata = currentreaddata;
1189                                                 QApplication::processEvents();
1190                                         }
1191                                         pkgcount_apt++;
1192                                         if( newpkg )
1193                                         {
1194                                                 newpkg->addRepository( currentRepo );
1195
1196                                                 Package* exists = iPackagesAvailable.value(newpkg->name(),0);
1197                                                 if( !exists ) {
1198                                                         iPackagesAvailable.insert(newpkg->name(), newpkg);
1199                                                 } else {
1200                                                         if( Package::versionCompare(newpkg->version(),exists->version()) )
1201                                                         {
1202                                                                 iPackagesAvailable.remove(exists->name());
1203                                                                 delete exists;
1204                                                                 exists=0;
1205                                                                 iPackagesAvailable.insert(newpkg->name(), newpkg);
1206                                                         } else {
1207                                                                 if( newpkg->version() == exists->version() ) {
1208                                                                         exists->addRepository( currentRepo );
1209                                                                         if( newpkg->fullFileNames().count()>0 )
1210                                                                                 exists->addFullFileName( newpkg->fullFileNames().at(0) );
1211                                                                         else
1212                                                                                 exists->addFullFileName("unknown_dir/unknown_filename");
1213                                                                 }
1214                                                                 delete newpkg;
1215                                                                 newpkg=0;
1216                                                         }
1217                                                 }
1218                                         }
1219                                 }
1220                                 db.close();
1221                         }
1222                 }
1223
1224                 qDebug() << "apt database read took" << time_aptread.elapsed() << "ms";
1225                 qDebug() << "Processed" << filecount << "package list files";
1226
1227                 if( iTerminated ) {
1228                         if( iUiDimmer )
1229                                 iUiDimmer->setProgress(-1);
1230                         cleanAfterError();
1231                         communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1232                         return;
1233                 }
1234
1235                 iNeedListRefresh = false;
1236                 iNeedDateRefresh = true;
1237                 iLastListUpdate = QDateTime::currentDateTime();
1238         }
1239
1240 // read dpkg database (installed packages)
1241         if( iNeedDpkgRefresh )
1242         {
1243                 iCanCancel = true;
1244                 QTime time_dpkgread;
1245                 time_dpkgread.start();
1246
1247                 int pkgcount_dpkg = 0;
1248                 QFile db( KDpkgStatusFile );
1249                 if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) {
1250                         qDebug() << "FAIL: Unable to read dpkg database";
1251                         communicateStatusToUi(false, "Error", "Unable to read package database");
1252                         cleanAfterError();
1253                         return;
1254                 }
1255
1256                 if( iUiDimmer ) {
1257                         iUiDimmer->updateText("Reading package lists<br><font size=\"-1\">dpkg database</font>");
1258                 }
1259
1260                 while (!db.atEnd() && !iTerminated) {
1261                         Package* newpkg = ReadNextPackage(db, currentreaddata);
1262                         if( iUiDimmer && currentreaddata >= lastupdatedata+updateinterval ) {
1263                                 iUiDimmer->setProgress( currentreaddata*100/totaldatasize );
1264                                 lastupdatedata = currentreaddata;
1265                                 QApplication::processEvents();
1266                         }
1267                         pkgcount_dpkg++;
1268                         if( newpkg ) {
1269                                 if( newpkg->isInstalled() && !newpkg->name().isEmpty() ) {
1270                                         iPackagesInstalled.insert(newpkg->name(), newpkg);
1271                                 } else {
1272                                         delete newpkg;
1273                                         newpkg=0;
1274                                 }
1275                         }
1276                 }
1277                 db.close();
1278
1279                 qDebug() << "dpkg database read took" << time_dpkgread.elapsed() << "ms";
1280
1281                 qDebug() << "Processed" << pkgcount_apt << "(apt) and" << pkgcount_dpkg << "(dpkg) package entries";
1282                 qDebug() << "In DB:" << iPackagesAvailable.count() << "packages available,"
1283                                 << iPackagesInstalled.count() << "installed";
1284
1285                 if( iTerminated ) {
1286                         cleanAfterError();
1287                         communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1288                         return;
1289                 }
1290                 iNeedDpkgRefresh = false;
1291                 iLastDpkgUpdate = QDateTime::currentDateTime();
1292         }
1293
1294         logToFile( QString("Finished reading package lists") );
1295         if( iUiDimmer ) {
1296                 iUiDimmer->updateText("Reading package lists<br><font size=\"-1\">Creating package view</font>");
1297                 QApplication::processEvents();
1298                 iUiDimmer->setProgress(-1);
1299         }
1300
1301         readBlacklist();
1302
1303         communicateStatusToUi(true, "Operation finished", "Package data read");
1304         iCanCancel = false;
1305         runNext();
1306 }
1307
1308 Package* AAptInterface::ReadNextPackage(QFile& f, quint64& currentreaddata)
1309 {
1310         iMultiLine=MultiLineNone;
1311
1312         Package* pkg = new Package("", this);
1313
1314         bool pkgready = false;
1315
1316         // this is faster than directly reading to QByteArray...
1317         char* data = new char[25000];
1318         QByteArray line;
1319         while( !pkgready && !f.atEnd() ) {
1320                 f.readLine(data,25000);
1321                 line = data;
1322                 currentreaddata += line.size();
1323                 if( processPackageDataLine(pkg,line) ) {
1324                         pkgready = true;
1325                 }
1326         }
1327         delete data;
1328
1329         if( !pkg->name().isEmpty() && pkg->isInstalled() ) {
1330                 QFileInfo f( KDpkgInfoDir + "/" + pkg->name() + ".list" );
1331                 if( f.exists() )
1332                         pkg->setDate( f.lastModified() );
1333         }
1334
1335         pkg->updateStatus();
1336
1337         if( pkg->name().isEmpty() ) {
1338                 qDebug() << "null name package!";
1339                 delete pkg;
1340                 pkg = 0;
1341         }
1342         return pkg;
1343 }
1344
1345 bool AAptInterface::processPackageDataLine(Package*& pkg, QByteArray& line)
1346 {
1347         if( line.isEmpty() || line=="\n" )
1348         {
1349                 return true;
1350         }
1351
1352         else if( iMultiLine == MultiLineDesc ) {
1353                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1354                         if( line.trimmed()!="." )
1355                                 pkg->appendDescLong( line.trimmed() + "\n" );
1356                         else
1357                                 pkg->appendDescLong( "\n" );
1358                 } else {
1359                         iMultiLine = MultiLineNone;
1360                 }
1361         }
1362         else if( iMultiLine == MultiLineIcon ) {
1363                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1364                         pkg->appendIconData( line.trimmed() );
1365                 } else {
1366                         iMultiLine = MultiLineNone;
1367                 }
1368         }
1369         else if( iMultiLine == MultiLineUpgradeDesc ) {
1370                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1371                         pkg->appendUpgradeDescription( line.trimmed() + "\n" );
1372                 } else {
1373                         iMultiLine = MultiLineNone;
1374                 }
1375         }
1376
1377         else if( line.startsWith("Package:") )
1378         {
1379                 pkg->setName( line.mid(8).trimmed() );
1380                 iMultiLine=MultiLineNone;
1381         }
1382         else if( line.startsWith("Status:") )
1383         {
1384                 if( line.mid(7).trimmed() == "install ok installed" )
1385                         pkg->setInstalled(true);
1386                 else
1387                         pkg->setInstalled(false);
1388         }
1389         else if( line.startsWith("Section:") )
1390         {
1391                 pkg->setSection( line.mid(8).trimmed() );
1392         }
1393         else if( line.startsWith("Version:") )
1394         {
1395                 pkg->setVersion( line.mid(8).trimmed() );
1396         }
1397         else if( line.startsWith("Filename:") )
1398         {
1399                 pkg->addFullFileName( line.mid(9).trimmed() );
1400         }
1401         else if( line.startsWith("Size:") )
1402         {
1403                 pkg->setSize( line.mid(5).trimmed().toInt() );
1404         }
1405         else if( line.startsWith("Installed-Size:") )
1406         {
1407                 pkg->setInstalledSize( line.mid(15).trimmed().toInt() );
1408         }
1409         else if( line.startsWith("Maemo-Display-Name:") )
1410         {
1411                 pkg->setMaemoDisplayName( line.mid(19).trimmed() );
1412         }
1413         else if( line.startsWith("Depends:") )
1414         {
1415                 pkg->appendDepends( line.mid(8).trimmed() );
1416         }
1417         else if( line.startsWith("Conflicts:") )
1418         {
1419                 pkg->appendConflicts( line.mid(10).trimmed() );
1420         }
1421         else if( line.startsWith("Pre-Depends:") )
1422         {
1423                 pkg->appendPreDepends( line.mid(12).trimmed() );
1424         }
1425         else if( line.startsWith("Replaces:") )
1426         {
1427                 pkg->appendReplaces( line.mid(9).trimmed() );
1428         }
1429         else if( line.startsWith("Recommends:") )
1430         {
1431                 pkg->appendRecommends( line.mid(11).trimmed() );
1432         }
1433         else if( line.startsWith("Suggests:") )
1434         {
1435                 pkg->appendSuggests( line.mid(9).trimmed() );
1436         }
1437         else if( line.startsWith("Provides:") )
1438         {
1439                 pkg->appendProvides( line.mid(9).trimmed() );
1440         }
1441         else if( line.startsWith("Breaks:") )
1442         {
1443                 pkg->appendBreaks( line.mid(7).trimmed() );
1444         }
1445
1446         else if( line.startsWith("Description:") )
1447         {
1448                 pkg->setDescShort( line.mid(12).trimmed() );
1449                 iMultiLine = MultiLineDesc;
1450         }
1451         else if( line.startsWith("Maemo-Icon-26:") )
1452         {
1453                 if( line.mid(15).trimmed() != "" ) {
1454                         pkg->appendIconData( line.mid(15).trimmed() );
1455                 }
1456                 iMultiLine = MultiLineIcon;
1457         }
1458         else if( line.startsWith("Maemo-Upgrade-Description:") )
1459         {
1460                 pkg->appendUpgradeDescription( line.mid(26).trimmed() + "\n" );
1461                 iMultiLine = MultiLineUpgradeDesc;
1462         }
1463
1464         return false;
1465 }
1466
1467
1468
1469 void AAptInterface::writeBlacklist()
1470 {
1471         QHashIterator<QString, Package*> i( iPackagesAvailable );
1472         while (i.hasNext())
1473         {
1474                 i.next();
1475
1476                 if( i.value()->blacklisted() == BlacklistSelect::BlacklistAll ) {
1477                         iBlacklist << i.value()->name();
1478                 }
1479                 else if( i.value()->blacklisted() == BlacklistSelect::BlacklistThis ) {
1480                         iBlacklist << (i.value()->name() + " " + i.value()->version());
1481                 }
1482         }
1483
1484         QHashIterator<QString, Package*> j( iPackagesInstalled );
1485         while (j.hasNext())
1486         {
1487                 j.next();
1488
1489                 if( j.value()->blacklisted() == BlacklistSelect::BlacklistAll ) {
1490                         iBlacklist << j.value()->name();
1491                 }
1492                 else if( j.value()->blacklisted() == BlacklistSelect::BlacklistThis ) {
1493                         iBlacklist << (j.value()->name() + " " + j.value()->version());
1494                 }
1495         }
1496
1497         iBlacklist.removeDuplicates();
1498
1499         QFile f( KBlacklistFile );
1500         if( f.open(QIODevice::WriteOnly | QIODevice::Text ) )
1501         {
1502                 QTextStream out(&f);
1503                 for( int i=0; i<iBlacklist.count(); i++ )
1504                         out << iBlacklist.at(i) << "\n";
1505                 f.close();
1506         }
1507
1508         qDebug() << "blacklist: wrote" << iBlacklist.count() << "entries";
1509 }
1510
1511 void AAptInterface::readBlacklist()
1512 {
1513         QFile f( KBlacklistFile );
1514         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1515         {
1516                 while( !f.atEnd() ) {
1517                         QString line = f.readLine().trimmed();
1518                         if( line != "" )
1519                                 iBlacklist.append(line);
1520                 }
1521                 f.close();
1522         }
1523
1524         iBlacklist.removeDuplicates();
1525
1526         qDebug() << "blacklist: read" << iBlacklist.count() << "entries";
1527
1528         for( int i=0; i<iBlacklist.count(); i++ )
1529         {
1530                 QStringList parts = iBlacklist.at(i).split(' ');
1531                 Package* pkg = iPackagesAvailable.value(parts.at(0).trimmed(), 0);
1532                 Package* pkg2 = iPackagesInstalled.value(parts.at(0).trimmed(), 0);
1533                 if( parts.count()==1 ) {
1534                         if( pkg )
1535                                 pkg->setBlacklisted(BlacklistSelect::BlacklistAll);
1536                         if( pkg2 )
1537                                 pkg2->setBlacklisted(BlacklistSelect::BlacklistAll);
1538                 } else if( parts.count()==2 ) {
1539                         if( pkg && pkg->version()==parts.at(1) )
1540                                 pkg->setBlacklisted(BlacklistSelect::BlacklistThis);
1541                         if( pkg2 && pkg2->version()==parts.at(1) )
1542                                 pkg2->setBlacklisted(BlacklistSelect::BlacklistThis);
1543                 } else {
1544                         qDebug() << "Warning: invalid blacklist entry:" << iBlacklist.at(i);
1545                 }
1546         }
1547 }
1548
1549 void AAptInterface::removeFromBlacklist(Package *pkg, BlacklistSelect::blackList oldstate)
1550 {
1551         if( !pkg ) {
1552                 qDebug() << "Warning: trying to remove null package from blacklist";
1553                 return;
1554         }
1555
1556         QStringList newlist;
1557         bool removed = false;
1558
1559         for( int i=0; i<iBlacklist.count(); i++ )
1560         {
1561                 if( oldstate == BlacklistSelect::BlacklistAll )
1562                 {
1563                         if( !(iBlacklist.at(i) == pkg->name()) ) {
1564                                 newlist << iBlacklist.at(i);
1565                         } else removed = true;
1566                 } else if( oldstate == BlacklistSelect::BlacklistThis ) {
1567                         if( !(iBlacklist.at(i) == (pkg->name()+" "+pkg->version())) ) {
1568                                 newlist << iBlacklist.at(i);
1569                         } else removed = true;
1570                 }
1571         }
1572
1573         if( removed )
1574                 qDebug() << "blacklist: removed" << pkg->name();
1575         else
1576                 qDebug() << "blacklist:" << pkg->name() << "not in saved list";
1577
1578         iBlacklist = newlist;
1579 }
1580
1581
1582 void AAptInterface::startFetchDates()
1583 {
1584         logToFile( QString("Start fetching package dates") );
1585         qDebug() << "start fetching package dates";
1586
1587         if( !iNeedDateRefresh || iSkipRefreshListAndDates ) {
1588                 qDebug() << "no need to fetch dates";
1589                 logToFile( QString("No need to fetch dates") );
1590                 communicateStatusToUi(true, "Operation finished", "Date information already up to date");
1591                 runNext();
1592                 return;
1593         }
1594
1595         if( iUiDimmer ) {
1596                 iUiDimmer->updateText("Reading date cache");
1597                 iUiDimmer->setProgress(0);
1598                 QApplication::processEvents();
1599         }
1600
1601         readDateCache();
1602
1603         if( iUiDimmer ) {
1604                 iUiDimmer->updateText("Fetching package date information");
1605                 QApplication::processEvents();
1606         }
1607
1608         QNetworkAccessManager* nam = new QNetworkAccessManager(this);
1609
1610         if( iSettings->qsettings()->value("use_proxies").toBool() && !iSettings->qsettings()->value("http_proxy").toString().isEmpty() )
1611         {
1612                  QNetworkProxy proxy = Settings::createProxyFromString( iSettings->qsettings()->value("http_proxy").toString() );
1613                  nam->setProxy(proxy);
1614         }
1615
1616         iCanCancel = true;
1617
1618         int count = 0;
1619         int updProgress = 0;
1620
1621         QHash<QString, Package*> fetchable;
1622         QHashIterator<QString, Package*> i( iPackagesAvailable );
1623         while (i.hasNext() )
1624         {
1625                 i.next();
1626                 if( !i.value()->date().isValid() && i.value()->section().startsWith("user/") && !i.value()->isBlacklisted() )
1627                 {
1628                         Repository* repo = 0;
1629                         for( int x=0; x<i.value()->repositories().count(); x++ ) {
1630                                 if( i.value()->repositories().at(x) && i.value()->repositories().at(x)->url().startsWith("http://repository.maemo.org") )
1631                                 {
1632                                         repo = i.value()->repositories().at(x);
1633                                         break;
1634                                 }
1635                         }
1636                         if( repo ) {
1637                                 fetchable.insert(i.value()->name(), i.value());
1638                         }
1639                 }
1640         }
1641
1642         connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(dateFetchNetworkReply(QNetworkReply*)));
1643         iDateRequestsWaiting = 0;
1644         iDateRequestsSent = 0;
1645         iDateRequestsReceived = 0;
1646         iNetworkError = QNetworkReply::NoError;
1647
1648         QString dbgcount = QString("need to fetch date for %1 packages").arg(fetchable.count());
1649         qDebug() << dbgcount;
1650         logToFile(dbgcount);
1651
1652         QHashIterator<QString, Package*> fe( fetchable );
1653         while (fe.hasNext() && !iTerminated)
1654         {
1655                 fe.next();
1656
1657                 if( updProgress >=20 ) {
1658                         iUiDimmer->setProgress( count*100/fetchable.count() );
1659                         updProgress=0;
1660                 }
1661
1662                 if( !fe.value()->date().isValid() && fe.value()->section().startsWith("user/") && !fe.value()->isBlacklisted() )
1663                 {
1664                         QString url;
1665                         Repository* repo = 0;
1666                         QString fullFilename = "unknown_filename";
1667                         for( int x=0; x<fe.value()->repositories().count(); x++ ) {
1668                                 if( fe.value()->repositories().at(x) && fe.value()->repositories().at(x)->url().startsWith("http://repository.maemo.org") )
1669                                 {
1670                                         repo = fe.value()->repositories().at(x);
1671                                         if( fe.value()->fullFileNames().count()>x )
1672                                                 fullFilename = fe.value()->fullFileNames().at(x);
1673                                         break;
1674                                 }
1675                         }
1676
1677                         if( repo )
1678                         {
1679                                 url = repo->url() + repo->dir() + fullFilename + "#" + fe.value()->name();
1680
1681                                 //qDebug() << "getting date for" << fe.value()->name();
1682                                 //qDebug() << url;
1683
1684                                 QUrl u(url);
1685                                 QNetworkRequest r(u);
1686
1687                                 if( iDateRequestsReceived == 0 ) {
1688                                         while( iDateRequestsWaiting>0 ) {
1689                                                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1690                                         }
1691                                 } else {
1692                                         while( iDateRequestsWaiting>50 ) {
1693                                                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1694                                         }
1695                                 }
1696
1697                                 if( iDateRequestsReceived>0 && iNetworkError != QNetworkReply::NoError &&
1698                                         iNetworkError != QNetworkReply::QNetworkReply::ContentAccessDenied && iNetworkError != QNetworkReply::QNetworkReply::ContentNotFoundError )
1699                                 {
1700                                         qDebug() << "fatal network error, aborting fetch";
1701                                         logToFile(QString("Fatal network error, date fetch aborted"));
1702                                         break;
1703                                 } else {
1704                                         nam->head(r);
1705                                         iDateRequestsSent++;
1706                                         iDateRequestsWaiting++;
1707                                 }
1708                         }
1709                         count = iDateRequestsReceived;
1710                         updProgress++;
1711                 }
1712         }
1713         while( iDateRequestsWaiting>0 ) {
1714                 if( updProgress >=20 ) {
1715                         iUiDimmer->setProgress( count*100/fetchable.count() );
1716                         updProgress=0;
1717                 }
1718                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1719                 count = iDateRequestsReceived;
1720                 updProgress++;
1721         }
1722         delete nam;
1723
1724         if( iDateRequestsReceived>0 && iNetworkError != QNetworkReply::NoError &&
1725                 iNetworkError != QNetworkReply::QNetworkReply::ContentAccessDenied && iNetworkError != QNetworkReply::QNetworkReply::ContentNotFoundError )
1726         {
1727                 // don't stop on this error, only inform the user
1728                 iMainWindow->notifyDialog("Network error", "There was a network error while fetching date information, the fetch was aborted");
1729         }
1730
1731         if( iTerminated ) {
1732                 if( iUiDimmer )
1733                         iUiDimmer->setProgress(-1);
1734                 cleanAfterError();
1735                 communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1736                 return;
1737         }
1738         iCanCancel = false;
1739
1740         if( iUiDimmer ) {
1741                 iUiDimmer->setProgress(100);
1742                 QApplication::processEvents();
1743         }
1744
1745         QString dbgstr = QString("sent %1 requests, received %2 replies with %3 errors").arg(iDateRequestsSent).arg(iDateRequestsReceived).arg(iDateRequestErrors);
1746
1747         qDebug() << dbgstr;
1748         logToFile(dbgstr);
1749
1750         logToFile( QString("Finished fetching package dates") );
1751
1752         if( fetchable.count()>0 || !dateCacheExists() ) {
1753                 iUiDimmer->updateText("Writing date cache");
1754                 QApplication::processEvents();
1755                 writeDateCache();
1756         }
1757
1758         if( iUiDimmer ) {
1759                 iUiDimmer->updateText("Creating package view");
1760                 QApplication::processEvents();
1761                 iUiDimmer->setProgress(-1);
1762         }
1763
1764         communicateStatusToUi(true, "Operation finished", "Package dates fetched");
1765         iNeedDateRefresh = false;
1766         runNext();
1767 }
1768
1769 void AAptInterface::dateFetchNetworkReply(QNetworkReply* reply)
1770 {
1771         iDateRequestsWaiting--;
1772         iDateRequestsReceived++;
1773         //qDebug() << "reply" << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
1774         iNetworkError = reply->error();
1775
1776         if( reply->error() == QNetworkReply::NoError && reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().isValid() )
1777         {
1778                 QString pkgname = reply->url().fragment();
1779                 //qDebug() << pkgname;
1780
1781                 if( pkgname.isEmpty() )
1782                         qDebug() << "warning: empty packagename in reply";
1783
1784                 Package* pkg = iPackagesAvailable.value(pkgname, 0);
1785                 if( pkg ) {
1786                         pkg->setDate( reply->header(QNetworkRequest::LastModifiedHeader).toDateTime() );
1787                 } else {
1788                         qDebug() << "warning: unknown packagename in reply:" << pkgname;
1789                 }
1790         }
1791
1792         if( reply->error() != QNetworkReply::NoError ) {
1793                 QString dbg = reply->url().fragment() + QString(": error %1: ").arg(reply->error()) + reply->errorString();
1794                 qDebug() << dbg;
1795                 logToFile( dbg );
1796                 iDateRequestErrors++;
1797         }
1798         reply->deleteLater();
1799 }
1800
1801 void AAptInterface::writeDateCache()
1802 {
1803         qDebug() << "writing date cache";
1804
1805         QFile f(KDateCacheFile);
1806         if( f.open(QIODevice::WriteOnly | QIODevice::Text ) )
1807         {
1808                 QTextStream out(&f);
1809                 QHashIterator<QString, Package*> i( iPackagesAvailable );
1810                 while (i.hasNext() )
1811                 {
1812                         i.next();
1813                         if( i.value()->date().isValid() && i.value()->section().startsWith("user/") )
1814                                 out << i.value()->name() << " " << i.value()->version() << " " << i.value()->date().toString(Qt::ISODate) << "\n";
1815                 }
1816                 f.close();
1817         } else {
1818                 qDebug() << "Warning: failed to write date cache";
1819                 logToFile(QString("Failed to write date cache"));
1820         }
1821 }
1822
1823 void AAptInterface::readDateCache()
1824 {
1825         qDebug() << "reading date cache";
1826
1827         QFile f(KDateCacheFile);
1828         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1829         {
1830                 while( !f.atEnd() ) {
1831                         QString line = f.readLine().trimmed();
1832                         QStringList parts = line.split(' ');
1833                         if( parts.count()==3 ) {
1834                                 Package* pkg = iPackagesAvailable.value(parts.at(0),0);
1835                                 if( pkg && pkg->section().startsWith("user/") && pkg->version()==parts.at(1) )
1836                                 {
1837                                         QDateTime dt = QDateTime::fromString( parts.at(2), Qt::ISODate);
1838                                         if( dt.isValid() ) {
1839                                                 pkg->setDate( dt );
1840                                         } else {
1841                                                 qDebug() << "Warning: Invalid date in date cache";
1842                                                 logToFile(QString("Invalid date in date cache"));
1843                                         }
1844                                 }
1845                         } else {
1846                                 qDebug() << "Warning: error in date cache:" << line;
1847                                 logToFile(QString("Error in date cache"));
1848                         }
1849                 }
1850                 f.close();
1851         } else {
1852                 qDebug() << "date cache does not exist";
1853         }
1854 }
1855
1856 bool AAptInterface::dateCacheExists()
1857 {
1858         QFileInfo f(KDateCacheFile);
1859         return f.exists();
1860 }
1861
1862 bool AAptInterface::loadInstallFiles(QStringList files_)
1863 {
1864         qDebug() << files_;
1865
1866         return false;
1867 }