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