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