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