source: src-qt4/pc-softwaremanager/pbiDBAccess.cpp @ 32be23f

releng/10.0.1releng/10.0.2releng/10.0.3
Last change on this file since 32be23f was 32be23f, checked in by Ken Moore <ken@…>, 7 months ago

Clean up the AppCafe? browser home page - now just have a toolbutton to browser categories, and instead put recommended applications on the home page (new defaultrecommendations.txt resource). These recommendations can be automatically updated later if we add that ability for the repo to include an additional file with this information.

  • Property mode set to 100644
File size: 15.5 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 #include "pbiDBAccess.h"
27
28// ================================
29// =======  SETUP FUNCTIONS =======
30// ================================
31bool PBIDBAccess::setDBPath(QString fullPath){
32  bool ok = FALSE;
33  //Make sure the directory exists first
34  if(QFile::exists(fullPath)){
35    DBPath = fullPath;
36    if(!DBPath.endsWith("/")){DBPath.append("/");}
37    DBDir->setPath(fullPath);
38    //Now read the list of available repos
39    reloadRepoList();
40    ok = TRUE;
41  }
42  return ok;
43}
44
45void PBIDBAccess::setRootCMDPrefix(QString prefix){
46  //All commands need root permissions to run, so this is used
47  //    to add the proper cmd prefix to prompt to run with root permissions
48  // Example: "pc-su" or "warden chroot <ip>"
49  cmdPrefix = prefix;
50  if(!cmdPrefix.endsWith(" ")){ cmdPrefix.append(" "); }
51  //qDebug() << "DB command prefix:" << cmdPrefix;
52}
53
54void PBIDBAccess::reloadRepoList(){
55  repoList.clear();
56  if(DBDir->cd(DBPath+"repos")){ //directory exists
57    repoList = DBDir->entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Readable);
58  }     
59}
60
61bool PBIDBAccess::setRepo(QString repoNum){
62  //Make sure the repo is available
63  bool ok = DBDir->cd(DBPath+"repos");
64  if(ok){ //directory exists
65    QStringList rL = DBDir->entryList(QStringList()<<repoNum+"*",QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Readable);
66    if(rL.length() == 1){ //this repo exists
67      currentRepoNumber=rL[0].section(".",0,0,QString::SectionSkipEmpty);
68      currentRepoID=rL[0].section(".",1,1,QString::SectionSkipEmpty);
69      return TRUE;
70    }
71  }
72  return FALSE;
73}
74
75bool PBIDBAccess::currentRepoInvalid(){
76  //Check to make sure that the current Repo is still valid
77  reloadRepoList();
78  if(repoList.contains(currentRepoNumber+"."+currentRepoID)){ return FALSE; }
79  else{ return TRUE; }
80}
81
82QStringList PBIDBAccess::availableRepos(){
83  QStringList output;
84  for(int i=0; i<repoList.length(); i++){
85    output << repoList[i].section(".",0,0,QString::SectionSkipEmpty);
86  }
87  return output;
88}
89
90QStringList PBIDBAccess::repoInfo(QString repoNum){
91  //Returns: output=[Name, Master URL] 
92  QStringList output;
93  QString ID = getIDFromNum(repoNum);
94    QStringList lines = Extras::readFile(DBPath+"repos/"+repoNum+"."+ID);
95    if(!lines.isEmpty()){
96      output <<"" << ""; //make sure there are two entries available
97      for(int j=0; j<lines.length(); j++){
98         if(lines[j].startsWith("URL: ")){ output[1] = lines[j].section("URL: ",1,50).simplified(); }
99         else if(lines[j].startsWith("Desc: ")){ output[0] = lines[j].section("Desc: ",1,50).simplified(); }
100      }
101    }
102  return output;
103}
104
105QStringList PBIDBAccess::repoMirrors(QString repoNum){
106  QStringList output;
107  QString ID = getIDFromNum(repoNum);
108  if(!ID.isEmpty()){
109    output = Extras::readFile(DBPath+"mirrors/"+ID);     
110  }
111  return output;
112}
113
114// ========================================
115// =======  PUBLIC ACCESS FUNCTIONS =======
116// ========================================
117QStringList PBIDBAccess::installed(){
118  QStringList output;
119  bool ok = DBDir->cd(DBPath+"installed");
120  if(ok){
121    output = DBDir->entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Readable);
122  }
123  return output;
124}
125
126QStringList PBIDBAccess::installedPbiInfo(QString pbiID){
127  //Output format: output[ name, version, arch, date created, author, website, installpath, iconpath, maintainer, description, fbsdversion]
128  QStringList output;
129  QString path = DBPath+"installed/"+pbiID;
130  bool ok = DBDir->cd(path);
131  if(ok){
132    output << readOneLineFile(path+"/pbi_name");
133    output << readOneLineFile(path+"/pbi_version");
134    output << readOneLineFile(path+"/pbi_arch");
135    //Get the latest date (remove the time) for this PBI (mdate and patchmdate seem to vary in use)
136    QString mdate = readOneLineFile(path+"/pbi_mdate").section(" ",0,0).simplified();
137    QString pdate = readOneLineFile(path+"/pbi_patchmdate").section(" ",0,0).simplified();
138    if( !pdate.isEmpty() && (pdate > mdate) ){
139      output << pdate; //use the date it was patched
140    }else{
141      output <<  mdate; //use the date it was initially created
142    }
143    output << readOneLineFile(path+"/pbi_author");
144    output << readOneLineFile(path+"/pbi_web");
145    output << readOneLineFile(path+"/pbi_installedpath");
146    output << path+"/pbi_icon.png";
147    output << readOneLineFile(path+"/pbi_maintainer");
148    output << cleanupDescription( readOneLineFile(path+"/pbi_desc").split("\n") );
149    output << readOneLineFile(path+"/pbi_fbsdver");
150  }
151  return output;
152}
153
154bool PBIDBAccess::installedPbiAutoUpdate(QString pbiID){
155   bool ok = FALSE;
156   if( QFile::exists(DBPath+"installed/"+pbiID+"/autoupdate-enable") ){ ok = TRUE; }
157   //qDebug() << "AutoUpdate:" << pbiID << ok;
158   return ok;
159}
160
161bool PBIDBAccess::installedPbiNeedsRoot(QString pbiID){
162  bool ok=FALSE;
163  if( QFile::exists(DBPath+"installed/"+pbiID+"/pbi_requiresroot") ){ ok=TRUE; }
164  else{
165    //Also check who installed the PBI if not flagged directly
166    QFileInfo fInfo(DBPath+"installed/"+pbiID);
167    if( fInfo.owner() == "root" ){ ok=TRUE; }
168  }
169  //qDebug() << pbiID << "requires root:" << ok;
170  return ok;
171}
172
173bool PBIDBAccess::installedPbiHasXdgDesktop(QString installPath){
174  if(!installPath.endsWith("/")){ installPath.append("/"); }
175  bool ok = DBDir->cd(installPath+".xdg-desktop");
176  if(ok){
177    if( DBDir->entryList(QStringList()<<"*.desktop",QDir::Files).length() > 0 ){ return TRUE; }   
178  }
179  return FALSE;
180}
181
182bool PBIDBAccess::installedPbiHasXdgMenu(QString installPath){
183  if(!installPath.endsWith("/")){ installPath.append("/"); }
184  bool ok = DBDir->cd(installPath+".xdg-menu");
185  if(ok){
186    if( DBDir->entryList(QStringList()<<"*.desktop",QDir::Files).length() > 0 ){ return TRUE; }   
187  }
188  return FALSE;
189}
190
191bool PBIDBAccess::installedPbiHasXdgMime(QString installPath){
192  if(!installPath.endsWith("/")){ installPath.append("/"); }
193  bool ok = DBDir->cd(installPath+".xdg-mime");
194  if(ok){
195    if( DBDir->entryList(QStringList()<<"*.xml",QDir::Files).length() > 0 ){ return TRUE; }       
196  }
197  return FALSE;
198}
199
200QString PBIDBAccess::indexFilePath(){
201  return DBPath+"index/"+currentRepoID+"-index";
202}
203
204QString PBIDBAccess::metaFilePath(){
205  return DBPath+"index/"+currentRepoID+"-meta"; 
206}
207
208QStringList PBIDBAccess::parseIndexLine(QString line){
209  //output[name, arch, version, date, sizeK, isLatest(bool), filename]
210  //line format 12/3/2013: [name,arch,version,checksum,datetimeBuilt,mirrorPathToPBI,datetimeApproved,?,current/active,sizeInK,?]
211      // NOTE: last two entries missing quite often
212  QStringList lineInfo = line.split(":");
213  QStringList output;
214  if(lineInfo.length() < 9 ){ return output; } //skip incomplete entries
215  output << lineInfo[0]; //name
216  output << lineInfo[1]; //architecture
217  output << lineInfo[2]; //version
218  QDateTime DT;
219  DT.setTime_t(lineInfo[6].toInt());
220  output << DT.toString("yyyyMMdd"); //date added to AppCafe
221  if(lineInfo.length() >= 10){ output << lineInfo[9]; }//Size in KB
222  else{ output << ""; }
223  if(lineInfo[8].simplified() == "current"){ output << "true"; } //is most recent version
224  else{ output << "false"; }  //is an older version
225  output << lineInfo[5].section("/",-1); //filename (Example: myapp-0.1-amd64.pbi)
226  return output;
227}
228
229QStringList PBIDBAccess::parseAppMetaLine(QString line){
230  // line format 11/14/2013 (10.x PBI format):
231  // [name,category,remoteIcon,author,website,license,apptype,tags,description,requiresroot,dateadded,maintainerEmail,shortDescription]
232  QStringList list = line.split(";");
233  //Format the output list
234  QStringList output;
235  //bool DEBUG = (list[0].toLower()=="xastir");
236  if(list.length() < 13){ return output;} //invalid line
237  output << list[0]; //NAME
238  output << list[1]; //CATEGORY
239  output << list[2]; //remoteIcon
240  output << list[3]; //AUTHOR
241  output << list[4]; //WEBSITE
242  output << list[5]; //LICENSE
243  output << list[6]; //APP-TYPE
244  output << list[7]; //TAGS
245  //Cleanup the description (try to format the text properly)
246  output << cleanupDescription( list[8].split("<br>") ); //DESCRIPTION
247  if(list[9]=="YES"){ list[9]="true"; } //change to the same true/false syntax as elsewhere
248  output << list[9]; //REQUIRESROOT
249  output << list[10]; //DATE ADDED (just a number - not human-readable)
250  output << list[11]; //MAINTAINER EMAIL
251  //Cleanup the short description
252  output << cleanupDescription( list[12].split("<br>", QString::SkipEmptyParts) ); //SHORT DESCRIPTION
253  return output;
254}
255
256QStringList PBIDBAccess::parseCatMetaLine(QString line){
257  // line format 11/14/2013: [name,remoteicon,description]
258  QStringList output = line.split(";");
259  if(output.length() < 3){output.clear(); } //incomplete line
260  return output;
261}
262       
263QString PBIDBAccess::remoteToLocalIcon(QString name, QString remoteIconPath){
264  QString output = DBPath+"repo-icons/"+currentRepoID+"-"+name+"."+remoteIconPath.section(".",-1);
265  //qDebug() << "Remote to Local Icon Path conversion:" << remoteIconPath << output;
266  return output;
267}
268
269// ===== Database Modification Functions =====
270bool PBIDBAccess::addRepoFile(QString rpofilepath){
271  if(!QFile::exists(rpofilepath)){ return FALSE; }
272  //Generate the command
273  QString cmd;
274  if(cmdPrefix.isEmpty()){ return FALSE; }
275  else{ cmd = cmdPrefix; }
276  cmd.append("\"pbi_addrepo "+rpofilepath+"\"");
277  qDebug() <<"DB cmd generated:" << cmd;
278  //Now run the command
279  QStringList result = runCMD(cmd).split("\n");
280  if(!result.isEmpty()){
281    if(result[result.length()-1].startsWith("Added new repo:")){ 
282      //Make sure to prompt the PBI Daemon to refresh the meta/index files now
283      runCMD( cmdPrefix+"\"pbid --refresh\"" ); //don't care about output
284      return TRUE;
285    }
286  } 
287  qDebug() << "PBI Database Error:";
288  qDebug() << " - CMD:"<<cmd;
289  qDebug() << " - Error:"<<result;
290  return FALSE;
291}
292
293bool PBIDBAccess::removeRepo(QString repoNum){
294  //Generate the command
295  QString cmd;
296  if(cmdPrefix.isEmpty()){ return FALSE; }
297  else{ cmd = cmdPrefix; }
298  cmd.append("\"pbi_deleterepo "+repoNum+"\"");
299  qDebug() <<"DB cmd generated:" << cmd;
300  //Now run the command
301  QStringList result = runCMD(cmd).split("\n");
302  if(!result.isEmpty()){
303    //qDebug() << "Repo Removed:" << result;
304    if(result[result.length()-1].startsWith("Deleted Repository")){ return TRUE; }
305  } 
306  qDebug() << "PBI Database Error:";
307  qDebug() << " - CMD:"<<cmd;
308  qDebug() << " - Error:"<<result;
309  return FALSE; 
310}
311
312bool PBIDBAccess::moveRepoUp(QString repoNum){
313  //Generate the command
314  QString cmd;
315  if(cmdPrefix.isEmpty()){ return FALSE; }
316  else{ cmd = cmdPrefix; }
317  cmd.append("\"pbi_listrepo --up "+repoNum+"\"");
318  qDebug() <<"DB cmd generated:" << cmd;
319  //Now run the command
320  QStringList result = runCMD(cmd).split("\n");
321  if(!result.isEmpty()){
322    return TRUE; //no special check for this - will need to re-load repos anyway
323  } 
324  return FALSE;
325}
326
327bool PBIDBAccess::moveRepoDown(QString repoNum){
328  //Generate the command
329  QString cmd;
330  if(cmdPrefix.isEmpty()){ return FALSE; }
331  else{ cmd = cmdPrefix; }
332  cmd.append("\"pbi_listrepo --down "+repoNum+"\"");
333  qDebug() <<"DB cmd generated:" << cmd;
334  //Now run the command
335  QStringList result = runCMD(cmd).split("\n");
336  if(!result.isEmpty()){
337    return TRUE; //no special check for this - will need to re-load repos anyway
338  } 
339  return FALSE;
340}
341
342bool PBIDBAccess::setRepoMirrors(QString repoNum, QStringList mirrors){
343  QString cmd;
344  if(cmdPrefix.isEmpty()){ return FALSE; }
345  else{ cmd = cmdPrefix; }
346  cmd.append("\"pbi_listrepo --mirror "+mirrors.join(",")+" "+repoNum+"\"");
347  qDebug() <<"DB cmd generated:" << cmd;
348  //Now run the command
349  QStringList result = runCMD(cmd).split("\n");
350  if(!result.isEmpty()){
351    if(mirrors.isEmpty()){
352      if(result[result.length()-1].startsWith("Mirror(s):")){ return TRUE; }
353    }else{
354      if(result[result.length()-1] == mirrors[mirrors.length()-1] ){ return TRUE; }
355    }
356  } 
357  qDebug() << "PBI Database Error:";
358  qDebug() << " - CMD:"<<cmd;
359  qDebug() << " - Error:"<<result;
360  return FALSE;         
361}
362
363// ========================================
364// =======  PRIVATE ACCESS FUNCTIONS ======
365// ========================================
366QString PBIDBAccess::readOneLineFile(QString path){
367  QFile file(path);
368  if(!file.exists()){ return ""; } //Return nothing for missing file
369  //Now read the file
370  QString output;
371  if(file.open(QIODevice::ReadOnly | QIODevice::Text)){
372    QTextStream in(&file);
373    while(!in.atEnd()){
374      if(!output.isEmpty()){ output.append("\n"); }
375      output.append( in.readLine() ); 
376    }
377    file.close();
378  }
379  return output;
380}
381
382QString PBIDBAccess::getIDFromNum(QString repoNum){
383  QString output;
384  for(int i=0; i<repoList.length(); i++){
385    if(repoList[i].startsWith(repoNum+".")){
386      output = repoList[i].section(".",1,1);
387      break;       
388    }
389  }     
390  return output;
391}
392
393QString PBIDBAccess::runCMD(QString cmd){
394  //Small function to run quick database modification commands
395  QString output;
396  proc->start(cmd);
397  if(proc->waitForFinished(30000)){
398    output = proc->readAll();     
399  }else{
400    proc->terminate();
401    output = "Process timed out (30 sec)";
402  }
403  if(output.endsWith("\n")){ output.chop(1); }
404  output = output.simplified();
405  return output;
406}
407
408QString PBIDBAccess::cleanupDescription(QStringList tmp){
409  for(int i=1; i<tmp.length(); i++){
410    //tmp[i-1] = tmp[i-1].simplified();
411    tmp[i] = tmp[i].simplified();
412    if( tmp[i].startsWith("WWW: ") ){
413      //Remove the website URL from the end, it is already accounted for elsewhere
414      tmp.removeAt(i);
415      i--;
416    }else if(tmp[i-1].isEmpty() || tmp[i].isEmpty() ){}
417    else if(tmp[i-1].endsWith(".") || tmp[i-1].endsWith(":") || tmp[i-1].endsWith(";") || tmp[i-1].endsWith("?") || tmp[i-1].endsWith("!") ){}
418    else if( tmp[i].startsWith("*") || tmp[i].startsWith("0") || tmp[i].startsWith("-") || tmp[i].startsWith("o ") ){}
419    else{
420      //if(DEBUG){ qDebug() << " - Bad Line Break:\n" << tmp[i-1] << "<br>" << tmp[i]; }
421      //Bad line break, combine it with the previous line
422      tmp[i-1].append(" "+tmp[i]);
423      tmp.removeAt(i);
424      i--;
425    }
426  }
427  QString desc = tmp.join("\n");
428  desc.remove("\\\\"); //Remove any double backslashes
429  return desc;
430}
Note: See TracBrowser for help on using the repository browser.