source: src-qt4/pc-softwaremanager/pbiDBAccess.cpp @ 2916111d

enter/10releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1releng/10.1.1releng/10.1.2
Last change on this file since 2916111d was 2916111d, checked in by Ken Moore <ken@…>, 17 months ago

Update the AppCafe? Installed tab to take advantage of the new info available for 10.x PBI's.
Changes Include:
1) Move currently installed app info to a seperate page, that can be opened by either double-clicking on an installed application or clicking the "details" button at the bottom of the main page.
2) Update the "actions" button to be actively enabled/disabled depending on whether any items are currently checked.
3) Add ability to start composing an email (in the DE's default email client) to the port maintainer for an installed PBI. This email template also includes all the important info regarding the PBI in question (build date, architecture, FreeBSD version, version number).
4) Fix a bug with setting/unsetting the "auto-update" status for a PBI.
5) Clean up application descriptions a bit better now (affects both installed and browser tabs).

  • Property mode set to 100644
File size: 15.2 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
191QString PBIDBAccess::indexFilePath(){
192  return DBPath+"index/"+currentRepoID+"-index";
193}
194
195QString PBIDBAccess::metaFilePath(){
196  return DBPath+"index/"+currentRepoID+"-meta"; 
197}
198
199QStringList PBIDBAccess::parseIndexLine(QString line){
200  //output[name, arch, version, date, sizeK, isLatest(bool), filename]
201  //line format 12/3/2013: [name,arch,version,checksum,datetimeBuilt,mirrorPathToPBI,datetimeApproved,?,current/active,sizeInK,?]
202      // NOTE: last two entries missing quite often
203  QStringList lineInfo = line.split(":");
204  QStringList output;
205  if(lineInfo.length() < 9 ){ return output; } //skip incomplete entries
206  output << lineInfo[0]; //name
207  output << lineInfo[1]; //architecture
208  output << lineInfo[2]; //version
209  QDateTime DT;
210  DT.setTime_t(lineInfo[6].toInt());
211  output << DT.toString("yyyyMMdd"); //date added to AppCafe
212  if(lineInfo.length() >= 10){ output << lineInfo[9]; }//Size in KB
213  else{ output << ""; }
214  if(lineInfo[8].simplified() == "current"){ output << "true"; } //is most recent version
215  else{ output << "false"; }  //is an older version
216  output << lineInfo[5].section("/",-1); //filename (Example: myapp-0.1-amd64.pbi)
217  return output;
218}
219
220QStringList PBIDBAccess::parseAppMetaLine(QString line){
221  // line format 11/14/2013 (10.x PBI format):
222  // [name,category,remoteIcon,author,website,license,apptype,tags,description,requiresroot,dateadded,maintainerEmail,shortDescription]
223  QStringList list = line.split(";");
224  //Format the output list
225  QStringList output;
226  //bool DEBUG = (list[0].toLower()=="xastir");
227  if(list.length() < 13){ return output;} //invalid line
228  output << list[0]; //NAME
229  output << list[1]; //CATEGORY
230  output << list[2]; //remoteIcon
231  output << list[3]; //AUTHOR
232  output << list[4]; //WEBSITE
233  output << list[5]; //LICENSE
234  output << list[6]; //APP-TYPE
235  output << list[7]; //TAGS
236  //Cleanup the description (try to format the text properly)
237  output << cleanupDescription( list[8].split("<br>") ); //DESCRIPTION
238  if(list[9]=="YES"){ list[9]="true"; } //change to the same true/false syntax as elsewhere
239  output << list[9]; //REQUIRESROOT
240  output << list[10]; //DATE ADDED (just a number - not human-readable)
241  output << list[11]; //MAINTAINER EMAIL
242  //Cleanup the short description (remove any line breaks)
243  QStringList tmp = list[12].split("<br>", QString::SkipEmptyParts);
244  list[12] = tmp.join(" ").simplified();
245  output << list[12]; //SHORT DESCRIPTION
246  return output;
247}
248
249QStringList PBIDBAccess::parseCatMetaLine(QString line){
250  // line format 11/14/2013: [name,remoteicon,description]
251  QStringList output = line.split(";");
252  if(output.length() < 3){output.clear(); } //incomplete line
253  return output;
254}
255       
256QString PBIDBAccess::remoteToLocalIcon(QString name, QString remoteIconPath){
257  QString output = DBPath+"repo-icons/"+currentRepoID+"-"+name+"."+remoteIconPath.section(".",-1);
258  //qDebug() << "Remote to Local Icon Path conversion:" << remoteIconPath << output;
259  return output;
260}
261
262// ===== Database Modification Functions =====
263bool PBIDBAccess::addRepoFile(QString rpofilepath){
264  if(!QFile::exists(rpofilepath)){ return FALSE; }
265  //Generate the command
266  QString cmd;
267  if(cmdPrefix.isEmpty()){ return FALSE; }
268  else{ cmd = cmdPrefix; }
269  cmd.append("\"pbi_addrepo "+rpofilepath+"\"");
270  qDebug() <<"DB cmd generated:" << cmd;
271  //Now run the command
272  QStringList result = runCMD(cmd).split("\n");
273  if(!result.isEmpty()){
274    if(result[result.length()-1].startsWith("Added new repo:")){ 
275      //Make sure to prompt the PBI Daemon to refresh the meta/index files now
276      runCMD( cmdPrefix+"\"pbid --refresh\"" ); //don't care about output
277      return TRUE;
278    }
279  } 
280  qDebug() << "PBI Database Error:";
281  qDebug() << " - CMD:"<<cmd;
282  qDebug() << " - Error:"<<result;
283  return FALSE;
284}
285
286bool PBIDBAccess::removeRepo(QString repoNum){
287  //Generate the command
288  QString cmd;
289  if(cmdPrefix.isEmpty()){ return FALSE; }
290  else{ cmd = cmdPrefix; }
291  cmd.append("\"pbi_deleterepo "+repoNum+"\"");
292  qDebug() <<"DB cmd generated:" << cmd;
293  //Now run the command
294  QStringList result = runCMD(cmd).split("\n");
295  if(!result.isEmpty()){
296    //qDebug() << "Repo Removed:" << result;
297    if(result[result.length()-1].startsWith("Deleted Repository")){ return TRUE; }
298  } 
299  qDebug() << "PBI Database Error:";
300  qDebug() << " - CMD:"<<cmd;
301  qDebug() << " - Error:"<<result;
302  return FALSE; 
303}
304
305bool PBIDBAccess::moveRepoUp(QString repoNum){
306  //Generate the command
307  QString cmd;
308  if(cmdPrefix.isEmpty()){ return FALSE; }
309  else{ cmd = cmdPrefix; }
310  cmd.append("\"pbi_listrepo --up "+repoNum+"\"");
311  qDebug() <<"DB cmd generated:" << cmd;
312  //Now run the command
313  QStringList result = runCMD(cmd).split("\n");
314  if(!result.isEmpty()){
315    return TRUE; //no special check for this - will need to re-load repos anyway
316  } 
317  return FALSE;
318}
319
320bool PBIDBAccess::moveRepoDown(QString repoNum){
321  //Generate the command
322  QString cmd;
323  if(cmdPrefix.isEmpty()){ return FALSE; }
324  else{ cmd = cmdPrefix; }
325  cmd.append("\"pbi_listrepo --down "+repoNum+"\"");
326  qDebug() <<"DB cmd generated:" << cmd;
327  //Now run the command
328  QStringList result = runCMD(cmd).split("\n");
329  if(!result.isEmpty()){
330    return TRUE; //no special check for this - will need to re-load repos anyway
331  } 
332  return FALSE;
333}
334
335bool PBIDBAccess::setRepoMirrors(QString repoNum, QStringList mirrors){
336  QString cmd;
337  if(cmdPrefix.isEmpty()){ return FALSE; }
338  else{ cmd = cmdPrefix; }
339  cmd.append("\"pbi_listrepo --mirror "+mirrors.join(",")+" "+repoNum+"\"");
340  qDebug() <<"DB cmd generated:" << cmd;
341  //Now run the command
342  QStringList result = runCMD(cmd).split("\n");
343  if(!result.isEmpty()){
344    if(mirrors.isEmpty()){
345      if(result[result.length()-1].startsWith("Mirror(s):")){ return TRUE; }
346    }else{
347      if(result[result.length()-1] == mirrors[mirrors.length()-1] ){ return TRUE; }
348    }
349  } 
350  qDebug() << "PBI Database Error:";
351  qDebug() << " - CMD:"<<cmd;
352  qDebug() << " - Error:"<<result;
353  return FALSE;         
354}
355
356// ========================================
357// =======  PRIVATE ACCESS FUNCTIONS ======
358// ========================================
359QString PBIDBAccess::readOneLineFile(QString path){
360  QFile file(path);
361  if(!file.exists()){ return ""; } //Return nothing for missing file
362  //Now read the file
363  QString output;
364  if(file.open(QIODevice::ReadOnly | QIODevice::Text)){
365    QTextStream in(&file);
366    while(!in.atEnd()){
367      if(!output.isEmpty()){ output.append("\n"); }
368      output.append( in.readLine() ); 
369    }
370    file.close();
371  }
372  return output;
373}
374
375QString PBIDBAccess::getIDFromNum(QString repoNum){
376  QString output;
377  for(int i=0; i<repoList.length(); i++){
378    if(repoList[i].startsWith(repoNum+".")){
379      output = repoList[i].section(".",1,1);
380      break;       
381    }
382  }     
383  return output;
384}
385
386QString PBIDBAccess::runCMD(QString cmd){
387  //Small function to run quick database modification commands
388  QString output;
389  proc->start(cmd);
390  if(proc->waitForFinished(30000)){
391    output = proc->readAll();     
392  }else{
393    proc->terminate();
394    output = "Process timed out (30 sec)";
395  }
396  if(output.endsWith("\n")){ output.chop(1); }
397  output = output.simplified();
398  return output;
399}
400
401QString PBIDBAccess::cleanupDescription(QStringList tmp){
402  for(int i=1; i<tmp.length(); i++){
403    //tmp[i-1] = tmp[i-1].simplified();
404    tmp[i] = tmp[i].simplified();
405    if( tmp[i].startsWith("WWW: ") ){
406      //Remove the website URL from the end, it is already accounted for elsewhere
407      tmp.removeAt(i);
408      i--;
409    }else if(tmp[i-1].isEmpty() || tmp[i].isEmpty() ){}
410    else if(tmp[i-1].endsWith(".") || tmp[i-1].endsWith(":") || tmp[i-1].endsWith(";") || tmp[i-1].endsWith("?") || tmp[i-1].endsWith("!") ){}
411    else if( tmp[i].startsWith("*") || tmp[i].startsWith("0") || tmp[i].startsWith("-") || tmp[i].startsWith("o ") ){}
412    else{
413      //if(DEBUG){ qDebug() << " - Bad Line Break:\n" << tmp[i-1] << "<br>" << tmp[i]; }
414      //Bad line break, combine it with the previous line
415      tmp[i-1].append(" "+tmp[i]);
416      tmp.removeAt(i);
417      i--;
418    }
419  }
420  return tmp.join("\n");
421}
Note: See TracBrowser for help on using the repository browser.