1 /* This file is part of pr-downloader (GPL v2 or later), see the LICENSE file */
2 
3 #include "RapidDownloader.h"
4 #include "FileSystem/FileSystem.h"
5 #include "Util.h"
6 #include "Logger.h"
7 #include "Repo.h"
8 #include "Sdp.h"
9 
10 #include <stdio.h>
11 #include <string>
12 #include <string.h>
13 #include <list>
14 #include <zlib.h>
15 
16 #ifndef WIN32
17 #include <regex.h>
18 #endif
19 
20 
CRapidDownloader()21 CRapidDownloader::CRapidDownloader():
22 	url(REPO_MASTER),
23 	reposLoaded(false)
24 {
25 }
26 
~CRapidDownloader()27 CRapidDownloader::~CRapidDownloader()
28 {
29 	sdps.clear();
30 }
31 
32 
addRemoteDsp(CSdp & sdp)33 void CRapidDownloader::addRemoteDsp(CSdp& sdp)
34 {
35 	sdps.push_back(sdp);
36 }
37 
38 
list_compare(CSdp & first,CSdp & second)39 bool CRapidDownloader::list_compare(CSdp& first ,CSdp& second)
40 {
41 	std::string name1;
42 	std::string name2;
43 	name1.clear();
44 	name2.clear();
45 	name1=(first.getShortName());
46 	name2=(second.getShortName());
47 	unsigned int len;
48 	len=std::min(name1.size(), name2.size());
49 	for (unsigned int i=0; i<len; i++) {
50 		if (tolower(name1[i])<tolower(name2[i])) {
51 			return true;
52 		}
53 	}
54 	return false;
55 }
56 
reloadRepos()57 bool CRapidDownloader::reloadRepos()
58 {
59 	if (reposLoaded)
60 		return true;
61 	updateRepos();
62 	reposLoaded=true;
63 	return true;
64 }
65 
66 
download_name(IDownload * download,int reccounter,std::string name)67 bool CRapidDownloader::download_name(IDownload* download, int reccounter,std::string name)
68 {
69 	LOG_DEBUG("%s %s",name.c_str(),download->name.c_str());
70 	std::list<CSdp>::iterator it;
71 	if (reccounter>10)
72 		return false;
73 	LOG_DEBUG("Using rapid");
74 	for (it=sdps.begin(); it!=sdps.end(); ++it) {
75 		if (match_download_name((*it).getName(),name.length() == 0 ? download->name : name )) {
76 
77 			LOG_DOWNLOAD((it)->getName().c_str() );
78 			if (!(*it).download(download))
79 				return false;
80 			if ((*it).getDepends().length()>0) {
81 				if (!download_name(download,reccounter+1,(*it).getDepends()))
82 					return false;
83 			}
84 			return true;
85 		}
86 	}
87 	return false;
88 }
89 
90 
91 
search(std::list<IDownload * > & result,const std::string & name,IDownload::category cat)92 bool CRapidDownloader::search(std::list<IDownload*>& result, const std::string& name, IDownload::category cat)
93 {
94 	LOG_DEBUG("%s",name.c_str());
95 	reloadRepos();
96 	sdps.sort(list_compare);
97 	std::list<CSdp>::iterator it;
98 	for (it=sdps.begin(); it!=sdps.end(); ++it) {
99 		if (match_download_name((*it).getShortName(),name)
100 		    || (match_download_name((*it).getName(),name))) {
101 			IDownload* dl=new IDownload((*it).getName().c_str(), name, cat, IDownload::TYP_RAPID);
102 			dl->addMirror((*it).getShortName().c_str());
103 			result.push_back(dl);
104 		}
105 	}
106 	return true;
107 }
108 
download(IDownload * download,int)109 bool CRapidDownloader::download(IDownload* download, int /*max_parallel*/)
110 {
111 	LOG_DEBUG("%s",download->name.c_str());
112 	if (download->dltype != IDownload::TYP_RAPID) { //skip non-rapid downloads
113 		LOG_DEBUG("skipping non rapid-dl");
114 		return true;
115 	}
116 	reloadRepos();
117 	return download_name(download,0);
118 }
119 
match_download_name(const std::string & str1,const std::string & str2)120 bool CRapidDownloader::match_download_name(const std::string &str1,const std::string& str2)
121 {
122 	if (str2=="") return true;
123 	if (str1==str2) return true;
124 	if (str2=="*")	 return true;
125 	//FIXME: add regex support for win32
126 	/*
127 	#ifndef WIN32
128 		regex_t regex;
129 		if (regcomp(&regex, str2.c_str(), 0)==0) {
130 			int res=regexec(&regex, str1.c_str(),0, NULL, 0 );
131 			regfree(&regex);
132 			if (res==0) {
133 				return true;
134 			}
135 		}
136 	#endif
137 	*/
138 	return false;
139 }
140 
setOption(const std::string & key,const std::string & value)141 bool CRapidDownloader::setOption(const std::string& key, const std::string& value)
142 {
143 	if (key == "masterurl") {
144 		url=value;
145 		reposLoaded = false;
146 		return true;
147 	}
148 	if (key == "forceupdate") {
149 		reposLoaded = false;
150 		return true;
151 	}
152 	return IDownloader::setOption(key, value);
153 }
154 
155 
download(const std::string & name)156 void CRapidDownloader::download(const std::string& name)
157 {
158 	std::string tmp;
159 	if (!urlToPath(name,tmp)){
160 		LOG_ERROR("Invalid path: %s", tmp.c_str());
161 		return;
162 	}
163 	path = fileSystem->getSpringDir() + PATH_DELIMITER +"rapid" +PATH_DELIMITER+ tmp;
164 	fileSystem->createSubdirs(path);
165 	LOG_DEBUG("%s",name.c_str());
166 	//first try already downloaded file, as repo master file rarely changes
167 	if ((fileSystem->fileExists(path)) && (fileSystem->isOlder(path,REPO_MASTER_RECHECK_TIME)) && parse())
168 		return;
169 	IDownload dl(path);
170 	dl.addMirror(name);
171 	httpDownload->download(&dl);
172 	parse();
173 }
174 
parse()175 bool CRapidDownloader::parse()
176 {
177 	FILE* f = fileSystem->propen(path, "rb");
178 	gzFile fp=gzdopen(fileno(f), "rb");
179 	if (fp==Z_NULL) {
180 		LOG_ERROR("Could not open %s", path.c_str());
181 		return false;
182 	}
183 	char buf[IO_BUF_SIZE];
184 	repos.clear();
185 	int i=0;
186 	while (gzgets(fp, buf, sizeof(buf))!=Z_NULL) {
187 		std::string tmp=buf;
188 		std::string url;
189 		getStrByIdx(tmp,url, ',',1);
190 		i++;
191 		if (url.size()>0) { //create new repo from url
192 			CRepo repotmp=CRepo(url, this);
193 			repos.push_back(repotmp);
194 		} else {
195 			LOG_ERROR("Parse Error %s, Line %d: %s",path.c_str(),i,buf);
196 			return false;
197 		}
198 	}
199 	gzclose(fp);
200 	fclose(f);
201 	LOG_INFO("Found %d repos in %s",repos.size(),path.c_str());
202 	return true;
203 }
204 
updateRepos()205 void CRapidDownloader::updateRepos()
206 {
207 	LOG_DEBUG("%s","Updating repos...");
208 	download(url);
209 	std::list<CRepo>::iterator it;
210 	std::list<IDownload*> dls;
211 	for (it = repos.begin(); it != repos.end(); ++it) {
212 		IDownload* dl = new IDownload();
213 		if ((*it).getDownload(*dl)) {
214 			dls.push_back(dl);
215 		} else {
216 			delete dl;
217 		}
218 	}
219 	httpDownload->download(dls);
220 	for (it = repos.begin(); it != repos.end(); ++it) {
221 		(*it).parse();
222 	}
223 	IDownloader::freeResult(dls);
224 }
225 
226