source: src-qt4/pc-softwaremanager/pbiBackend.cpp @ ecf5153

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since ecf5153 was ecf5153, checked in by Ken Moore <ken@…>, 17 months ago

Small fix for the repo parser, now it will still list PBI's that do not have an "active" version in the repo, but do have a "current" version

  • Property mode set to 100644
File size: 52.7 KB
Line 
1/***************************************************************************
2 *   Copyright (C) 2011 - iXsystems                                       *
3 *   kris@pcbsd.org  *
4 *   tim@pcbsd.org   *
5 *   ken@pcbsd.org   *
6 *                                                                         *
7 *   Permission is hereby granted, free of charge, to any person obtaining *
8 *   a copy of this software and associated documentation files (the       *
9 *   "Software"), to deal in the Software without restriction, including   *
10 *   without limitation the rights to use, copy, modify, merge, publish,   *
11 *   distribute, sublicense, and/or sell copies of the Software, and to    *
12 *   permit persons to whom the Software is furnished to do so, subject to *
13 *   the following conditions:                                             *
14 *                                                                         *
15 *   The above copyright notice and this permission notice shall be        *
16 *   included in all copies or substantial portions of the Software.       *
17 *                                                                         *
18 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
19 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
20 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
21 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
22 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
23 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
24 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
25 ***************************************************************************/
26 //ID FORMATS: [<name> = Extras::nameToID(formalName)]
27 //  pbiID: <name>-<version>-<arch> (of locally installed PBI)
28 //  catID: <name> (of category in repo)
29 //  appID: <name> (of app in repo)
30 
31 #include "pbiBackend.h"
32
33 
34 PBIBackend::PBIBackend(QWidget *parent) : QObject(){
35   parentWidget = parent;
36   //initialize the background processes
37   PMAN = new ProcessManager();
38   connect(PMAN, SIGNAL(ProcessFinished(int)),this,SLOT(slotProcessFinished(int)) );
39   connect(PMAN, SIGNAL(ProcessMessage(int, QString)),this,SLOT(slotProcessMessage(int, QString)) );
40   connect(PMAN, SIGNAL(ProcessError(int,QString)),this,SLOT(slotProcessError(int,QString)) );
41   PENDINGREMOVAL.clear(); PENDINGDL.clear(); PENDINGINSTALL.clear(); PENDINGUPDATE.clear(); PENDINGOTHER.clear();
42   sDownload=FALSE; sInstall=FALSE; sUpdate=FALSE; sRemove=FALSE;
43   //setup the base paths
44   baseDBDir = "/var/db/pbi/";
45   sysDB = new PBIDBAccess();
46   noRepo=FALSE;
47   wardenMode=FALSE;
48   //Default User Preferences
49   settingsFile = QDir::homePath()+"/.appcafeprefs";
50   baseDlDir = QDir::homePath()+"/Downloads/";
51   if(!QFile::exists(baseDlDir)){ baseDlDir = QDir::homePath()+"/"; }
52   keepDownloads = FALSE;
53   autoXDG.clear(); autoXDG << "desktop" << "menu" << "mime" << "paths";
54   currentRepoNum = "001"; //first repo by default
55   //Filesystem watcher
56   watcher = new QFileSystemWatcher();
57   connect(watcher,SIGNAL(directoryChanged(QString)),this,SLOT(slotSyncToDatabase()) );
58   
59 }
60 
61 // ==============================
62 // ====== PUBLIC FUNCTIONS ======
63 // ==============================
64 void PBIBackend::setWardenMode(QString dir, QString ip){
65   wardenDir = dir; wardenIP = ip;
66   //wardenDir: used for direct access to a directory in the jail
67   //wardenIP: used to chroot into the jail to run commands
68   if( wardenDir.isEmpty() || wardenIP.isEmpty() ){ wardenMode = FALSE; }
69   else{ 
70     if(!wardenDir.endsWith("/")){wardenDir.append("/"); } //make sure it has a / on the end
71     wardenMode = TRUE; 
72   }
73 }
74 
75 bool PBIBackend::start(){
76   sysArch = Extras::getSystemArch(); //get the architecture for the current system
77   //Setup the base paths for the system database and downloads
78   if(wardenMode){ 
79     DBDir = wardenDir + baseDBDir;
80   }else{ 
81     DBDir = baseDBDir;
82   }
83   if(!DBDir.endsWith("/")){ DBDir.append("/"); }
84   if( !loadSettings() ){
85     saveSettings();       
86   }
87   updateDlDirPath(baseDlDir);
88   //Now setup the database access class
89   sysDB->setDBPath(DBDir);
90   sysDB->setRootCMDPrefix( addRootCMD("",TRUE).remove("\"") );
91     //Make sure the deisred repo is valid
92     QStringList repos = sysDB->availableRepos();
93     if(repos.length() > 0){ 
94       if(!repos.contains(currentRepoNum)){currentRepoNum = repos[0];} //make sure the desired repo actually exists
95     }else{
96       qDebug() << "No PBI Repositories available: disabling the browser";
97       emit NoRepoAvailable();
98       noRepo = TRUE;
99     }
100   //Set the repo
101   if(!noRepo){ sysDB->setRepo(currentRepoNum); }
102   //Now start the filesystem watcher
103   startFileSystemWatcher();
104   //Now initialize the hash lists with the database info
105   QTimer::singleShot(1,this,SLOT(slotSyncToDatabase()) );
106
107   return TRUE;
108 }
109
110 QStringList PBIBackend::installedList(){
111   return QStringList( PBIHASH.keys() ); 
112 }
113 
114 QStringList PBIBackend::browserCategories(){
115   return QStringList( CATHASH.keys() ); 
116 }
117 
118QStringList PBIBackend::browserApps( QString catID ){
119  QStringList output;
120  if(!CATHASH.contains(catID)){ return output; }
121  QStringList apps = APPHASH.keys();
122  for(int i=0; i<apps.length(); i++){
123    if(Extras::nameToID(APPHASH[apps[i]].category)==catID){ output << apps[i]; }
124  }
125  return output;
126}
127
128QStringList PBIBackend::getRecentApps(){
129  //List all new/updated applications in the last 10 days (sorted by oldest first)
130  QStringList output;
131  //Generate a string for today's date
132  QDate date = QDate::currentDate();
133  date = date.addDays(-10);
134  QString chk = date.toString(Qt::ISODate);
135  chk.remove("-"); chk.append(" 000000"); //don't specify a particular time
136  //Now compare the latest apps to this date
137  QStringList apps = APPHASH.keys();
138  QStringList  unsorted;
139  for(int i=0; i<apps.length(); i++){
140    if(Extras::newerDateTime(APPHASH[apps[i]].latestDatetime, chk)){
141      unsorted << APPHASH[apps[i]].latestDatetime + "::"+apps[i];           
142    }
143  }
144  //Now sort them by datetime and then remove the datetime stamp
145  unsorted.sort(); //oldest->newest
146  for(int i=(unsorted.length()-1); i>=0; i--){ //go in reverse order
147    output << unsorted[i].section("::",1,50);
148  }
149  return output; //newest->oldest
150}
151
152bool PBIBackend::safeToQuit(){
153  //returns true if there is no pending/current processes
154  bool ok = ( PENDINGDL.isEmpty() && PENDINGUPDATE.isEmpty() && PENDINGREMOVAL.isEmpty() \
155            && PENDINGINSTALL.isEmpty() && PENDINGOTHER.isEmpty() && cDownload.isEmpty() \
156            && cUpdate.isEmpty() && cRemove.isEmpty() && cInstall.isEmpty() && cOther.isEmpty() );
157  return ok;
158}
159// ===== Local/Repo Interaction Functions =====
160QString PBIBackend::isInstalled(QString appID){
161  //Returns pbiID of the installed application
162  QString output;
163  if(!APPHASH.contains(appID)){
164    qDebug() << "Invalid application ID:" << appID;
165    return output;
166  }
167  QStringList pbiID = PBIHASH.keys(); //get list of installed PBI's
168  for(int i=0; i<pbiID.length();i++){
169    QString pbi = Extras::nameToID(PBIHASH[pbiID[i]].name);
170    if( (pbi == appID) && !PBIHASH[pbiID[i]].path.isEmpty() ){
171      output = pbiID[i];
172      break;
173    }
174  }
175  return output;
176}
177
178QString PBIBackend::upgradeAvailable(QString pbiID){
179  QString output;
180  if(!PBIHASH.contains(pbiID)){return output;}
181  QString appID = Extras::nameToID(PBIHASH[pbiID].name);
182  if(APPHASH.contains(appID)){
183    if(APPHASH[appID].latestVersion != PBIHASH[pbiID].version){output = APPHASH[appID].latestVersion;} 
184  }
185  return output;
186}
187
188// === PBI Actions ===
189void PBIBackend::cancelActions(QStringList pbiID){
190  qDebug() << "Cancel Actions requested for:" << pbiID;
191  for(int i=0; i<pbiID.length(); i++){
192    if(PBIHASH.contains(pbiID[i]) ){
193      //Remove any pending actions for this pbiID
194      PENDINGDL = removePbiCMD(pbiID[i],PENDINGDL);
195      PENDINGINSTALL = removePbiCMD(pbiID[i],PENDINGINSTALL);
196      PENDINGREMOVAL = removePbiCMD(pbiID[i],PENDINGREMOVAL);
197      PENDINGUPDATE = removePbiCMD(pbiID[i],PENDINGUPDATE);
198      PENDINGOTHER = removePbiCMD(pbiID[i],PENDINGOTHER); //doubtful that there will even be anything here
199      //Now cancel any current operations for this pbiID
200      if(cDownload==pbiID[i]){ sDownload=TRUE; PMAN->stopProcess(ProcessManager::DOWNLOAD); }
201      if(cUpdate==pbiID[i]){ sUpdate=TRUE; PMAN->stopProcess(ProcessManager::UPDATE); }
202      if(cRemove==pbiID[i]){ sRemove=TRUE; PMAN->stopProcess(ProcessManager::REMOVE); }
203      if(cInstall==pbiID[i]){ sInstall=TRUE; PMAN->stopProcess(ProcessManager::INSTALL); }
204      //Ignore OTHER process - those commands are pretty much instant
205    }
206  }
207  //Now refresh the PBIHASH info (remove stale entries, update statuses, etc..)
208  slotSyncToDatabase(TRUE);
209}
210
211void PBIBackend::upgradePBI(QStringList pbiID){
212  qDebug() << "PBI Upgrades requested for:" << pbiID;
213  for(int i=0; i<pbiID.length(); i++){
214    if( PBIHASH.contains(pbiID[i]) ){
215      if( PBIHASH[pbiID[i]].status == InstalledPBI::UPDATEAVAILABLE ){
216        QString cmd = generateUpdateCMD(pbiID[i]);
217        PENDINGUPDATE << pbiID[i] + ":::" + cmd;
218        PBIHASH[pbiID[i]].setStatus(InstalledPBI::PENDINGUPDATE);
219        emit PBIStatusChange(pbiID[i]);
220      }else{
221        qDebug() << "No update available for:" << pbiID[i];     
222      }
223    }else{
224      qDebug() << pbiID[i] << "not a valid PBI to update";         
225    }
226  }
227  //Now check/start the update process
228  QTimer::singleShot(0,this,SLOT(checkProcesses()) );
229}
230
231void PBIBackend::removePBI(QStringList pbiID){
232  qDebug() << "PBI Removals requested for:" << pbiID;
233  QStringList xdgrem; xdgrem << "remove-desktop" << "remove-menu" << "remove-mime" << "remove-paths";
234  for(int i=0; i<pbiID.length(); i++){
235    if(PBIHASH.contains(pbiID[i])){
236      //Remove XDG entries for this app
237      PENDINGREMOVAL << pbiID[i]+":::"+generateXDGCMD(pbiID[i],xdgrem, FALSE);
238      //Remove the app itself
239      PENDINGREMOVAL << pbiID[i]+":::"+generateRemoveCMD(pbiID[i]);
240      //Now update the status
241      PBIHASH[pbiID[i]].setStatus(InstalledPBI::PENDINGREMOVAL);
242      emit PBIStatusChange(pbiID[i]);
243    }else{
244      qDebug() << pbiID[i] << "not a valid PBI to remove";         
245    }
246  }
247  //Now check/start the remove process
248  QTimer::singleShot(0,this,SLOT(checkProcesses()) );
249}
250
251void PBIBackend::stopUpdate(QStringList pbiID){
252  qDebug() << "Stop Update requested for:" << pbiID;
253}
254
255void PBIBackend::installApp(QStringList appID){
256  qDebug() << "Install App requested for:" << appID;
257  for(int i=0; i<appID.length(); i++){
258    if(!APPHASH.contains(appID[i])){ 
259      qDebug() << appID[i] << "is not a valid application";
260      continue; //go to the next item is this one is invalid
261    } 
262    //Find out if app is installed already
263    QString pbiID = isInstalled(appID[i]);
264    //Generate the download command
265    QString cmd, version, arch, dlfile;
266    bool needDownload = TRUE;
267    if(pbiID.isEmpty()){ //Not installed currently
268      version = APPHASH[appID[i]].latestVersion; //install the latest version available
269      arch = APPHASH[appID[i]].latestArch;
270      dlfile = APPHASH[appID[i]].latestFilename;
271      if(QFile::exists(dlDir+dlfile)){ //If the file was downloaded previously just use it
272        needDownload=FALSE; 
273        cmd = generateInstallCMD(dlfile);
274      }else{ 
275        cmd = generateDownloadCMD(appID[i], version); //need to download the file first
276      } 
277    }else if( PBIHASH[pbiID].version == APPHASH[appID[i]].latestVersion ){
278      version = APPHASH[appID[i]].backupVersion; //Already latest version, revert to the backup version
279      arch = APPHASH[appID[i]].backupArch;
280      dlfile = APPHASH[appID[i]].backupFilename;
281      if(version.isEmpty()){ qDebug() << appID[i] << "already the latest version (no backup)";}
282      else{ 
283        if(QFile::exists(dlDir+dlfile)){ //If the file was downloaded previously just use it
284          needDownload=FALSE; 
285          cmd = generateInstallCMD(dlfile);
286        }else{ 
287          cmd = generateDownloadCMD(appID[i], version); //need to download the file first
288        } 
289      }     
290    }else{
291      //Old version installed - run the update command instead
292      upgradePBI(QStringList() << pbiID);
293    }
294    if( cmd.isEmpty() || version.isEmpty() || arch.isEmpty() || dlfile.isEmpty() ){ continue; } //go to the next item - is invalid
295    else{
296      QString newPbiID = appID[i]+"-"+version+"-"+arch; //generate a PBI ID
297      if(PBIHASH.contains(newPbiID)){ qDebug() << newPbiID << "is either already installed or installing"; }
298      else{
299        //Now create a new entry for the item
300        PBIHASH.insert(newPbiID,InstalledPBI());
301        PBIHASH[newPbiID].metaID = appID[i]; //set this for the initial sync to work properly
302        PBIHASH[newPbiID].version = version;
303        PBIHASH[newPbiID].arch = arch;
304        PBIHASH[newPbiID].downloadfile = dlfile;             
305        //put the command in the queue
306        if(needDownload){
307          PENDINGDL << newPbiID+":::"+cmd;
308        }else{ //no need to download, just install
309          //Check for if a different version needs to be removed first
310          qDebug() << "Using existing PBI:" << dlDir+dlfile;
311          if(!pbiID.isEmpty()){
312            //Remove the currently installed version first
313            QString cmd2 = generateRemoveCMD(pbiID);
314            if(!cmd2.isEmpty()){ PENDINGINSTALL << pbiID+":::"+cmd2; }
315          }
316          //Now add the install command
317          PENDINGINSTALL << newPbiID+":::"+cmd;
318        }
319        syncPBI(newPbiID,FALSE); //fill item with info from app database (including status)
320      }
321    }
322  } // end of loop over items
323  //Now check/start the remove process
324  QTimer::singleShot(0,this,SLOT(checkProcesses()) );
325  //Now emit the signal that items have changed or been added
326  emit LocalPBIChanges();
327}
328
329void PBIBackend::addDesktopIcons(QStringList pbiID, bool allusers){ // add XDG desktop icons
330  for(int i=0; i<pbiID.length(); i++){
331    if(PBIHASH.contains(pbiID[i])){
332      //generate the command
333      QString cmd = generateXDGCMD(pbiID[i],QStringList()<<"desktop",allusers);
334      //Now add it to the queue
335      PENDINGOTHER << pbiID[i]+":::"+cmd;
336    }
337  }
338  //Now check/start the process
339  QTimer::singleShot(0,this,SLOT(checkProcesses()) );
340}
341
342void PBIBackend::addMenuIcons(QStringList pbiID, bool allusers){ // add XDG menu icons
343  for(int i=0; i<pbiID.length(); i++){
344    if(PBIHASH.contains(pbiID[i])){
345      //generate the command
346      QString cmd = generateXDGCMD(pbiID[i],QStringList()<<"menu",allusers);
347      //Now add it to the queue
348      PENDINGOTHER << pbiID[i]+":::"+cmd;
349    }
350  }
351  //Now check/start the process
352  QTimer::singleShot(0,this,SLOT(checkProcesses()) );
353}
354
355void PBIBackend::addPaths(QStringList pbiID, bool allusers){ // create path links
356  for(int i=0; i<pbiID.length(); i++){
357    if(PBIHASH.contains(pbiID[i])){
358      //generate the command
359      QString cmd = generateXDGCMD(pbiID[i],QStringList()<<"paths",allusers);
360      //Now add it to the queue
361      PENDINGOTHER << pbiID[i]+":::"+cmd;
362    }
363  }
364  //Now check/start the process
365  QTimer::singleShot(0,this,SLOT(checkProcesses()) );   
366}
367
368void PBIBackend::addMimeTypes(QStringList pbiID, bool allusers){ // remove path links
369  for(int i=0; i<pbiID.length(); i++){
370    if(PBIHASH.contains(pbiID[i])){
371      //generate the command
372      QString cmd = generateXDGCMD(pbiID[i],QStringList()<<"mime",allusers);
373      //Now add it to the queue
374      PENDINGOTHER << pbiID[i]+":::"+cmd;
375    }
376  }
377  //Now check/start the process
378  QTimer::singleShot(0,this,SLOT(checkProcesses()) );   
379}
380
381void PBIBackend::rmDesktopIcons(QStringList pbiID, bool allusers){ // remove XDG desktop icons
382  for(int i=0; i<pbiID.length(); i++){
383    if(PBIHASH.contains(pbiID[i])){
384      //generate the command
385      QString cmd = generateXDGCMD(pbiID[i],QStringList()<<"remove-desktop",allusers);
386      //Now add it to the queue
387      PENDINGOTHER << pbiID[i]+":::"+cmd;
388    }
389  }
390  //Now check/start the process
391  QTimer::singleShot(0,this,SLOT(checkProcesses()) );   
392}
393
394void PBIBackend::rmMenuIcons(QStringList pbiID, bool allusers){ // remove XDG menu icons
395  for(int i=0; i<pbiID.length(); i++){
396    if(PBIHASH.contains(pbiID[i])){
397      //generate the command
398      QString cmd = generateXDGCMD(pbiID[i],QStringList()<<"remove-menu",allusers);
399      //Now add it to the queue
400      PENDINGOTHER << pbiID[i]+":::"+cmd;
401    }
402  }
403  //Now check/start the process
404  QTimer::singleShot(0,this,SLOT(checkProcesses()) );   
405}
406
407void PBIBackend::rmPaths(QStringList pbiID, bool allusers){ // remove path links
408  for(int i=0; i<pbiID.length(); i++){
409    if(PBIHASH.contains(pbiID[i])){
410      //generate the command
411      QString cmd = generateXDGCMD(pbiID[i],QStringList()<<"remove-paths",allusers);
412      //Now add it to the queue
413      PENDINGOTHER << pbiID[i]+":::"+cmd;
414    }
415  }
416  //Now check/start the process
417  QTimer::singleShot(0,this,SLOT(checkProcesses()) );   
418}
419
420void PBIBackend::rmMimeTypes(QStringList pbiID, bool allusers){ // remove path links
421  for(int i=0; i<pbiID.length(); i++){
422    if(PBIHASH.contains(pbiID[i])){
423      //generate the command
424      QString cmd = generateXDGCMD(pbiID[i],QStringList()<<"remove-mime",allusers);
425      //Now add it to the queue
426      PENDINGOTHER << pbiID[i]+":::"+cmd;
427    }
428  }
429  //Now check/start the process
430  QTimer::singleShot(0,this,SLOT(checkProcesses()) );   
431}
432
433void PBIBackend::enableAutoUpdate(QString pbiID, bool enable){
434  if(!PBIHASH.contains(pbiID)){return;}
435  //Generate the command
436  QString cmd = generateAutoUpdateCMD(pbiID,enable);
437  //Now put it in the queue
438  PENDINGOTHER << pbiID+":::"+cmd;
439  //Now check/start the process
440  QTimer::singleShot(0,this,SLOT(checkProcesses()) );
441}
442
443 // === Information Retrieval functions ===
444QStringList PBIBackend::PBIInfo( QString pbiID, QStringList infoList){
445  QStringList output;
446  if(!PBIHASH.contains(pbiID)){ return output; }
447  for(int i=0; i<infoList.length(); i++){
448    if(infoList[i]=="name"){ output << PBIHASH[pbiID].name; }
449    else if(infoList[i]=="version"){ output << PBIHASH[pbiID].version; }
450    else if(infoList[i]=="author"){ output << PBIHASH[pbiID].author; }
451    else if(infoList[i]=="website"){ output << PBIHASH[pbiID].website; }
452    else if(infoList[i]=="arch"){ output << PBIHASH[pbiID].arch; }
453    else if(infoList[i]=="path"){ output << PBIHASH[pbiID].path; }
454    else if(infoList[i]=="icon"){ output << PBIHASH[pbiID].icon; }
455    else if(infoList[i]=="license"){ output << PBIHASH[pbiID].license; }
456    else if(infoList[i]=="metaid"){ output << PBIHASH[pbiID].metaID; }
457    else if(infoList[i]=="status"){ output << PBIHASH[pbiID].statusString; }
458    //Now the boolians
459    else if(infoList[i]=="requiresroot"){ 
460      if(PBIHASH[pbiID].rootInstall){output<<"true";}
461      else{ output<<"false";}
462    }
463    else if(infoList[i]=="autoupdate"){ 
464      if(PBIHASH[pbiID].autoUpdate){output<<"true";}
465      else{ output<<"false";}
466    }
467    else if(infoList[i]=="hasdesktopicons"){ 
468      if(PBIHASH[pbiID].desktopIcons){output<<"true";}
469      else{ output<<"false";}
470    }
471    else if(infoList[i]=="hasmenuicons"){ 
472      if(PBIHASH[pbiID].menuIcons){output<<"true";}
473      else{ output<<"false";}
474    }
475    else{ output << ""; }
476  }
477  //qDebug()<< "Info Requested for:" << pbiID << infoList << "Info:" << output;
478  return output;
479}       
480
481QStringList PBIBackend::CatInfo( QString catID, QStringList infoList){
482  QStringList output;
483  if(!CATHASH.contains(catID)){ return output; }
484  for(int i=0; i<infoList.length(); i++){
485    if(infoList[i]=="name"){ output << CATHASH[catID].name; }
486    else if(infoList[i]=="icon"){ output << CATHASH[catID].localIcon; }
487    else if(infoList[i]=="description"){ output << CATHASH[catID].description; }   
488    else{ output << ""; }
489  }
490  //qDebug()<< "Info Requested for:" << catID << infoList << "Info:" << output;
491  return output;
492}
493
494QStringList PBIBackend::AppInfo( QString appID, QStringList infoList){
495  QStringList output;
496  if(!APPHASH.contains(appID)){ return output; }
497  for(int i=0; i<infoList.length(); i++){
498    if(infoList[i]=="name"){ output << APPHASH[appID].name; }
499    else if(infoList[i]=="author"){ output << APPHASH[appID].author; }
500    else if(infoList[i]=="website"){ output << APPHASH[appID].website; }
501    else if(infoList[i]=="icon"){ output << APPHASH[appID].localIcon; }
502    else if(infoList[i]=="license"){ output << APPHASH[appID].license; }
503    else if(infoList[i]=="type"){ output << APPHASH[appID].appType; }
504    else if(infoList[i]=="description"){ output << APPHASH[appID].description; }
505    else if(infoList[i]=="category"){ output << APPHASH[appID].category; }
506    else if(infoList[i]=="latestversion"){ output << APPHASH[appID].latestVersion; }
507    else if(infoList[i]=="latestarch"){ output << APPHASH[appID].latestArch; }
508    else if(infoList[i]=="latestsize"){ output << APPHASH[appID].latestSizeK; }
509    else if(infoList[i]=="backupversion"){ output << APPHASH[appID].backupVersion; }
510    else if(infoList[i]=="backuparch"){ output << APPHASH[appID].backupArch; }
511    else if(infoList[i]=="backupsize"){ output << APPHASH[appID].backupSizeK; }
512    //Now the boolians
513    else if(infoList[i]=="requiresroot"){ 
514      if(APPHASH[appID].requiresroot){output<<"true";}
515      else{ output<<"false";}
516    }
517    else{ output << ""; }
518  }
519  //qDebug()<< "Info Requested for:" << appID << infoList << "Info:" << output;
520  return output;
521}
522
523QString PBIBackend::currentAppStatus( QString appID ){
524  //Determine if the app is currently in a pending state
525  if(!APPHASH.contains(appID)){ return ""; }
526  QString output;
527  QStringList pbilist = PBIHASH.keys();
528  for(int i=0; i<pbilist.length(); i++){
529    if(PBIHASH[pbilist[i]].metaID == appID){
530      switch (PBIHASH[pbilist[i]].status){
531        case InstalledPBI::DOWNLOADING:
532          output = tr("Downloading"); break;
533        case InstalledPBI::INSTALLING:
534          output = tr("Installing"); break;
535        case InstalledPBI::REMOVING:
536          output = tr("Removing"); break;
537        case InstalledPBI::UPDATING:
538          output = tr("Updating"); break;
539        case InstalledPBI::PENDINGDOWNLOAD:
540          output = tr("Pending Download"); break;
541        case InstalledPBI::PENDINGINSTALL:
542          output = tr("Pending Install"); break;
543        case InstalledPBI::PENDINGREMOVAL:
544          output = tr("Pending Removal"); break;
545        case InstalledPBI::PENDINGUPDATE:
546          output = tr("Pending Update"); break;
547        default: //do nothing for the rest
548          output.clear();
549      }
550      if(!output.isEmpty()){ break; } //break out of the loop
551    }
552  }
553  return output;
554}
555
556// === Configuration Management ===
557void PBIBackend::openConfigurationDialog(){
558  //temporarily disable the filesystem watcher (causes issues with repo changes)
559  stopFileSystemWatcher();
560  //Now setup the UI
561  ConfigDialog *cfg = new ConfigDialog(parentWidget);
562  cfg->xdgOpts = autoXDG;
563  cfg->keepDownloads = keepDownloads;
564  cfg->downloadDir = baseDlDir;
565  cfg->DB = sysDB; //for access to database operations
566  cfg->setupDone();
567  //Now run it
568  cfg->exec(); //makes it modal and waits for it to finish
569  //Now see if there are changes to save
570  if(cfg->applyChanges){
571    autoXDG = cfg->xdgOpts;
572    keepDownloads = cfg->keepDownloads;
573    updateDlDirPath(cfg->downloadDir);
574    //Now save the configuration data to file
575    saveSettings();
576
577  }
578  //Now re-enable the filesystem watcher
579  startFileSystemWatcher();
580  //Now re-sync the repo info
581  syncCurrentRepo();
582}
583
584// === Import/Export PBI Lists ===
585bool PBIBackend::exportPbiListToFile(QString filepath){
586  bool ok = FALSE;
587  //get the currently installed PBI's
588  QStringList list = PBIHASH.keys();
589  QStringList installed;
590  for(int i=0; i<list.length(); i++){
591    if(!PBIHASH[list[i]].path.isEmpty()){ installed << PBIHASH[list[i]].metaID; }         
592  }
593  qDebug() << "Export List:" << installed;
594  //Now save the list
595  if(installed.isEmpty()){ ok = TRUE; }
596  else{ ok = Extras::writeFile(filepath,installed); }
597  return ok;
598}
599
600bool PBIBackend::importPbiListFromFile(QString filepath){
601  bool ok = FALSE;
602  if(!QFile::exists(filepath)){ return ok; }
603  QStringList inlist = Extras::readFile(filepath);
604  if(inlist.isEmpty()){ return ok; }
605  qDebug() << "Import List:" << inlist;
606  //Now sort the apps based on validity
607  QStringList good, bad, current; 
608  for(int i=0; i<inlist.length(); i++){
609    QString app = inlist[i].toLower();
610    if(app.isEmpty()){continue;}
611    else if(!APPHASH.contains(app)){ bad << inlist[i]; }
612    else if( !isInstalled(app).isEmpty() ){ current << inlist[i]; }
613    else{ good << app; } //make sure to use the appID here
614  }
615  ok = TRUE;
616  //List the results
617  if(good.isEmpty()){
618    QString results = tr("No applications to install from this list.")+"\n"+tr("Results:")+"\n";
619    if(!bad.isEmpty()){ results.append( QString(tr("Unavailable Apps: %1")).arg(bad.join(",  "))+"\n" ); }
620    if(!current.isEmpty()){ results.append( QString(tr("Currently Installed: %1")).arg(current.join(",  "))+"\n" ); }
621    QMessageBox::information(0,tr("Import Results"),results);
622  }else{
623    QString results = tr("Are you sure you wish to install these applications?")+"\n\n"+good.join(", ");
624    if( QMessageBox::question(0,tr("Import Results"),results,QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes){
625      //Go ahead and install these applications
626      installApp(good);
627    }
628  }
629  return ok;
630}
631
632 // ==========================
633 // ====== PUBLIC SLOTS ======
634 // ==========================
635 // NOTE: These functions should only be called from a QTimer::singleShot()
636 //    Because outputs come via different signals (due to time for these functions to run)
637 
638void PBIBackend::startAppSearch(){
639 //  Outputs come via the "SearchComplete(QStringList best,QStringList rest)" signal
640 QString search = searchTerm; //This public variable needs to be set beforehand by the calling process
641 QStringList best, rest;
642 //Now perform the search and categorize it
643 search = search.toLower();
644 QStringList namematch, tagmatch, descmatch;
645 QStringList app = APPHASH.keys();
646 for(int i=0; i<app.length(); i++){
647   if(APPHASH[app[i]].name.toLower() == search){ best << app[i]; } //exact match - top of the "best" list
648   else if(APPHASH[app[i]].name.toLower().contains(search)){ namematch << app[i]; }
649   else if(APPHASH[app[i]].tags.contains(search)){ tagmatch << app[i]; }
650   else if(APPHASH[app[i]].description.contains(search)){ descmatch << app[i]; }
651 }
652 //Now sort the lists and assign a priority
653 namematch.sort(); tagmatch.sort(); descmatch.sort();
654 best << namematch;
655 if(best.isEmpty()){ best << tagmatch; }
656 else{ rest << tagmatch; }
657 if(best.isEmpty()){ best << descmatch; }
658 else{ rest << descmatch; }
659
660 //Now emit the signal with the results
661 emit SearchComplete(best,rest);
662}
663
664void PBIBackend::startSimilarSearch(){
665  //  Outputs come via the "SimilarFound(QStringList results)" signal
666  QString sID = searchSimilar; // this public variable needs to be set beforehand by the calling process
667  QStringList output; 
668  if(!APPHASH.contains(sID)){ return; } 
669  //Now find the tags on the given ID
670  QStringList stags = APPHASH[sID].tags;
671  QStringList apps = APPHASH.keys();
672  QStringList unsorted;
673  int maxMatch=0;
674  for(int i=0; i<apps.length(); i++){
675    if(apps[i]==sID){continue;} //skip the app we were given for search parameters
676    QStringList tags = APPHASH[apps[i]].tags;
677    int match=0;
678    for(int j=0; j<stags.length(); j++){
679       if(tags.indexOf(stags[j]) != -1){ match++; }
680    }
681    if(match > 1){ unsorted << QString::number(match)+"::"+apps[i]; }
682    if(match > maxMatch){ maxMatch = match; }
683  }
684  unsorted.sort();
685  for(int i=unsorted.length(); i>0; i--){
686    if( unsorted[i-1].section("::",0,0).toInt() > (maxMatch-1) ){
687      output << unsorted[i-1].section("::",1,50);
688    }else if( output.length() < 5 ){ //not enough matches, grab the next set too
689      output << unsorted[i-1].section("::",1,50);
690      maxMatch--;
691    }else{ break; } //already sorted and the rest are even lower match rating
692  }
693  //Now emit the signal with the results
694  emit SimilarFound(output);
695}
696
697 // ===============================
698 // ====== PRIVATE FUNCTIONS ======
699 // ===============================
700bool PBIBackend::saveSettings(){
701  //Generate the file layout
702  QStringList file;
703  file << "POSTINSTALL="+autoXDG.join(",").simplified();
704  file << "REPONUMBER="+sysDB->currentRepo();
705  if(keepDownloads){ file << "KEEPDOWNLOADS=TRUE"; }
706  else{ file << "KEEPDOWNLOADS=FALSE"; }
707  file << "DOWNLOADDIR="+baseDlDir;
708  //Now save the file
709  bool ok = Extras::writeFile(settingsFile, file);
710  return ok;
711}
712
713bool PBIBackend::loadSettings(){
714  if(!QFile::exists(settingsFile)){ return FALSE; }
715  QStringList file = Extras::readFile(settingsFile);
716  if(file.isEmpty()){ return FALSE; }
717  for(int i=0; i<file.length(); i++){
718    if(file[i].startsWith("POSTINSTALL=")){ autoXDG = file[i].section("=",1,1).split(","); }
719    else if(file[i].startsWith("REPONUMBER=")){ sysDB->setRepo(file[i].section("=",1,1)); }
720    else if(file[i].startsWith("KEEPDOWNLOADS=")){ 
721      if(file[i].section("=",1,1) == "TRUE"){ keepDownloads = TRUE; }
722      else{ keepDownloads = FALSE; }
723    }else if(file[i].startsWith("DOWNLOADDIR=")){ updateDlDirPath(file[i].section("=",1,1)); }
724  }
725  return TRUE;
726}
727 
728 QString PBIBackend::addRootCMD(QString cmd, bool needRoot){
729   //Check for warden and root permissions and adjust the command accordingly
730   if(wardenMode){
731     cmd.prepend("warden chroot "+wardenIP+" \""); cmd.append("\"");
732   }else if( needRoot ){
733     cmd.prepend("pc-su \""); cmd.append("\"");     
734   }
735   return cmd;
736 }
737 
738 QString PBIBackend::generateUpdateCMD(QString pbiID){
739   QString output;
740   if(!PBIHASH.contains(pbiID)){ return output; }
741   output = "pbi_update "+pbiID;
742   output = addRootCMD(output, PBIHASH[pbiID].rootInstall);
743   return output;
744 }
745 
746 QString PBIBackend::generateRemoveCMD(QString pbiID){
747   QString output;
748   if(!PBIHASH.contains(pbiID)){ return output; }
749   output = "pbi_delete "+pbiID;
750   output = addRootCMD(output, PBIHASH[pbiID].rootInstall);
751   return output;       
752 }
753 
754 QString PBIBackend::generateAutoUpdateCMD(QString pbiID, bool enable){
755   QString output;
756   if(!PBIHASH.contains(pbiID)){ return output; }
757   output = "pbi_update";
758   if(enable){ output.append(" --enable-auto"); }
759   else{ output.append(" --disable-auto"); }
760   output.append(" "+pbiID);
761   output = addRootCMD(output, PBIHASH[pbiID].rootInstall);
762   return output;       
763 }
764
765 QString PBIBackend::generateXDGCMD(QString pbiID, QStringList actions, bool allusers){
766   QString output;
767   if(!PBIHASH.contains(pbiID)){ return output; }
768   output = "pbi_icon";
769   if(actions.contains("desktop")){ output.append(" add-desktop"); }
770   if(actions.contains("menu")){ output.append(" add-menu"); }
771   if(actions.contains("paths")){ output.append(" add-pathlnk"); }
772   if(actions.contains("mime")){ output.append(" add-mime"); }
773   if(actions.contains("remove-desktop")){ output.append(" del-desktop"); }
774   if(actions.contains("remove-menu")){ output.append(" del-menu"); }
775   if(actions.contains("remove-paths")){ output.append(" del-pathlnk"); }
776   if(actions.contains("remove-mime")){ output.append(" del-mime"); }
777   //Check command actions
778   if(output == "pbi_icon"){ 
779     output.clear(); //no actions - do nothing
780   }else{
781     output.append(" "+pbiID);
782     output = addRootCMD(output,allusers);
783   }
784   return output;       
785 }
786 
787 QString PBIBackend::generateDownloadCMD(QString appID, QString version){
788   if(!APPHASH.contains(appID)){ return ""; }
789   QString output = "pbi_add -R --repo "+sysDB->currentRepo();
790   if(!version.isEmpty()){ output.append(" --rVer "+version); }
791   output.append(" "+appID);
792   return output;
793 }
794 
795 QString PBIBackend::generateInstallCMD(QString filename){
796   QString output = "pbi_add --licagree "+filename;
797   return output;
798 }
799 
800 QStringList PBIBackend::removePbiCMD(QString pbiID, QStringList list){
801   //Used for removing any pending CMD's for a particular pbiID
802   QStringList output;
803   for(int i=0; i<list.length(); i++){
804     if(!list[i].startsWith(pbiID)){ output << list[i]; }         
805   }
806   return output;
807 }
808 
809 // ===============================
810 // ======   PRIVATE SLOTS   ======
811 // ===============================
812 void PBIBackend::updateDlDirPath(QString newbase){
813   baseDlDir = newbase;
814   if(wardenMode){ 
815     dlDir = wardenDir + baseDlDir;
816   }else{ 
817     dlDir = baseDlDir; 
818   }
819   if(!dlDir.endsWith("/")){ dlDir.append("/"); } 
820 }
821 
822 // Internal Process Management
823 void PBIBackend::checkProcesses(){
824   bool again=FALSE;
825   if( cDownload.isEmpty() && !PENDINGDL.isEmpty() ){
826     //internal management
827     cDownload = PENDINGDL[0].section(":::",0,0); //should be a pbiID -ONLY-
828     QString cmd = PENDINGDL[0].section(":::",1,50);
829     PENDINGDL.removeAt(0);       
830     if( !cmd.isEmpty() && PBIHASH.contains(cDownload) ){
831       //Update the PBI status
832       PBIHASH[cDownload].setStatus(InstalledPBI::DOWNLOADING);
833       emit PBIStatusChange(cDownload);
834       //Start the process
835       PMAN->goToDirectory(ProcessManager::DOWNLOAD,dlDir);
836       PMAN->startProcess(ProcessManager::DOWNLOAD,cmd);
837     }else{
838       cDownload.clear();
839       again=TRUE; //Move to the next pending download
840     }
841   }
842   if( cInstall.isEmpty() && !PENDINGINSTALL.isEmpty() ){
843     //internal management
844     cInstall = PENDINGINSTALL[0].section(":::",0,0); //should be a pbiID -ONLY-
845     QString cmd = PENDINGINSTALL[0].section(":::",1,50);
846     PENDINGINSTALL.removeAt(0);         
847     if( !cmd.isEmpty() && PBIHASH.contains(cInstall) ){
848       //Update the PBI status
849       PBIHASH[cInstall].setStatus(InstalledPBI::INSTALLING);
850       emit PBIStatusChange(cInstall);
851       //Start the process
852       PMAN->goToDirectory(ProcessManager::INSTALL,dlDir);
853       PMAN->startProcess(ProcessManager::INSTALL,cmd);
854     }else{
855       cInstall.clear();
856       again=TRUE; //Move to the next pending install
857     }
858   }
859   if( cRemove.isEmpty() && !PENDINGREMOVAL.isEmpty() ){
860     //internal management
861     cRemove = PENDINGREMOVAL[0].section(":::",0,0); //should be a pbiID -ONLY-
862     QString cmd = PENDINGREMOVAL[0].section(":::",1,50);
863     PENDINGREMOVAL.removeAt(0);         
864     if( !cmd.isEmpty() && PBIHASH.contains(cRemove) ){
865       //Update the PBI status
866       PBIHASH[cRemove].setStatus(InstalledPBI::REMOVING);
867       emit PBIStatusChange(cRemove);
868       //Start the process
869       PMAN->startProcess(ProcessManager::REMOVE,cmd);
870     }else{
871       cRemove.clear();
872       again=TRUE; //Move to the next pending removal
873     }
874   }
875   if( cUpdate.isEmpty() && !PENDINGUPDATE.isEmpty() ){
876     //internal management
877     cUpdate = PENDINGUPDATE[0].section(":::",0,0); //should be a pbiID -ONLY-
878     QString cmd = PENDINGUPDATE[0].section(":::",1,50);
879     PENDINGUPDATE.removeAt(0);   
880     if( !cmd.isEmpty() && PBIHASH.contains(cUpdate) ){
881       //Update the PBI status
882       PBIHASH[cUpdate].setStatus(InstalledPBI::UPDATING);
883       emit PBIStatusChange(cUpdate);
884       //Start the process
885       PMAN->startProcess(ProcessManager::UPDATE,cmd);
886     }else{
887       cUpdate.clear();
888       again=TRUE; //Move to the next pending update
889     }
890   }
891   if( cOther.isEmpty() && !PENDINGOTHER.isEmpty() ){
892     //internal management
893     cOther = PENDINGOTHER[0].section(":::",0,0); //should be a pbiID -ONLY-
894     QString cmd = PENDINGOTHER[0].section(":::",1,50);
895     PENDINGOTHER.removeAt(0);   
896     if( !cmd.isEmpty() && PBIHASH.contains(cOther) ){
897       //Update the PBI status
898       PBIHASH[cOther].setStatus(InstalledPBI::WORKING);
899       emit PBIStatusChange(cOther);
900       //Start the process
901       PMAN->startProcess(ProcessManager::OTHER,cmd);
902     }else{
903       cOther.clear();
904       again=TRUE; //Move to the next pending command
905     }
906   }
907   if(again){ QTimer::singleShot(10,this,SLOT(checkProcesses()) ); }
908 }
909 
910 void PBIBackend::slotProcessFinished(int ID){
911   bool resync = FALSE;
912   if(ID == ProcessManager::UPDATE){
913     cUpdate.clear(); //remove that it is finished
914     sUpdate=FALSE;
915     resync=TRUE;
916   }else if(ID == ProcessManager::REMOVE){
917     sRemove=FALSE;
918     cRemove.clear(); //remove that it is finished         
919   }else if(ID == ProcessManager::INSTALL){
920     //Add XDG commands to the queue
921     if(!sInstall){  // do not continue on if it was cancelled
922       qDebug() << "Installation Finished:" << cInstall;
923       if(!keepDownloads){ QFile::remove(dlDir+PBIHASH[cInstall].downloadfile); }
924       //Generate XDG commands
925       QString cmd = generateXDGCMD(cInstall, autoXDG, FALSE);
926       PENDINGOTHER << cInstall+":::"+cmd;
927     }
928     sInstall = FALSE;
929     cInstall.clear(); //remove that it is finished
930     resync=TRUE; //make sure to reload local files
931   }else if(ID == ProcessManager::DOWNLOAD){
932     //Make sure the download was successful
933     //qDebug() << "dlDir:" << dlDir << "file:" << PBIHASH[cDownload].downloadfile;
934     if(!sDownload){  // do not continue on if it was cancelled
935       if(!QFile::exists(dlDir+PBIHASH[cDownload].downloadfile)){
936         qDebug() << "Download Error:" << cDownload << PBIHASH[cDownload].downloadfile;
937         QString title = QString(tr("%1 Download Error:")).arg(PBIHASH[cDownload].name);
938         QString err = tr("The PBI could not be downloaded, please try again later");
939         emit Error(title,err);
940       }else{
941         //Now Check to see if an alternate version needs to be removed
942         QString otherID = isInstalled( Extras::nameToID(PBIHASH[cDownload].name) );
943         QString cmd;
944         if(!otherID.isEmpty()){
945           cmd = generateRemoveCMD(otherID);
946           PENDINGINSTALL << otherID+":::"+cmd; //make sure it happens before the install, so put it in the same queue
947         }
948         //Now add the installation of this PBI to the queue
949         cmd = generateInstallCMD(PBIHASH[cDownload].downloadfile);
950         PENDINGINSTALL << cDownload+":::"+cmd;
951       }
952     }
953     sDownload = FALSE;
954     cDownload.clear(); //remove that it is finished   
955   }else if(ID == ProcessManager::OTHER){
956     cOther.clear();       
957   }
958   //Get the next processes going
959   slotSyncToDatabase(resync); //update internal database with/without reading local files again
960   QTimer::singleShot(0,this,SLOT(checkProcesses()) ); //look for more processes to start
961 }
962 
963void PBIBackend::slotProcessMessage(int ID, QString dlinfo){
964   if(ID == ProcessManager::UPDATE){
965     PBIHASH[cUpdate].setStatus(InstalledPBI::UPDATING, dlinfo); 
966     emit PBIStatusChange(cUpdate);
967   }else if(ID == ProcessManager::DOWNLOAD){
968     PBIHASH[cDownload].setStatus(InstalledPBI::DOWNLOADING, dlinfo); 
969     emit PBIStatusChange(cDownload);
970   }   
971}
972
973void PBIBackend::slotProcessError(int ID, QString err){
974   QString title;
975   QString name;
976   if(ID == ProcessManager::UPDATE){
977     if(!sUpdate){ //not stopped manually
978       if(PBIHASH.contains(cUpdate)){name = PBIHASH[cUpdate].name; }
979       title = QString(tr("%1 Update Error:")).arg(name); 
980     }
981   }
982   else if(ID == ProcessManager::INSTALL){ 
983     if(!sInstall){ //not stopped manually
984       if(APPHASH.contains(cInstall)){name = APPHASH[cInstall].name; }
985       title = QString(tr("%1 Installation Error:")).arg(name);
986     }
987   }
988   else if(ID == ProcessManager::REMOVE){ 
989     if(!sRemove){ //not stopped manually
990       if(PBIHASH.contains(cRemove)){name = PBIHASH[cRemove].name; }
991       title = QString(tr("%1 Removal Error:")).arg(name);
992     }
993   }
994   else if(ID == ProcessManager::DOWNLOAD){ 
995     if(!sDownload){ //not stopped manually
996       if(APPHASH.contains(cDownload)){name = APPHASH[cDownload].name; }
997       title = QString(tr("%1 Download Error:")).arg(name);
998     }
999   }
1000   else if(ID == ProcessManager::OTHER){ 
1001     if(PBIHASH.contains(cOther)){name = PBIHASH[cOther].name; }
1002     title = QString(tr("%1 PBI Error:")).arg(name); 
1003   }
1004   if(!title.isEmpty() && !err.isEmpty()){
1005     qDebug() << "Process Error:" << title << err;
1006     emit Error(title,err); //send error signal
1007   }
1008   slotProcessFinished(ID); //clean up
1009}
1010
1011 // === Database Synchronization ===
1012 void PBIBackend::slotSyncToDatabase(bool localChanges){
1013   //qDebug() << "Sync Database with local changes:" << localChanges;
1014   //Locally Installed PBI Changes
1015   QStringList currInst = installedList();
1016   QStringList sysList = sysDB->installed();
1017   numInstalled = sysList.length();
1018   //All locally installed applications
1019   for(int i=0; i<sysList.length(); i++){
1020     int index = currInst.indexOf(sysList[i]);
1021     if( index == -1){ //New entry
1022       PBIHASH.insert(sysList[i],InstalledPBI()); //add a blank entry
1023       syncPBI(sysList[i],TRUE); //Now update the info
1024       //Add it to the watcher
1025       watcher->addPath(DBDir+"installed/"+sysList[i]);
1026     }else{  //Existing entry - remove it from the currInst list
1027       if(localChanges){ syncPBI(sysList[i],TRUE); } //synchronize the data with local file changes
1028       else{ updateStatus(sysList[i]); } //just update the status
1029       currInst.removeAt(index);
1030     }     
1031   }
1032   //Non-Installed applications
1033   for(int i=0; i<currInst.length(); i++){
1034     updateStatus(currInst[i]); //update status
1035     InstalledPBI::PBISTATUS stat = PBIHASH[currInst[i]].status;
1036     bool actionPending = (stat != InstalledPBI::NONE) && (stat != InstalledPBI::UPDATEAVAILABLE);
1037     if( !actionPending ){ PBIHASH.remove(currInst[i]); }
1038     else{} //do nothing here
1039   }
1040   emit LocalPBIChanges(); //Let others know that the local PBI's have been updated
1041   //Repo Changes
1042   //qDebug() << "noRepo" << noRepo << "Invalid Repo:" << sysDB->currentRepo() << sysDB->currentRepoInvalid();
1043   if( noRepo || sysDB->currentRepoInvalid() ){
1044     //Change to the first repo available
1045     qDebug() << "Try to find an alternate Repo:";
1046     QStringList repos = sysDB->availableRepos();
1047     if(repos.length() > 0){ 
1048       sysDB->setRepo(repos[0]);
1049     }
1050     syncCurrentRepo();
1051   }else if( CATHASH.isEmpty() && APPHASH.isEmpty() ){
1052     qDebug() << "Load Repo Information";
1053     //If successful, the repo data should only be loaded once
1054     syncCurrentRepo(); 
1055     //If the sync was successful, re-run the PBI sync process to update the license info
1056     if( !APPHASH.isEmpty() ){               
1057       QTimer::singleShot(10,this,SLOT(slotSyncToDatabase())); 
1058     }else{
1059       QTimer::singleShot(60000,this,SLOT(slotSyncToDatabase())); //try again in a minute 
1060     }
1061   }
1062 }
1063 
1064 void PBIBackend::syncPBI(QString pbiID, bool useDB){
1065   //This function is mainly used for initializing the PBIHASH entry
1066   //  but is also used for updating the entry if the local installation settings change (useDB=true)
1067         
1068   //useDB: pull info from the locally installed database (pbiID MUST be installed locally)
1069   //Get the PBI structure
1070   if( !PBIHASH.contains(pbiID) ){ return; }
1071   InstalledPBI pbi = PBIHASH[pbiID];
1072   //Get the associated appID
1073   QString appID = pbi.metaID;
1074   QStringList info = sysDB->installedPbiInfo(pbiID); //info[name,version,arch,date,author,web,path,icon]
1075   if(useDB && !info.isEmpty()){
1076     //Now get additional database info
1077     bool autoUp = sysDB->installedPbiAutoUpdate(pbiID);
1078     bool root = sysDB->installedPbiNeedsRoot(pbiID);
1079     bool desktop = sysDB->installedPbiHasXdgDesktop(info[6]);
1080     bool menu = sysDB->installedPbiHasXdgMenu(info[6]);
1081     //Now add this info to the PBI structure
1082     pbi.name    = info[0];
1083     pbi.version = info[1];
1084     pbi.arch    = info[2];
1085     pbi.mdate   = info[3];
1086     pbi.author  = info[4];
1087     pbi.website = info[5];
1088     if(pbi.website.endsWith("/")){ pbi.website.chop(1); }
1089     pbi.path    = info[6];
1090     pbi.icon    = info[7];
1091     if(appID.isEmpty()){ 
1092       appID = Extras::nameToID(pbi.name); 
1093       pbi.metaID = appID;
1094     } //for new item initialization
1095     if(APPHASH.contains(appID)){
1096       pbi.license = APPHASH[appID].license;       
1097     }else{
1098       pbi.license = tr("Unknown");
1099     }
1100     pbi.rootInstall = root;
1101     pbi.autoUpdate  = autoUp;
1102     pbi.desktopIcons= desktop;
1103     pbi.menuIcons   = menu;
1104     
1105   }else{
1106     //Pull basic info from the pre-loaded App database instead
1107     // This is for application entries still in a pending state and not fully installed
1108     if(!APPHASH.contains(appID)){ return; }
1109     pbi.name = APPHASH[appID].name;
1110     //Do not change the version or arch - this is usually why the app is in a pending state
1111     pbi.author = APPHASH[appID].author;
1112     pbi.website = APPHASH[appID].website;
1113     pbi.icon = APPHASH[appID].localIcon;
1114     pbi.license = APPHASH[appID].license;
1115     pbi.rootInstall = APPHASH[appID].requiresroot;
1116     pbi.autoUpdate=FALSE;
1117     pbi.desktopIcons=FALSE;
1118     pbi.menuIcons=FALSE;
1119     if(pbi.metaID.isEmpty()){ pbi.metaID = appID; }
1120   }
1121   //Now add this pbi structure back into the hash
1122   PBIHASH.insert(pbiID, pbi); 
1123   //Now update the status
1124   updateStatus(pbiID);
1125 }
1126 
1127 void PBIBackend::slotUpdateAllStatus(){
1128   QStringList pbiID = PBIHASH.keys();
1129   for(int i=0; i<pbiID.length(); i++){
1130     updateStatus(pbiID[i]);     
1131   }
1132 }
1133 
1134 void PBIBackend::updateStatus(QString pbiID){
1135   if(!PBIHASH.contains(pbiID)){ return; }
1136   QString upgrade = upgradeAvailable(pbiID);
1137   QString chk = pbiID+":::"; //for list checking
1138   QString iIndex = PENDINGINSTALL.filter(chk).join(" "); //special case for install list
1139   if(cDownload == pbiID){PBIHASH[pbiID].setStatus(InstalledPBI::DOWNLOADING);}
1140   else if(cInstall == pbiID){PBIHASH[pbiID].setStatus(InstalledPBI::INSTALLING);}
1141   else if(cRemove == pbiID){PBIHASH[pbiID].setStatus(InstalledPBI::REMOVING);}
1142   else if(cUpdate == pbiID){PBIHASH[pbiID].setStatus(InstalledPBI::UPDATING);}
1143   //Look through the pending lists
1144   else if(PENDINGDL.join(" ").contains(chk)){PBIHASH[pbiID].setStatus(InstalledPBI::PENDINGDOWNLOAD);}
1145   else if(!iIndex.isEmpty()){ //install queue can also have special-case removals
1146     if(iIndex.contains("pbi_delete")){ PBIHASH[pbiID].setStatus(InstalledPBI::PENDINGREMOVAL); }
1147     else{ PBIHASH[pbiID].setStatus(InstalledPBI::PENDINGINSTALL); }
1148   }else if(PENDINGREMOVAL.join(" ").contains(chk)){PBIHASH[pbiID].setStatus(InstalledPBI::PENDINGREMOVAL);}
1149   else if(PENDINGUPDATE.join(" ").contains(chk)){PBIHASH[pbiID].setStatus(InstalledPBI::PENDINGUPDATE);}
1150   //else if(PENDINGOTHER.join(" ").contains(chk)){PBIHASH[pbiID].setStatus(InstalledPBI::WORKING);}
1151   else if( !upgrade.isEmpty() ){PBIHASH[pbiID].setStatus(InstalledPBI::UPDATEAVAILABLE, upgrade); }
1152   else{ PBIHASH[pbiID].setStatus(InstalledPBI::NONE); }
1153 }
1154 
1155 void PBIBackend::syncCurrentRepo(){
1156  //Calling this function will automatically clear and re-populate the APPHASH and CATHASH fields
1157  APPHASH.clear(); CATHASH.clear();
1158  QString mFile = sysDB->metaFilePath(); 
1159  QString iFile = sysDB->indexFilePath();
1160  if( QFile::exists(mFile) && QFile::exists(iFile) ){
1161   //First do the meta data (app/cat info)
1162   QStringList metaFile = Extras::readFile(mFile);
1163   QStringList catsUsed, catsAvail;
1164   //qDebug() << "Sync Meta File Info";
1165   for(int i=0; i<metaFile.length(); i++){
1166     if(metaFile[i].startsWith("App=")){
1167       QStringList info = sysDB->parseAppMetaLine(metaFile[i].section("=",1,50,QString::SectionSkipEmpty));
1168       //qDebug() << "App Meta data:" << info;
1169       if(!info.isEmpty()){ //Make sure the info list is valid
1170         //info[name,category,remoteIcon,author,website,license,apptype,tags,description,requiresroot] (5/1/2013)
1171         //Simplify a couple pieces of info
1172         QString metaID = Extras::nameToID(info[0]);
1173         QString localIcon = sysDB->remoteToLocalIcon(info[0],info[2]);
1174         //Create the container and fill it
1175         MetaPBI app;
1176         app.name=info[0]; app.category=info[1]; app.remoteIcon=info[2];
1177         app.localIcon=localIcon; app.author=info[3]; app.website=info[4];
1178         app.license=info[5]; app.appType=info[6]; app.tags=info[7].toLower().split(","); 
1179         app.description=info[8];
1180         if(info[9]=="true"){ app.requiresroot=TRUE; }
1181         else{ app.requiresroot=FALSE; }
1182         //Fix the website if needed
1183         if(app.website.endsWith("/")){ app.website.chop(1); }
1184         //Add it to the hash
1185         APPHASH.insert(metaID,app);
1186       }
1187     }else if(metaFile[i].startsWith("Cat=")){
1188       QStringList info = sysDB->parseCatMetaLine(metaFile[i].section("=",1,50,QString::SectionSkipEmpty));
1189       //qDebug() << "Cat Meta Data:" << info;
1190       if(info.length() > 2){ //Make sure the info list is complete
1191         //info[name,remoteicon,description,?] (5/1/2013)
1192         QString catID = Extras::nameToID(info[0]);
1193         QString localIcon = sysDB->remoteToLocalIcon(info[0],info[1]);
1194         //Create the container and fill it
1195         MetaCategory cat;
1196         cat.name=info[0]; cat.remoteIcon=info[1]; cat.localIcon=localIcon; cat.description=info[2];
1197         //Add it to the available list
1198         catsAvail << catID;
1199         //Add it to the hash
1200         CATHASH.insert(catID,cat);
1201       }
1202     }
1203     //qDebug() << " - done with meta line";
1204   }
1205   //qDebug() << "Sync Index File Info";
1206   //Then do the list of available PBI's
1207   QStringList indexFile = Extras::readFile(iFile);
1208   bool sys64 = (sysArch=="amd64");
1209   for(int i=0; i<indexFile.length(); i++){
1210     QStringList info = sysDB->parseIndexLine(indexFile[i]);
1211       //info[name, arch, version, datetime, size, isLatest(true/false)]
1212     if(!info.isEmpty()){
1213       QString metaID = Extras::nameToID(info[0]);
1214       if(APPHASH.contains(metaID)){
1215         bool islatest = (info[5]=="true");
1216         bool is64 = (info[1] == "amd64");
1217         bool save=FALSE;
1218         //Determine whether to save the data or not
1219         if( !sys64 && is64 ){}   // do not save 64-bit apps on a 32-bit system
1220         else if(info[1] == sysArch){ save=TRUE; }  // high priority for identical architecture
1221         else{ //lower priority for 32-bit App on 64-bit system
1222           if(islatest){ 
1223             if(APPHASH[metaID].latestDatetime.isEmpty()){ save=TRUE; } //nothing saved yet, go ahead
1224             else if(APPHASH[metaID].latestArch==sysArch){} // do not save over an app that is the proper arch
1225             else if(Extras::newerDateTime(info[3], APPHASH[metaID].latestDatetime) ){ save=TRUE; } //save over older app
1226           }else{
1227             if(APPHASH[metaID].backupDatetime.isEmpty()){ save=TRUE; } //nothing saved yet, go ahead
1228             else if(APPHASH[metaID].backupArch==sysArch){} // do not save over an app that is the proper arch
1229             else if(Extras::newerDateTime(info[3], APPHASH[metaID].backupDatetime) ){ save=TRUE; } //save over older app
1230           }
1231         }
1232         //Save the data if appropriate
1233         if(save && islatest){
1234           APPHASH[metaID].latestVersion=info[2];
1235           APPHASH[metaID].latestDatetime=info[3];
1236           APPHASH[metaID].latestArch=info[1]; 
1237           APPHASH[metaID].latestSizeK=info[4];
1238           APPHASH[metaID].latestFilename = info[6];
1239         }else if(save){
1240           APPHASH[metaID].backupVersion=info[2];
1241           APPHASH[metaID].backupDatetime=info[3];
1242           APPHASH[metaID].backupArch=info[1]; 
1243           APPHASH[metaID].backupSizeK=info[4];
1244           APPHASH[metaID].backupFilename=info[6];
1245         } 
1246         //if(save){ qDebug() << "APP:" << metaID << info[1] << info[2] << info[3] << info[4] << info[5]; }
1247       } //end check for ID in hash
1248     } //end check for info available
1249   } //end loop over index file
1250   
1251   //Now clean up the APPHASH to remove any entries that do not have a PBI associated with them
1252   QStringList apps = APPHASH.keys();
1253   for(int i=0; i<apps.length(); i++){
1254     if(APPHASH[apps[i]].latestVersion.isEmpty()){
1255       //Make sure there is not a backup version we can use instead
1256       if(APPHASH[apps[i]].backupVersion.isEmpty()){
1257         APPHASH.remove(apps[i]);           
1258       }else{
1259         //Move over the backup to the latest
1260         APPHASH[apps[i]].latestVersion=  APPHASH[apps[i]].backupVersion;
1261         APPHASH[apps[i]].latestDatetime= APPHASH[apps[i]].backupDatetime;
1262         APPHASH[apps[i]].latestArch=     APPHASH[apps[i]].backupArch; 
1263         APPHASH[apps[i]].latestSizeK=    APPHASH[apps[i]].backupSizeK;
1264         APPHASH[apps[i]].latestFilename= APPHASH[apps[i]].backupFilename;
1265         //Now clear the backup
1266         APPHASH[apps[i]].backupVersion.clear();
1267         APPHASH[apps[i]].backupDatetime.clear();
1268         APPHASH[apps[i]].backupArch.clear(); 
1269         APPHASH[apps[i]].backupSizeK.clear();
1270         APPHASH[apps[i]].backupFilename.clear();
1271       }
1272     }else{
1273       //Category being used, remove this category from the list
1274       int catID = catsAvail.indexOf( Extras::nameToID(APPHASH[apps[i]].category) );
1275       if(catID != -1){ catsAvail.removeAt(catID); }
1276     }
1277   }
1278   //Now remove any empty categories (ones left over in catAvail list)
1279   for(int i=0; i<catsAvail.length(); i++){
1280     if( CATHASH.contains(catsAvail[i]) ){
1281       //qDebug() << " - Empty category:" << catsAvail[i];
1282       CATHASH.remove(catsAvail[i]); 
1283     }
1284   }
1285  } // end check for both files existing
1286 
1287  if(APPHASH.isEmpty() && CATHASH.isEmpty()){
1288    numAvailable = -1;
1289    emit NoRepoAvailable();       
1290  }else{
1291    numAvailable = QStringList(APPHASH.keys()).length(); //update the number of apps available
1292    emit RepositoryInfoReady();
1293  }
1294 }
1295 
1296void PBIBackend::startFileSystemWatcher(){
1297   QStringList watched = watcher->directories();
1298   if(!watched.isEmpty()){ watcher->removePaths(watched); }//clear the watcher first
1299   watcher->addPath( DBDir+"installed" ); //look for installed app changes
1300   watcher->addPath( DBDir+"repos" ); //look for repo changes
1301   watcher->addPath( DBDir+"index" ); //look for index/meta file changes         
1302}
1303
1304void PBIBackend::stopFileSystemWatcher(){
1305   QStringList watched = watcher->directories();
1306   if(!watched.isEmpty()){ watcher->removePaths(watched); }//clear the watcher first     
1307}
Note: See TracBrowser for help on using the repository browser.