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