release 0.6.6
[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         } else if( output.contains("E: Invalid record in the preferences file") ) {
543                 msg = "Invalid record in the apt preferences file.";
544         } else if( output.contains("E: Malformed line") && output.contains("in source list") ) {
545                 msg = "Malformed line in sources list. Check your repository settings.";
546         }
547
548         return msg;
549 }
550
551 void AAptInterface::finishedAptGetUpdate(int exitCode, QProcess::ExitStatus exitStatus)
552 {
553         //QByteArray output = iProcAptGetUpdate->readAllStandardOutput();
554         //logToFile( "Output from last process:\n---\n"+output );
555
556         if( iErrorDone ) {
557                 iErrorDone = false;
558                 iProcAptGetUpdate->close();
559                 return;
560         }
561
562         bool success = true;
563         QString title = "Operation finished";
564         QString msg = "Catalogs updated";
565         if( exitCode != 0 || exitStatus == QProcess::CrashExit )
566         {
567                 success = false;
568                 title = "Error";
569                 msg = finishProcessCommonErrorMessages(iProcAptGetUpdateOutput);
570         }
571
572         if( iUiDimmer )
573                 iUiDimmer->setProgress(-1);
574
575         if( iProcAptGetUpdateOutput.contains("Could not resolve ") || iProcAptGetUpdateOutput.contains("W: Failed to fetch") ||
576                 iProcAptGetUpdateOutput.contains("Temporary failure resolving") ) {
577                 success = false;
578                 title = "Error";
579                 msg = "Failed to update some or all of the catalogs. Check your network connection and/or repository configuration";
580         }
581
582         if( success ) {
583                 iNeedRepoRefresh = false;
584
585                 QFile lastupdate(KLastUpdateFile);   // create an empty file and/or update the modification time
586                 if( lastupdate.open(QIODevice::WriteOnly) )
587                         lastupdate.close();
588
589                 int pos = iProcAptGetUpdateOutput.indexOf("\nFetched ");
590                 if( pos!=-1 ) {
591                         msg += "<br>apt-get: ";
592                         msg += iProcAptGetUpdateOutput.mid(pos+1, iProcAptGetUpdateOutput.indexOf('\n', pos+1)-pos ).trimmed();
593                 }
594
595         } else {
596                 cleanAfterError();
597         }
598
599         iProcAptGetUpdate->close();
600         communicateStatusToUi(success, title, msg);
601         runNext();
602 }
603
604 void AAptInterface::finishedAptGetSimulate(int exitCode, QProcess::ExitStatus exitStatus)
605 {
606         QByteArray output = iProcAptGetSimulate->readAllStandardOutput();
607         logToFile( "Output from last process:\n---\n"+output );
608
609         if( iErrorDone ) {
610                 iErrorDone = false;
611                 iProcAptGetSimulate->close();
612                 return;
613         }
614
615         bool success = true;
616         QString title = "Operation finished";
617         QString msg = "";
618         if( exitCode != 0 || exitStatus == QProcess::CrashExit )
619         {
620                 success = false;
621                 title = "Error";
622                 msg = finishProcessCommonErrorMessages(output);
623         }
624
625         iProcessPackages.clear();
626         iProcessPackageVersions.clear();
627         if( success )
628         {
629                 QList<QByteArray> lines = output.split('\n');
630
631                 for( int i=0; i<lines.count(); i++)
632                 {
633                         QString s = lines.at(i);
634                         if( s.startsWith("Inst ") )
635                         {
636                                 iProcessPackages <<  s.section(' ',1,1);
637
638                                 QString vs="";
639                                 /*
640                                 int a1 = s.indexOf('[');
641                                 int a2 = s.indexOf(']');
642                                 if( a1!=-1 && a2!=-1 && a2>a1) {
643                                         vs = s.mid(a1+1, a2-a1-1) + " -> ";
644                                 }*/
645                                 int b1 = s.indexOf('(');
646                                 int b2 = s.indexOf(' ',b1);
647                                 if( b1!=-1 && b2!=-1 && b2>b1) {
648                                         vs += s.mid(b1+1, b2-b1-1);
649                                 }
650                                 //qDebug() << vs;
651                                 iProcessPackageVersions << vs;
652                         }
653                         if( s.startsWith("Remv ") )
654                         {
655                                 iProcessPackages << s.section(' ',1,1) + "-";
656
657                                 QString vs="";
658                                 int a1 = s.indexOf('[');
659                                 int a2 = s.indexOf(']');
660                                 if( a1!=-1 && a2!=-1 && a2>a1) {
661                                         vs = s.mid(a1+1, a2-a1-1);
662                                 }
663                                 //qDebug() << vs;
664                                 iProcessPackageVersions << vs;
665                         }
666                 }
667         }
668         if( !success )
669                 cleanAfterError();
670
671         iProcAptGetSimulate->close();
672         communicateStatusToUi(success, title, msg);
673         runNext();
674 }
675
676 void AAptInterface::finishedAptGetInstall(int exitCode, QProcess::ExitStatus exitStatus)
677 {
678         //QByteArray output = iProcAptGetInstall->readAllStandardOutput();
679         //logToFile( "Output from last process:\n---\n"+output );
680
681         if( iErrorDone ) {
682                 iProcAptGetInstall->close();
683                 iErrorDone = false;
684                 return;
685         }
686
687         bool success = true;
688         QString title = "Operation finished";
689         QString msg = "Package operations finished successfully";
690         if( exitCode != 0 || exitStatus == QProcess::CrashExit || iTerminated )
691         {
692                 success = false;
693                 title = "Error";
694                 msg = finishProcessCommonErrorMessages(iProcAptGetInstallOutput);
695         }
696
697         if( iProgressCheckTimer ) {
698                 iProgressCheckTimer->stop();
699                 delete iProgressCheckTimer;
700                 iProgressCheckTimer = 0;
701         }
702         if( iUiDimmer ) {
703                 iUiDimmer->setProgress(-1);
704         }
705         iProcessPackages.clear();
706         iProcessPackagesOrig.clear();
707         iProcessPackageVersions.clear();
708
709         if( !success )
710                 cleanAfterError();
711
712         iProcAptGetInstall->close();
713         communicateStatusToUi(success, title, msg);
714         runNext();
715 }
716
717 void AAptInterface::finishedAptGetClean(int exitCode, QProcess::ExitStatus exitStatus)
718 {
719         QByteArray output = iProcAptGetClean->readAllStandardOutput();
720         // this should produce no output
721         //logToFile( "Output from last process:\n---\n"+output );
722
723         if( iErrorDone ) {
724                 iErrorDone = false;
725                 iProcAptGetClean->close();
726                 return;
727         }
728
729         bool success = true;
730         QString title = "Operation finished";
731         QString msg = "Package cache cleaned";
732         if( exitCode != 0 || exitStatus == QProcess::CrashExit )
733         {
734                 success = false;
735                 title = "Error";
736                 msg = finishProcessCommonErrorMessages(output);
737         }
738         if( !success )
739                 cleanAfterError();
740
741         iProcAptGetClean->close();
742         communicateStatusToUi(success, title, msg);
743         runNext();
744 }
745
746
747 void AAptInterface::uiUpdaterAptGetUpdate()
748 {
749         QByteArray data = iProcAptGetUpdate->read( iProcAptGetUpdate->bytesAvailable() );
750         logToFile( data, false );
751         iProcAptGetUpdateOutput.append(data);
752
753         if( !iUiDimmer )
754                 return;
755
756         QStringList lines = QString( data.trimmed() ).split('\n');
757         QString infoline;
758
759         for( int i=0; i<lines.count(); i++ )
760         {
761                 if( lines.at(i).startsWith("Get:") || lines.at(i).startsWith("Hit ") )
762                 {
763                         iCatalogCounter++;                      
764                 }
765                 infoline = lines.at(i).trimmed();
766         }
767         //if( infoline.isEmpty() )
768                 //infoline = "arstus";
769
770         //iUiDimmer->updateText( QString("Updating catalogs (%1)").arg(iCatalogCounter) );
771         //qDebug() << iCatalogCounter << iCatalogsTotal;
772
773         //iUiDimmer->updateText("Updating catalogs<br><font size=\"-1\">" + infoline + "</font>");
774
775         if( iCatalogsTotal > 0 )
776                 iUiDimmer->setProgress( iCatalogCounter*100/iCatalogsTotal );
777 }
778
779 void AAptInterface::uiUpdaterAptGetInstall()
780 {
781         QByteArray data = iProcAptGetInstall->read( iProcAptGetInstall->bytesAvailable() );
782         logToFile( data, false );
783         iProcAptGetInstallOutput.append(data);
784
785         if( !iUiDimmer )
786                 return;
787
788         QStringList lines = QString( data.trimmed() ).split('\n');
789
790         bool update = false;
791         bool resetprogress = true;
792         QString oper = "";
793         QString pkgname = "";
794         iCanCancel = false;
795         for( int i=0; i<lines.count(); i++ )
796         {
797                 QStringList l = lines.at(i).split(' ');
798
799                 if( l.count()>=4 && l.at(0).startsWith("Get:") ) {
800                         oper = "Downloading";
801                         iCanCancel = true;
802                         pkgname = l.at(3);
803                         Package* pkg = iPackagesAvailable.value(pkgname,0);
804                         if( pkg!=0 ) {
805                                 iAptGetCurrentDownloadFileName = pkg->fileName();
806                                 iAptGetCurrentFileTotalSize = pkg->size()/1024;
807                                 pkgname += QString(" (%1 kB)").arg(iAptGetCurrentFileTotalSize);
808                         }
809                         iAptGetDownloadCount++;
810                         oper += QString(" %1/%2").arg(iAptGetDownloadCount).arg(iAptGetInstallTotal);
811                         update = true;
812                         if( !iProgressCheckTimer ) {
813                                 iProgressCheckTimer = new QTimer(this);
814                                 connect(iProgressCheckTimer,SIGNAL(timeout()),this,SLOT(progressCheckTimerCallback()));
815                                 iProgressCheckTimer->start(500);
816                         }
817                         resetprogress = false;
818                 } else if( l.count()>=2 && l.at(0)=="Unpacking") {
819                         oper = "Installing";
820                         if( l.count()>=3 && l.at(1)=="replacement" )
821                                 pkgname = l.at(2);
822                         else
823                                 pkgname = l.at(1);
824                         iAptGetInstallCount++;
825                         oper += QString(" %1/%2").arg(iAptGetInstallCount).arg(iAptGetInstallTotal);
826                         update = true;
827                 } else if( l.count()>=3 && l.at(0)=="Setting" && l.at(1)=="up") {
828                         oper = "Setting up";
829                         pkgname = l.at(2);
830                 } else if( l.count()>=2 && l.at(0)=="Removing") {
831                         oper = "Removing";
832                         pkgname = l.at(1);
833                         iAptGetRemoveCount++;
834                         oper += QString(" %1/%2").arg(iAptGetRemoveCount).arg(iAptGetRemoveTotal);
835                         update = true;
836                 } else if( l.count()>=1 && l.at(0)=="Done!") {
837                         oper = "Setting up...";
838                         pkgname = "";
839                         update = true;
840                 }
841
842                 /*      // this does not seem to work, dpkg always dies first
843                 if( lines.at(i).startsWith("***") && lines.at(i).contains("(Y/I/N/O/D/Z)") ) {
844                         if( iMainWindow->confirmDialog("Overwrite configuration file?","la la la") )
845                         {
846                                 iProcAptGetInstall->write("Y\n");
847                         } else {
848                                 iProcAptGetInstall->write("N\n");
849                         }
850                 }
851                 */
852         }
853
854         if( update && iUiDimmer && iUiDimmer->busy() ) {
855                 iUiDimmer->updateText( oper + "<br>" + pkgname );
856                 if( resetprogress ) {
857                         iUiDimmer->setProgress(-1);
858                         if( iProgressCheckTimer ) {
859                                 iProgressCheckTimer->stop();
860                                 delete iProgressCheckTimer;
861                                 iProgressCheckTimer = 0;
862                         }
863                 }
864         }
865 }
866
867 void AAptInterface::progressCheckTimerCallback()
868 {
869         if( iAptGetCurrentDownloadFileName.isEmpty() )
870                 return;
871
872         qint64 prevsize = iAptGetCurrentFileDownloadSize;
873         QFile pkgfile(KAptArchivePartialDir + "/" + iAptGetCurrentDownloadFileName);
874         iAptGetCurrentFileDownloadSize = pkgfile.size()/1024;
875
876         if( iAptGetCurrentFileDownloadSize >= prevsize ) {
877                 iSpeedKbpsPrev = iSpeedKbps;
878                 iSpeedKbps = (iAptGetCurrentFileDownloadSize-prevsize)*2;
879         } else {
880                 iSpeedKbpsPrev = -1;
881         }
882         if( iUpdateSpeed )
883                 iUpdateSpeed = false;
884         else
885                 iUpdateSpeed = true;
886
887         if( iUiDimmer && iUiDimmer->busy() ) {
888                 int p = iAptGetCurrentFileDownloadSize*100/iAptGetCurrentFileTotalSize;
889                 if( iAptGetDownloadCount > 0 && iAptGetCurrentFileDownloadSize==0 )
890                         p = 100;
891                 iUiDimmer->setProgress( p );
892                 if( iSpeedKbps>=0 && iSpeedKbpsPrev>=0 && iUpdateSpeed ) {
893                         iUiDimmer->setDownloadSpeed( (iSpeedKbps+iSpeedKbpsPrev)/2 );
894                 }
895         }
896 }
897
898
899 void AAptInterface::communicateStatusToUi(bool success, QString title, QString msg)
900 {
901         qDebug() << title << msg;
902         iQueueMessages.append(msg);
903
904         if( iMainWindow && iOperationsQueue.count()==0 )
905         {
906                 // title comes from the last finished operation only
907                 iMainWindow->operationQueueFinished(iModeLog, success, title, iQueueMessages);
908         }
909 }
910
911 QByteArray AAptInterface::readLogFile()
912 {
913         QByteArray log = "";
914
915         QFile own(KLogFile);
916         if( own.open(QIODevice::ReadOnly | QIODevice::Text ) )
917         {
918                 while(!own.atEnd())
919                 {
920                         QByteArray line = own.readLine();
921                         log.append( line );
922                 }
923                 own.close();
924         }
925
926         if( log.isEmpty() )
927                 log = "The log is empty";
928
929         return log;
930 }
931
932 void AAptInterface::logToFile( QString data, bool logtime )
933 {
934         logToFile( data.toAscii(), logtime );
935 }
936
937 void AAptInterface::logToFile( QByteArray data, bool logtime )
938 {
939         QFile f(KLogFile);
940
941         if( f.open( QIODevice::Append | QIODevice::WriteOnly | QIODevice::Text ) )
942         {
943                 QTextStream out(&f);
944                 if( logtime )
945                         out << "--- " << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << " ---\n";
946
947                 out << data;
948
949                 if( logtime )
950                         out << "\n";
951                 f.close();
952         }
953 }
954
955 void AAptInterface::readRepositoryInfo()
956 {
957         for(int i=0; i<iRepositories.count(); i++) {
958                 if( iRepositories.at(i) )
959                         delete iRepositories.at(i);
960         }
961         iRepositories.clear();
962         bool ownFound = false;
963
964         QFile own( KOwnRepoFile );
965         if( own.open(QIODevice::ReadOnly | QIODevice::Text ) )
966         {
967                 Repository* r = 0;
968                 while(!own.atEnd())
969                 {
970                         QString line = own.readLine().trimmed();
971                         if( line.startsWith("deb ") || line.startsWith("#deb ") )
972                         {
973                                 r = new Repository();
974                                 if( r->setFromString(line) ) {
975                                         iRepositories.append(r);
976                                         //qDebug() << r->toListFileNames();
977                                 } else {
978                                         delete r;
979                                 }
980                         }
981                 }
982                 own.close();
983                 if( iRepositories.count() > 0 )
984                         ownFound = true;
985         }
986
987         QFile names( KOwnRepoNamesFile );
988         if( names.open(QIODevice::ReadOnly | QIODevice::Text ) )
989         {
990                 int c=0;
991                 while(!names.atEnd() && c<iRepositories.count())
992                 {
993                         QString line = names.readLine().trimmed();
994                         if( !line.trimmed().isEmpty() )
995                         iRepositories.at(c)->setName( line.trimmed() );
996                         c++;
997                 }
998                 names.close();
999                 return;
1000         }
1001
1002         if( ownFound )
1003                 return;
1004
1005         QFile ham( KHamRepoListFile );
1006         if( ham.open(QIODevice::ReadOnly | QIODevice::Text ) )
1007         {
1008                 while(!ham.atEnd())
1009                 {
1010                         QString line = ham.readLine();
1011                         Repository* r = new Repository();
1012                         if( r->setFromString(line) ) {
1013                                 iRepositories.append(r);
1014                         } else {
1015                                 delete r;
1016                         }
1017                 }
1018                 ham.close();
1019         }
1020 }
1021
1022 bool AAptInterface::writeRepositories()
1023 {
1024 #ifndef Q_WS_MAEMO_5    // for simulator
1025         return true;
1026 #endif
1027
1028         iNeedListRefresh = true;
1029
1030         QFile own( KOwnRepoFile );
1031         if( own.open(QIODevice::WriteOnly | QIODevice::Text ) )
1032         {
1033                 QTextStream out(&own);
1034                 for( int i=0; i<iRepositories.count(); i++ )
1035                         out << iRepositories.at(i)->toString() << "\n";
1036                 own.close();
1037         }
1038
1039         QFile names( KOwnRepoNamesFile );
1040         if( names.open(QIODevice::WriteOnly | QIODevice::Text ) )
1041         {
1042                 QTextStream out(&names);
1043                 for( int i=0; i<iRepositories.count(); i++ )
1044                         out << iRepositories.at(i)->name() << "\n";
1045                 names.close();
1046         }
1047
1048         QFile ham( KAptSourceList );
1049         if( ham.open(QIODevice::WriteOnly | QIODevice::Text ) )
1050         {
1051                 QTextStream out(&ham);
1052                 for( int i=0; i<iRepositories.count(); i++ )
1053                         out << iRepositories.at(i)->toString() << "\n";
1054                 ham.close();
1055
1056                 return true;
1057         } else {
1058                 qDebug() << "Failed to write repository list!";
1059         }
1060
1061         return false;
1062 }
1063
1064 bool AAptInterface::needRepoRefresh()
1065 {
1066 #ifndef Q_WS_MAEMO_5    // for simulator
1067         return false;
1068 #endif
1069
1070         if( iNeedRepoRefresh || iSettings->qsettings()->value("need_repo_refresh", false).toBool() ) {
1071                 iNeedRepoRefresh = false;
1072                 iSettings->qsettings()->setValue("need_repo_refresh", false);
1073                 qDebug() << "repo update required, debug 1";
1074                 return true;
1075         }
1076
1077         QFile own(KAptSourceList);
1078
1079         if( !own.exists() )
1080         {
1081                 qDebug() << "repo update required, debug 2";
1082                 return true;
1083         }
1084
1085         QFileInfo a(KLastUpdateFile);
1086         QDateTime aDate;
1087         aDate.setTime_t(0);
1088         if( a.exists() ) {
1089                 aDate = a.lastModified();
1090         } else {
1091                 qDebug() << "repo update required, debug 3";
1092                 return true;
1093         }
1094         aDate = aDate.addSecs(24*60*60); //24h
1095
1096         if( aDate < QDateTime::currentDateTime() ) {
1097                 qDebug() << "repo update required, debug 4";
1098                 return true;
1099         }
1100
1101         qDebug() << "repo update not required";
1102         return false;
1103 }
1104
1105
1106
1107 void AAptInterface::startPkgListRead()
1108 {
1109         logToFile( QString("Start reading package lists") );
1110         qDebug() << "reading package list files";
1111
1112         if( !iNeedListRefresh && !iNeedDpkgRefresh ) {
1113                 qDebug() << "no need to refresh package lists";
1114                 logToFile( QString("No need to read package lists") );
1115                 communicateStatusToUi(true, "Operation finished", "Package data already up to date");
1116                 runNext();
1117                 return;
1118         }
1119
1120         if( iUiDimmer ) {
1121                 iUiDimmer->updateText("Reading package lists<br>");
1122                 iUiDimmer->setProgress(0);
1123         }
1124
1125 // clear packages lists
1126         if( iNeedListRefresh && !iSkipRefreshListAndDates )
1127         {
1128                 QHashIterator<QString, Package*> a( iPackagesAvailable );
1129                 while (a.hasNext())
1130                 {
1131                         a.next();
1132                         delete a.value();
1133                 }
1134                 iPackagesAvailable.clear();
1135         }
1136
1137         if( iNeedDpkgRefresh )
1138         {
1139                 QHashIterator<QString, Package*> i( iPackagesInstalled );
1140                 while (i.hasNext())
1141                 {
1142                         i.next();
1143                         delete i.value();
1144                 }
1145                 iPackagesInstalled.clear();
1146         }
1147
1148         // read apt database (available packages)
1149         QTime time_aptread;
1150         time_aptread.start();
1151
1152         int pkgcount_apt = 0;
1153         QDir dir( KAptListDir );
1154         QFileInfoList files = dir.entryInfoList();
1155
1156         quint64 totaldatasize = 0;
1157         quint64 currentreaddata = 0;
1158         quint64 lastupdatedata = 0;
1159         quint64 updateinterval = 2000000;
1160         if( iNeedListRefresh && !iSkipRefreshListAndDates ) {
1161                 for( int i=0; i<files.count(); i++ )
1162                 {
1163                         if( files.at(i).fileName().endsWith("_Packages"))
1164                                 totaldatasize += files.at(i).size();
1165                 }
1166         }
1167         if( iNeedDpkgRefresh ) {
1168                 QFileInfo dbinfo( KDpkgStatusFile );
1169                 totaldatasize += dbinfo.size();
1170         }
1171         int filecount = 0;
1172
1173         if( iNeedListRefresh && !iSkipRefreshListAndDates )
1174         {
1175                 iCanCancel = true;
1176
1177                 for( int i=0; i<files.count(); i++ )
1178                 {
1179                         Repository* currentRepo = 0;
1180                         if( files.at(i).absoluteFilePath().endsWith("_Packages") )
1181                         {
1182                                 filecount++;
1183                                 for(int x=0; x<iRepositories.count(); x++) {
1184                                         if( iRepositories.at(x)->toListFileNames().contains( files.at(i).fileName() ) ) {
1185                                                 currentRepo = iRepositories.at(x);
1186                                         }
1187                                 }
1188
1189                                 if( iUiDimmer && currentRepo ) {
1190                                         iUiDimmer->updateText("Reading package lists<br><font size=\"-1\">" + currentRepo->name() + "</font>");
1191                                 }
1192
1193                                 //qDebug() << files.at(i).fileName();
1194
1195                                 QFile db( files.at(i).absoluteFilePath() );
1196                                 if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) {
1197                                         qDebug() << "FAIL: Unable to read apt database";
1198                                         communicateStatusToUi(false, "Error", "Unable to read package lists");
1199                                         cleanAfterError();
1200                                         return;
1201                                 }
1202
1203                                 while (!db.atEnd() && !iTerminated) {
1204                                         Package* newpkg = ReadNextPackage(db, currentreaddata);
1205                                         if( iUiDimmer && currentreaddata >= lastupdatedata+updateinterval ) {
1206                                                 iUiDimmer->setProgress( currentreaddata*100/totaldatasize );
1207                                                 lastupdatedata = currentreaddata;
1208                                                 QApplication::processEvents();
1209                                         }
1210                                         pkgcount_apt++;
1211                                         if( newpkg )
1212                                         {
1213                                                 newpkg->addRepository( currentRepo );
1214
1215                                                 Package* exists = iPackagesAvailable.value(newpkg->name(),0);
1216                                                 if( !exists ) {
1217                                                         iPackagesAvailable.insert(newpkg->name(), newpkg);
1218                                                 } else {
1219                                                         if( Package::versionCompare(newpkg->version(),exists->version()) )
1220                                                         {
1221                                                                 iPackagesAvailable.remove(exists->name());
1222                                                                 delete exists;
1223                                                                 exists=0;
1224                                                                 iPackagesAvailable.insert(newpkg->name(), newpkg);
1225                                                         } else {
1226                                                                 if( newpkg->version() == exists->version() ) {
1227                                                                         exists->addRepository( currentRepo );
1228                                                                         if( newpkg->fullFileNames().count()>0 )
1229                                                                                 exists->addFullFileName( newpkg->fullFileNames().at(0) );
1230                                                                         else
1231                                                                                 exists->addFullFileName("unknown_dir/unknown_filename");
1232                                                                 }
1233                                                                 delete newpkg;
1234                                                                 newpkg=0;
1235                                                         }
1236                                                 }
1237                                         }
1238                                 }
1239                                 db.close();
1240                         }
1241                 }
1242
1243                 qDebug() << "apt database read took" << time_aptread.elapsed() << "ms";
1244                 qDebug() << "Processed" << filecount << "package list files";
1245
1246                 if( iTerminated ) {
1247                         if( iUiDimmer )
1248                                 iUiDimmer->setProgress(-1);
1249                         cleanAfterError();
1250                         communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1251                         return;
1252                 }
1253
1254                 iNeedListRefresh = false;
1255                 iNeedDateRefresh = true;
1256                 iLastListUpdate = QDateTime::currentDateTime();
1257         }
1258
1259 // read dpkg database (installed packages)
1260         if( iNeedDpkgRefresh )
1261         {
1262                 iCanCancel = true;
1263                 QTime time_dpkgread;
1264                 time_dpkgread.start();
1265
1266                 int pkgcount_dpkg = 0;
1267                 QFile db( KDpkgStatusFile );
1268                 if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) {
1269                         qDebug() << "FAIL: Unable to read dpkg database";
1270                         communicateStatusToUi(false, "Error", "Unable to read package database");
1271                         cleanAfterError();
1272                         return;
1273                 }
1274
1275                 if( iUiDimmer ) {
1276                         iUiDimmer->updateText("Reading package lists<br><font size=\"-1\">dpkg database</font>");
1277                 }
1278
1279                 while (!db.atEnd() && !iTerminated) {
1280                         Package* newpkg = ReadNextPackage(db, currentreaddata);
1281                         if( iUiDimmer && currentreaddata >= lastupdatedata+updateinterval ) {
1282                                 iUiDimmer->setProgress( currentreaddata*100/totaldatasize );
1283                                 lastupdatedata = currentreaddata;
1284                                 QApplication::processEvents();
1285                         }
1286                         pkgcount_dpkg++;
1287                         if( newpkg ) {
1288                                 if( newpkg->isInstalled() && !newpkg->name().isEmpty() ) {
1289                                         iPackagesInstalled.insert(newpkg->name(), newpkg);
1290                                 } else {
1291                                         delete newpkg;
1292                                         newpkg=0;
1293                                 }
1294                         }
1295                 }
1296                 db.close();
1297
1298                 qDebug() << "dpkg database read took" << time_dpkgread.elapsed() << "ms";
1299
1300                 qDebug() << "Processed" << pkgcount_apt << "(apt) and" << pkgcount_dpkg << "(dpkg) package entries";
1301                 qDebug() << "In DB:" << iPackagesAvailable.count() << "packages available,"
1302                                 << iPackagesInstalled.count() << "installed";
1303
1304                 if( iTerminated ) {
1305                         cleanAfterError();
1306                         communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1307                         return;
1308                 }
1309                 iNeedDpkgRefresh = false;
1310                 iLastDpkgUpdate = QDateTime::currentDateTime();
1311         }
1312
1313         logToFile( QString("Finished reading package lists") );
1314         if( iUiDimmer ) {
1315                 iUiDimmer->updateText("Reading package lists<br><font size=\"-1\">Creating package view</font>");
1316                 QApplication::processEvents();
1317                 iUiDimmer->setProgress(-1);
1318         }
1319
1320         readBlacklist();
1321         readPinnedPackages();
1322
1323         communicateStatusToUi(true, "Operation finished", "Package data read");
1324         iCanCancel = false;
1325         runNext();
1326 }
1327
1328 Package* AAptInterface::ReadNextPackage(QFile& f, quint64& currentreaddata)
1329 {
1330         iMultiLine=MultiLineNone;
1331
1332         Package* pkg = new Package("", this);
1333
1334         bool pkgready = false;
1335
1336         // this is faster than directly reading to QByteArray...
1337         QByteArray line;
1338         while( !pkgready && !f.atEnd() ) {
1339                 f.readLine(iDataReadBuffer,KDataReadBufferSize);
1340                 line = iDataReadBuffer;
1341                 currentreaddata += line.size();
1342                 if( processPackageDataLine(pkg,line) ) {
1343                         pkgready = true;
1344                 }
1345         }
1346
1347         if( !pkg->name().isEmpty() && pkg->isInstalled() ) {
1348                 QFileInfo f( KDpkgInfoDir + "/" + pkg->name() + ".list" );
1349                 if( f.exists() )
1350                         pkg->setDate( f.lastModified() );
1351         }
1352
1353         pkg->updateStatus();
1354
1355         if( pkg->name().isEmpty() ) {
1356                 qDebug() << "null name package!";
1357                 delete pkg;
1358                 pkg = 0;
1359         }
1360         return pkg;
1361 }
1362
1363 bool AAptInterface::processPackageDataLine(Package*& pkg, QByteArray& line)
1364 {
1365         if( line.isEmpty() || line=="\n" )
1366         {
1367                 return true;
1368         }
1369
1370         else if( iMultiLine == MultiLineDesc ) {
1371                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1372                         if( line.trimmed()!="." )
1373                                 pkg->appendDescLong( line.trimmed() + "\n" );
1374                         else
1375                                 pkg->appendDescLong( "\n" );
1376                 } else {
1377                         iMultiLine = MultiLineNone;
1378                 }
1379         }
1380         else if( iMultiLine == MultiLineIcon ) {
1381                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1382                         pkg->appendIconData( line.trimmed() );
1383                 } else {
1384                         iMultiLine = MultiLineNone;
1385                 }
1386         }
1387         else if( iMultiLine == MultiLineUpgradeDesc ) {
1388                 if( (line.startsWith(' ') || line.startsWith('\t')) && !line.trimmed().isEmpty() ) {
1389                         pkg->appendUpgradeDescription( line.trimmed() + "\n" );
1390                 } else {
1391                         iMultiLine = MultiLineNone;
1392                 }
1393         }
1394
1395         else if( line.startsWith("Package:") )
1396         {
1397                 pkg->setName( line.mid(8).trimmed() );
1398                 iMultiLine=MultiLineNone;
1399         }
1400         else if( line.startsWith("Status:") )
1401         {
1402                 if( line.mid(7).trimmed() == "install ok installed" )
1403                         pkg->setInstalled(true);
1404                 else
1405                         pkg->setInstalled(false);
1406         }
1407         else if( line.startsWith("Section:") )
1408         {
1409                 pkg->setSection( line.mid(8).trimmed() );
1410         }
1411         else if( line.startsWith("Version:") )
1412         {
1413                 pkg->setVersion( line.mid(8).trimmed() );
1414         }
1415         else if( line.startsWith("Filename:") )
1416         {
1417                 pkg->addFullFileName( line.mid(9).trimmed() );
1418         }
1419         else if( line.startsWith("Size:") )
1420         {
1421                 pkg->setSize( line.mid(5).trimmed().toInt() );
1422         }
1423         else if( line.startsWith("Installed-Size:") )
1424         {
1425                 pkg->setInstalledSize( line.mid(15).trimmed().toInt() );
1426         }
1427         else if( line.startsWith("Maemo-Display-Name:") )
1428         {
1429                 pkg->setMaemoDisplayName( line.mid(19).trimmed() );
1430         }
1431         else if( line.startsWith("Depends:") )
1432         {
1433                 pkg->appendDepends( line.mid(8).trimmed() );
1434         }
1435         else if( line.startsWith("Conflicts:") )
1436         {
1437                 pkg->appendConflicts( line.mid(10).trimmed() );
1438         }
1439         else if( line.startsWith("Pre-Depends:") )
1440         {
1441                 pkg->appendPreDepends( line.mid(12).trimmed() );
1442         }
1443         else if( line.startsWith("Replaces:") )
1444         {
1445                 pkg->appendReplaces( line.mid(9).trimmed() );
1446         }
1447         else if( line.startsWith("Recommends:") )
1448         {
1449                 pkg->appendRecommends( line.mid(11).trimmed() );
1450         }
1451         else if( line.startsWith("Suggests:") )
1452         {
1453                 pkg->appendSuggests( line.mid(9).trimmed() );
1454         }
1455         else if( line.startsWith("Provides:") )
1456         {
1457                 pkg->appendProvides( line.mid(9).trimmed() );
1458         }
1459         else if( line.startsWith("Breaks:") )
1460         {
1461                 pkg->appendBreaks( line.mid(7).trimmed() );
1462         }
1463         else if( line.startsWith("Maintainer:") )
1464         {
1465                 pkg->setMaintainer( line.mid(11).trimmed() );
1466         }
1467
1468         else if( line.startsWith("Description:") )
1469         {
1470                 pkg->setDescShort( line.mid(12).trimmed() );
1471                 iMultiLine = MultiLineDesc;
1472         }
1473         else if( line.startsWith("Maemo-Icon-26:") )
1474         {
1475                 if( line.mid(15).trimmed() != "" ) {
1476                         pkg->appendIconData( line.mid(15).trimmed() );
1477                 }
1478                 iMultiLine = MultiLineIcon;
1479         }
1480         else if( line.startsWith("Maemo-Upgrade-Description:") )
1481         {
1482                 pkg->appendUpgradeDescription( line.mid(26).trimmed() + "\n" );
1483                 iMultiLine = MultiLineUpgradeDesc;
1484         }
1485
1486         return false;
1487 }
1488
1489
1490
1491 void AAptInterface::writeBlacklist()
1492 {
1493         QHashIterator<QString, Package*> i( iPackagesAvailable );
1494         while (i.hasNext())
1495         {
1496                 i.next();
1497
1498                 if( i.value()->blacklisted() == BlacklistSelect::BlacklistAll ) {
1499                         iBlacklist << i.value()->name();
1500                 }
1501                 else if( i.value()->blacklisted() == BlacklistSelect::BlacklistThis ) {
1502                         iBlacklist << (i.value()->name() + " " + i.value()->version());
1503                 }
1504         }
1505
1506         QHashIterator<QString, Package*> j( iPackagesInstalled );
1507         while (j.hasNext())
1508         {
1509                 j.next();
1510
1511                 if( j.value()->blacklisted() == BlacklistSelect::BlacklistAll ) {
1512                         iBlacklist << j.value()->name();
1513                 }
1514                 else if( j.value()->blacklisted() == BlacklistSelect::BlacklistThis ) {
1515                         iBlacklist << (j.value()->name() + " " + j.value()->version());
1516                 }
1517         }
1518
1519         iBlacklist.removeDuplicates();
1520
1521         QFile f( KBlacklistFile );
1522         if( f.open(QIODevice::WriteOnly | QIODevice::Text ) )
1523         {
1524                 QTextStream out(&f);
1525                 for( int i=0; i<iBlacklist.count(); i++ )
1526                         out << iBlacklist.at(i) << "\n";
1527                 f.close();
1528         }
1529
1530         qDebug() << "blacklist: wrote" << iBlacklist.count() << "entries";
1531 }
1532
1533 void AAptInterface::readBlacklist()
1534 {
1535         QFile f( KBlacklistFile );
1536         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1537         {
1538                 while( !f.atEnd() ) {
1539                         QString line = f.readLine().trimmed();
1540                         if( line != "" )
1541                                 iBlacklist.append(line);
1542                 }
1543                 f.close();
1544         }
1545
1546         iBlacklist.removeDuplicates();
1547
1548         qDebug() << "blacklist: read" << iBlacklist.count() << "entries";
1549
1550         for( int i=0; i<iBlacklist.count(); i++ )
1551         {
1552                 QStringList parts = iBlacklist.at(i).split(' ');
1553                 Package* pkg = iPackagesAvailable.value(parts.at(0).trimmed(), 0);
1554                 Package* pkg2 = iPackagesInstalled.value(parts.at(0).trimmed(), 0);
1555                 if( parts.count()==1 ) {
1556                         if( pkg )
1557                                 pkg->setBlacklisted(BlacklistSelect::BlacklistAll);
1558                         if( pkg2 )
1559                                 pkg2->setBlacklisted(BlacklistSelect::BlacklistAll);
1560                 } else if( parts.count()==2 ) {
1561                         if( pkg && pkg->version()==parts.at(1) )
1562                                 pkg->setBlacklisted(BlacklistSelect::BlacklistThis);
1563                         if( pkg2 && pkg2->version()==parts.at(1) )
1564                                 pkg2->setBlacklisted(BlacklistSelect::BlacklistThis);
1565                 } else {
1566                         qDebug() << "Warning: invalid blacklist entry:" << iBlacklist.at(i);
1567                 }
1568         }
1569 }
1570
1571 void AAptInterface::removeFromBlacklist(Package *pkg, BlacklistSelect::blackList oldstate)
1572 {
1573         if( !pkg ) {
1574                 qDebug() << "Warning: trying to remove null package from blacklist";
1575                 return;
1576         }
1577
1578         QStringList newlist;
1579         bool removed = false;
1580
1581         for( int i=0; i<iBlacklist.count(); i++ )
1582         {
1583                 if( oldstate == BlacklistSelect::BlacklistAll )
1584                 {
1585                         if( !(iBlacklist.at(i) == pkg->name()) ) {
1586                                 newlist << iBlacklist.at(i);
1587                         } else removed = true;
1588                 } else if( oldstate == BlacklistSelect::BlacklistThis ) {
1589                         if( !(iBlacklist.at(i) == (pkg->name()+" "+pkg->version())) ) {
1590                                 newlist << iBlacklist.at(i);
1591                         } else removed = true;
1592                 }
1593         }
1594
1595         if( removed )
1596                 qDebug() << "blacklist: removed" << pkg->name();
1597         else
1598                 qDebug() << "blacklist:" << pkg->name() << "not in saved list";
1599
1600         iBlacklist = newlist;
1601 }
1602
1603
1604 void AAptInterface::startFetchDates()
1605 {
1606         logToFile( QString("Start fetching package dates") );
1607         qDebug() << "start fetching package dates";
1608
1609         if( !iNeedDateRefresh || iSkipRefreshListAndDates ) {
1610                 qDebug() << "no need to fetch dates";
1611                 logToFile( QString("No need to fetch dates") );
1612                 communicateStatusToUi(true, "Operation finished", "Date information already up to date");
1613                 runNext();
1614                 return;
1615         }
1616
1617         if( iUiDimmer ) {
1618                 iUiDimmer->updateText("Reading date cache");
1619                 iUiDimmer->setProgress(0);
1620                 QApplication::processEvents();
1621         }
1622
1623         readDateCache();
1624
1625         if( iUiDimmer ) {
1626                 iUiDimmer->updateText("Fetching package date information");
1627                 QApplication::processEvents();
1628         }
1629
1630         QNetworkAccessManager* nam = new QNetworkAccessManager(this);
1631
1632         if( iSettings->qsettings()->value("use_proxies").toBool() && !iSettings->qsettings()->value("http_proxy").toString().isEmpty() )
1633         {
1634                  QNetworkProxy proxy = Settings::createProxyFromString( iSettings->qsettings()->value("http_proxy").toString() );
1635                  nam->setProxy(proxy);
1636         }
1637
1638         iCanCancel = true;
1639
1640         int count = 0;
1641         int updProgress = 0;
1642
1643         QHash<QString, Package*> fetchable;
1644         QHashIterator<QString, Package*> i( iPackagesAvailable );
1645         while (i.hasNext() )
1646         {
1647                 i.next();
1648                 if( !i.value()->date().isValid() && i.value()->section().startsWith("user/") && !i.value()->isBlacklisted() )
1649                 {
1650                         Repository* repo = 0;
1651                         for( int x=0; x<i.value()->repositories().count(); x++ ) {
1652                                 if( i.value()->repositories().at(x) && i.value()->repositories().at(x)->url().startsWith("http://repository.maemo.org") )
1653                                 {
1654                                         repo = i.value()->repositories().at(x);
1655                                         break;
1656                                 }
1657                         }
1658                         if( repo ) {
1659                                 fetchable.insert(i.value()->name(), i.value());
1660                         }
1661                 }
1662         }
1663
1664         connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(dateFetchNetworkReply(QNetworkReply*)));
1665         iDateRequestsWaiting = 0;
1666         iDateRequestsSent = 0;
1667         iDateRequestsReceived = 0;
1668         iNetworkError = QNetworkReply::NoError;
1669
1670         QString dbgcount = QString("need to fetch date for %1 packages").arg(fetchable.count());
1671         qDebug() << dbgcount;
1672         logToFile(dbgcount);
1673
1674         QHashIterator<QString, Package*> fe( fetchable );
1675         while (fe.hasNext() && !iTerminated)
1676         {
1677                 fe.next();
1678
1679                 if( updProgress >=20 ) {
1680                         iUiDimmer->setProgress( count*100/fetchable.count() );
1681                         updProgress=0;
1682                 }
1683
1684                 if( !fe.value()->date().isValid() && fe.value()->section().startsWith("user/") && !fe.value()->isBlacklisted() )
1685                 {
1686                         QString url;
1687                         Repository* repo = 0;
1688                         QString fullFilename = "unknown_filename";
1689                         for( int x=0; x<fe.value()->repositories().count(); x++ ) {
1690                                 if( fe.value()->repositories().at(x) && fe.value()->repositories().at(x)->url().startsWith("http://repository.maemo.org") )
1691                                 {
1692                                         repo = fe.value()->repositories().at(x);
1693                                         if( fe.value()->fullFileNames().count()>x )
1694                                                 fullFilename = fe.value()->fullFileNames().at(x);
1695                                         break;
1696                                 }
1697                         }
1698
1699                         if( repo )
1700                         {
1701                                 url = repo->url() + repo->dir() + fullFilename + "#" + fe.value()->name();
1702
1703                                 //qDebug() << "getting date for" << fe.value()->name();
1704                                 //qDebug() << url;
1705
1706                                 QUrl u(url);
1707                                 QNetworkRequest r(u);
1708
1709                                 if( iDateRequestsReceived == 0 ) {
1710                                         while( iDateRequestsWaiting>0 ) {
1711                                                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1712                                         }
1713                                 } else {
1714                                         while( iDateRequestsWaiting>50 ) {
1715                                                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1716                                         }
1717                                 }
1718
1719                                 if( iDateRequestsReceived>0 && iNetworkError != QNetworkReply::NoError &&
1720                                         iNetworkError != QNetworkReply::QNetworkReply::ContentAccessDenied && iNetworkError != QNetworkReply::QNetworkReply::ContentNotFoundError )
1721                                 {
1722                                         qDebug() << "fatal network error, aborting fetch";
1723                                         logToFile(QString("Fatal network error, date fetch aborted"));
1724                                         break;
1725                                 } else {
1726                                         nam->head(r);
1727                                         iDateRequestsSent++;
1728                                         iDateRequestsWaiting++;
1729                                 }
1730                         }
1731                         count = iDateRequestsReceived;
1732                         updProgress++;
1733                 }
1734         }
1735         while( iDateRequestsWaiting>0 ) {
1736                 if( updProgress >=20 ) {
1737                         iUiDimmer->setProgress( count*100/fetchable.count() );
1738                         updProgress=0;
1739                 }
1740                 QApplication::processEvents(QEventLoop::WaitForMoreEvents);
1741                 count = iDateRequestsReceived;
1742                 updProgress++;
1743         }
1744         delete nam;
1745
1746         if( iDateRequestsReceived>0 && iNetworkError != QNetworkReply::NoError &&
1747                 iNetworkError != QNetworkReply::QNetworkReply::ContentAccessDenied && iNetworkError != QNetworkReply::QNetworkReply::ContentNotFoundError )
1748         {
1749                 // don't stop on this error, only inform the user
1750                 iMainWindow->notifyDialog("Network error", "There was a network error while fetching date information, the fetch was aborted");
1751         }
1752
1753         if( iTerminated ) {
1754                 if( iUiDimmer )
1755                         iUiDimmer->setProgress(-1);
1756                 cleanAfterError();
1757                 communicateStatusToUi(false, "Operation cancelled", "The operation was cancelled by user");
1758                 return;
1759         }
1760         iCanCancel = false;
1761
1762         if( iUiDimmer ) {
1763                 iUiDimmer->setProgress(100);
1764                 QApplication::processEvents();
1765         }
1766
1767         QString dbgstr = QString("sent %1 requests, received %2 replies with %3 errors").arg(iDateRequestsSent).arg(iDateRequestsReceived).arg(iDateRequestErrors);
1768
1769         qDebug() << dbgstr;
1770         logToFile(dbgstr);
1771
1772         logToFile( QString("Finished fetching package dates") );
1773
1774         if( fetchable.count()>0 || !dateCacheExists() ) {
1775                 iUiDimmer->updateText("Writing date cache");
1776                 QApplication::processEvents();
1777                 writeDateCache();
1778         }
1779
1780         if( iUiDimmer ) {
1781                 iUiDimmer->updateText("Creating package view");
1782                 QApplication::processEvents();
1783                 iUiDimmer->setProgress(-1);
1784         }
1785
1786         communicateStatusToUi(true, "Operation finished", "Package dates fetched");
1787         iNeedDateRefresh = false;
1788         runNext();
1789 }
1790
1791 void AAptInterface::dateFetchNetworkReply(QNetworkReply* reply)
1792 {
1793         iDateRequestsWaiting--;
1794         iDateRequestsReceived++;
1795         //qDebug() << "reply" << reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
1796         iNetworkError = reply->error();
1797
1798         if( reply->error() == QNetworkReply::NoError && reply->header(QNetworkRequest::LastModifiedHeader).toDateTime().isValid() )
1799         {
1800                 QString pkgname = reply->url().fragment();
1801                 //qDebug() << pkgname;
1802
1803                 if( pkgname.isEmpty() )
1804                         qDebug() << "warning: empty packagename in reply";
1805
1806                 Package* pkg = iPackagesAvailable.value(pkgname, 0);
1807                 if( pkg ) {
1808                         pkg->setDate( reply->header(QNetworkRequest::LastModifiedHeader).toDateTime() );
1809                 } else {
1810                         qDebug() << "warning: unknown packagename in reply:" << pkgname;
1811                 }
1812         }
1813
1814         if( reply->error() != QNetworkReply::NoError ) {
1815                 QString dbg = reply->url().fragment() + QString(": error %1: ").arg(reply->error()) + reply->errorString();
1816                 qDebug() << dbg;
1817                 logToFile( dbg );
1818                 iDateRequestErrors++;
1819         }
1820         reply->deleteLater();
1821 }
1822
1823 void AAptInterface::writeDateCache()
1824 {
1825         qDebug() << "writing date cache";
1826
1827         QFile f(KDateCacheFile);
1828         if( f.open(QIODevice::WriteOnly | QIODevice::Text ) )
1829         {
1830                 QTextStream out(&f);
1831                 QHashIterator<QString, Package*> i( iPackagesAvailable );
1832                 while (i.hasNext() )
1833                 {
1834                         i.next();
1835                         if( i.value()->date().isValid() && i.value()->section().startsWith("user/") )
1836                                 out << i.value()->name() << " " << i.value()->version() << " " << i.value()->date().toString(Qt::ISODate) << "\n";
1837                 }
1838                 f.close();
1839         } else {
1840                 qDebug() << "Warning: failed to write date cache";
1841                 logToFile(QString("Failed to write date cache"));
1842         }
1843 }
1844
1845 void AAptInterface::readDateCache()
1846 {
1847         qDebug() << "reading date cache";
1848
1849         QFile f(KDateCacheFile);
1850         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1851         {
1852                 while( !f.atEnd() ) {
1853                         QString line = f.readLine().trimmed();
1854                         QStringList parts = line.split(' ');
1855                         if( parts.count()==3 ) {
1856                                 Package* pkg = iPackagesAvailable.value(parts.at(0),0);
1857                                 if( pkg && pkg->section().startsWith("user/") && pkg->version()==parts.at(1) )
1858                                 {
1859                                         QDateTime dt = QDateTime::fromString( parts.at(2), Qt::ISODate);
1860                                         if( dt.isValid() ) {
1861                                                 pkg->setDate( dt );
1862                                         } else {
1863                                                 qDebug() << "Warning: Invalid date in date cache";
1864                                                 logToFile(QString("Invalid date in date cache"));
1865                                         }
1866                                 }
1867                         } else {
1868                                 qDebug() << "Warning: error in date cache:" << line;
1869                                 logToFile(QString("Error in date cache"));
1870                         }
1871                 }
1872                 f.close();
1873         } else {
1874                 qDebug() << "date cache does not exist";
1875         }
1876 }
1877
1878 bool AAptInterface::dateCacheExists()
1879 {
1880         QFileInfo f(KDateCacheFile);
1881         return f.exists();
1882 }
1883
1884 void AAptInterface::readPinnedPackages()
1885 {
1886         QFile f(KAptPreferencesFile);
1887         if( !f.exists() )
1888                 return;
1889
1890         bool warnAllPinned = false;
1891         if( f.open(QIODevice::ReadOnly | QIODevice::Text ) )
1892         {
1893                 qDebug() << "apt preferences exist: reading pinned packages";
1894                 int pinned_packages = 0;
1895                 while( !f.atEnd() )
1896                 {
1897                         QString line = f.readLine().trimmed();
1898
1899                         if( line=="Package: *" || line=="Package:*")
1900                                 warnAllPinned = true;
1901
1902                         if( line.startsWith("Package:") ) {
1903                                 pinned_packages++;
1904                                 QString pkg = line.mid(8).trimmed();
1905                                 Package* pkg_i = iPackagesInstalled.value(pkg,0);
1906                                 if( pkg_i ) {
1907                                         pkg_i->setPinned(true);
1908                                 }
1909                                 Package* pkg_a = iPackagesAvailable.value(pkg,0);
1910                                 if( pkg_a ) {
1911                                         pkg_a->setPinned(true);
1912                                 }
1913                         }
1914                 }
1915                 f.close();
1916                 qDebug() << "read" << pinned_packages << "pinned packages";
1917         }
1918
1919         if( warnAllPinned ) {
1920                 iMainWindow->notifyDialog("Warning","You have pinned packages with '*' in apt preferences. It is strongly recommended to "
1921                                                                   "remove such settings as they can result in unexpected behavior of Faster Application Manager.");
1922         }
1923 }
1924
1925 bool AAptInterface::loadInstallFiles(QStringList files_)
1926 {
1927         qDebug() << files_;
1928
1929         return false;
1930 }