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

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

Merge the new AppCafe? into the src-qt4 tree from the projects directory

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