1 /* Copyright 2009-2014 e-soul.org. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and
7 * the following disclaimer.
8 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
9 * and the following disclaimer in the documentation and/or other materials provided with the
10 * distribution.
11 *
12 * THIS SOFTWARE IS PROVIDED BY E-SOUL.ORG ``AS IS'' AND ANY EXPRESS OR IMPLIED
13 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
14 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL E-SOUL.ORG
15 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
16 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
17 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
19 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
20 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21 *
22 * The views and conclusions contained in the software and documentation are those of the authors and
23 * should not be interpreted as representing official policies, either expressed or implied, of e-soul.org.
24 */
25
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <sys/wait.h>
29 #include <sys/stat.h>
30 #include <cerrno>
31 #include <sstream>
32 #include <string>
33 #include <algorithm>
34 #include <iostream>
35 #include <cstdio>
36 #include <cerrno>
37
38 #include <QStringList>
39
40 #include "PortChecker.hh"
41 #include "Preferences.hh"
42 #include "Environment.hh"
43 #include "Downloader.hh"
44 #include "Logger.hh"
45 #include "Decompressor.hh"
46
47 using namespace std;
48
PortChecker(Preferences * preferences)49 PortChecker::PortChecker(Preferences* preferences)
50 {
51 this->preferences = preferences;
52 pipeFileDescr[0] = -1;
53 pipeFileDescr[1] = -1;
54 }
55
~PortChecker()56 PortChecker::~PortChecker()
57 {
58 }
59
check()60 QStringList* PortChecker::check()
61 {
62 int status = pipe(pipeFileDescr);
63 if(status != 0)
64 {
65 perror("pipe");
66 return NULL;
67 }
68 QStringList* list = new QStringList;
69 switch(fork())
70 {
71 case -1: // fork failed
72 return list;
73 case 0: // child
74 close(pipeFileDescr[0]);
75 dup2(pipeFileDescr[1], 1);
76 executePkgVersion();
77 break; // should not happen because there's a call to exec()
78 default: // parent
79 close(pipeFileDescr[1]);
80 catchPkgVersionOutput(list);
81 close(pipeFileDescr[0]);
82 // make sure *any* child processes have terminated
83 waitpid(-1, NULL, 0);
84 waitpid(0, NULL, 0);
85 }
86 return list;
87 }
88
executePkgVersion()89 void PortChecker::executePkgVersion()
90 {
91 const char* index;
92 if(isCompressedIndex())
93 {
94 try
95 {
96 index = handleCompressedIndex();
97 }
98 catch(PortracException e)
99 {
100 cerr << e.what() << endl;
101 return;
102 }
103 }
104 else
105 {
106 index = (preferences->getPreference(INDEX_PREF).c_str());
107 }
108 execl("/usr/sbin/pkg", "pkg", "version", "-I", index, (char *) 0);
109 perror("execl");
110 }
111
catchPkgVersionOutput(QStringList * list)112 void PortChecker::catchPkgVersionOutput(QStringList* list)
113 {
114 int n, i;
115 char buff[BUFF_LEN] = {'\0'};
116 string s;
117
118 while((n = read(pipeFileDescr[0], buff, BUFF_LEN)) > 0)
119 {
120 for(i = 0; i < n; i++)
121 {
122 if(buff[i] == '\n')
123 {
124 if(s.find("<") != string::npos)
125 {
126 size_t endpos = s.find_last_not_of(" \t");
127 s.erase(endpos);
128 list->append(s.c_str());
129 }
130 s.clear();
131 }
132 else
133 {
134 s.append(1, buff[i]);
135 }
136 }
137 }
138 }
139
isCompressedIndex()140 bool PortChecker::isCompressedIndex()
141 {
142 string index = preferences->getPreference(INDEX_PREF);
143 if(index.find(".bz2", index.find_last_of("/")) != string::npos)
144 {
145 return true;
146 }
147 return false;
148 }
149
handleCompressedIndex()150 const char* PortChecker::handleCompressedIndex()
151 {
152 string file = downloadIndex();
153 Decompressor::decompress(file, file.substr(0, file.find_last_of('.')));
154 return file.substr(0, file.find_last_of('.')).c_str();
155 }
156
downloadIndex() const157 string PortChecker::downloadIndex() const
158 {
159 string dataDir = Environment::getDataDir();
160 //TODO use system calls
161 string mkdirStr("mkdir -p ");
162 mkdirStr += dataDir;
163 system(mkdirStr.c_str());
164 struct stat statStruct;
165 if(stat(dataDir.c_str(), &statStruct))
166 {
167 string logEntry = Logger::systemCallError(string("stat"), dataDir, string(strerror(errno)));
168 throw PortracException(logEntry);
169 }
170 int modeMask = S_IFDIR | S_IRWXU;
171 if((statStruct.st_mode & modeMask) == modeMask)
172 {
173 Downloader downloader;
174 string url = preferences->getPreference(INDEX_PREF);
175 size_t fileNameIndex = url.find_last_of('/');
176 size_t fileNameLength = url.length() - fileNameIndex;
177 ostringstream file(dataDir, ios_base::app);
178 file << '/' << url.substr(fileNameIndex, fileNameLength);
179 downloader.download(url, file.str());
180 return file.str();
181 }
182 string message(dataDir);
183 message += ": invalid directory!";
184 throw PortracException(message);
185 }
186
run()187 void PortChecker::run()
188 {
189 list = check();
190 }
191
getList()192 QStringList* PortChecker::getList()
193 {
194 return list;
195 }
196