Implement search tab and version picker
[oweals/minetest.git] / src / convert_json.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <vector>
21 #include <iostream>
22 #include <sstream>
23
24 #include "convert_json.h"
25 #include "mods.h"
26 #include "config.h"
27 #include "log.h"
28 #include "main.h" // for g_settings
29 #include "settings.h"
30 #include "version.h"
31
32 #if USE_CURL
33 #include <curl/curl.h>
34
35 static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
36 {
37     ((std::string*)userp)->append((char*)contents, size * nmemb);
38     return size * nmemb;
39 }
40
41 #endif
42
43 Json::Value                 fetchJsonValue(const std::string url,
44                                                                                                         struct curl_slist *chunk) {
45 #if USE_CURL
46         std::string liststring;
47         CURL *curl;
48
49         curl = curl_easy_init();
50         if (curl)
51         {
52                 CURLcode res;
53
54                 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
55                 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
56                 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
57                 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &liststring);
58                 curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, g_settings->getS32("curl_timeout"));
59                 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, g_settings->getS32("curl_timeout"));
60                 curl_easy_setopt(curl, CURLOPT_USERAGENT, (std::string("Minetest ")+minetest_version_hash).c_str());
61
62                 if (chunk != 0)
63                         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
64
65                 res = curl_easy_perform(curl);
66                 if (res != CURLE_OK)
67                         errorstream<<"Jsonreader: "<< url <<" not found (" << curl_easy_strerror(res) << ")" <<std::endl;
68                 curl_easy_cleanup(curl);
69         }
70
71         Json::Value root;
72         Json::Reader reader;
73         std::istringstream stream(liststring);
74         if (!liststring.size()) {
75                 return Json::Value();
76         }
77
78         if (!reader.parse( stream, root ) )
79         {
80                 errorstream << "URL: " << url << std::endl;
81                 errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages();
82                 errorstream << "data: \"" << liststring << "\"" << std::endl;
83                 return Json::Value();
84         }
85
86         if (root.isArray()) {
87                 return root;
88         }
89         if ((root["list"].isArray())) {
90                 return root["list"];
91         }
92         else {
93                 return root;
94         }
95 #endif
96         return Json::Value();
97 }
98
99 std::vector<ModStoreMod>    readModStoreList(Json::Value& modlist) {
100         std::vector<ModStoreMod> retval;
101
102         if (modlist.isArray()) {
103                 for (unsigned int i = 0; i < modlist.size(); i++)
104                 {
105                         ModStoreMod toadd;
106                         toadd.valid = true;
107
108                         //id
109                         if (modlist[i]["id"].asString().size()) {
110                                 std::string id_raw = modlist[i]["id"].asString();
111                                 char* endptr = 0;
112                                 int numbervalue = strtol(id_raw.c_str(),&endptr,10);
113
114                                 if ((id_raw != "") && (*endptr == 0)) {
115                                         toadd.id = numbervalue;
116                                 }
117                                 else {
118                                         errorstream << "readModStoreList: missing id" << std::endl;
119                                         toadd.valid = false;
120                                 }
121                         }
122                         else {
123                                 errorstream << "readModStoreList: missing id" << std::endl;
124                                 toadd.valid = false;
125                         }
126
127                         //title
128                         if (modlist[i]["title"].asString().size()) {
129                                 toadd.title = modlist[i]["title"].asString();
130                         }
131                         else {
132                                 errorstream << "readModStoreList: missing title" << std::endl;
133                                 toadd.valid = false;
134                         }
135
136                         //basename
137                         if (modlist[i]["basename"].asString().size()) {
138                                 toadd.basename = modlist[i]["basename"].asString();
139                         }
140                         else {
141                                 errorstream << "readModStoreList: missing basename" << std::endl;
142                                 toadd.valid = false;
143                         }
144
145                         //author
146
147                         //rating
148
149                         //version
150
151                         if (toadd.valid) {
152                                 retval.push_back(toadd);
153                         }
154                 }
155         }
156         return retval;
157 }
158
159 ModStoreModDetails          readModStoreModDetails(Json::Value& details) {
160
161         ModStoreModDetails retval;
162
163         retval.valid = true;
164
165         //version set
166         if (details["version_set"].isArray()) {
167                 for (unsigned int i = 0; i < details["version_set"].size(); i++)
168                 {
169                         ModStoreVersionEntry toadd;
170
171                         if (details["version_set"][i]["id"].asString().size()) {
172                                 std::string id_raw = details["version_set"][i]["id"].asString();
173                                 char* endptr = 0;
174                                 int numbervalue = strtol(id_raw.c_str(),&endptr,10);
175
176                                 if ((id_raw != "") && (*endptr == 0)) {
177                                         toadd.id = numbervalue;
178                                 }
179                         }
180                         else {
181                                 errorstream << "readModStoreModDetails: missing version_set id" << std::endl;
182                                 retval.valid = false;
183                         }
184
185                         //date
186                         if (details["version_set"][i]["date"].asString().size()) {
187                                 toadd.date = details["version_set"][i]["date"].asString();
188                         }
189
190                         //file
191                         if (details["version_set"][i]["file"].asString().size()) {
192                                 toadd.file = details["version_set"][i]["file"].asString();
193                         }
194                         else {
195                                 errorstream << "readModStoreModDetails: missing version_set file" << std::endl;
196                                 retval.valid = false;
197                         }
198
199                         //approved
200
201                         //mtversion
202
203                         if( retval.valid ) {
204                                 retval.versions.push_back(toadd);
205                         }
206                         else {
207                                 break;
208                         }
209                 }
210         }
211
212         if (retval.versions.size() < 1) {
213                 infostream << "readModStoreModDetails: not a single version specified!" << std::endl;
214                 retval.valid = false;
215         }
216
217         //categories
218         if (details["categories"].isObject()) {
219                 for (unsigned int i = 0; i < details["categories"].size(); i++) {
220                         ModStoreCategoryInfo toadd;
221
222                         if (details["categories"][i]["id"].asString().size()) {
223
224                                 std::string id_raw = details["categories"][i]["id"].asString();
225                                 char* endptr = 0;
226                                 int numbervalue = strtol(id_raw.c_str(),&endptr,10);
227
228                                 if ((id_raw != "") && (*endptr == 0)) {
229                                         toadd.id = numbervalue;
230                                 }
231                         }
232                         else {
233                                 errorstream << "readModStoreModDetails: missing categories id" << std::endl;
234                                 retval.valid = false;
235                         }
236                         if (details["categories"][i]["title"].asString().size()) {
237                                 toadd.name = details["categories"][i]["title"].asString();
238                         }
239                         else {
240                                 errorstream << "readModStoreModDetails: missing categories title" << std::endl;
241                                 retval.valid = false;
242                         }
243
244                         if( retval.valid ) {
245                                 retval.categories.push_back(toadd);
246                         }
247                         else {
248                                 break;
249                         }
250                 }
251         }
252
253         //author
254         if (details["author"].isObject()) {
255                 if (details["author"]["id"].asString().size()) {
256
257                         std::string id_raw = details["author"]["id"].asString();
258                         char* endptr = 0;
259                         int numbervalue = strtol(id_raw.c_str(),&endptr,10);
260
261                         if ((id_raw != "") && (*endptr == 0)) {
262                                 retval.author.id = numbervalue;
263                         }
264                         else {
265                                 errorstream << "readModStoreModDetails: missing author id (convert)" << std::endl;
266                                 retval.valid = false;
267                         }
268                 }
269                 else {
270                         errorstream << "readModStoreModDetails: missing author id" << std::endl;
271                         retval.valid = false;
272                 }
273
274                 if (details["author"]["username"].asString().size()) {
275                         retval.author.username = details["author"]["username"].asString();
276                 }
277                 else {
278                         errorstream << "readModStoreModDetails: missing author username" << std::endl;
279                         retval.valid = false;
280                 }
281         }
282         else {
283                 errorstream << "readModStoreModDetails: missing author" << std::endl;
284                 retval.valid = false;
285         }
286
287         //license
288         if (details["license"].isObject()) {
289                 if (details["license"]["id"].asString().size()) {
290
291                         std::string id_raw = details["license"]["id"].asString();
292                         char* endptr = 0;
293                         int numbervalue = strtol(id_raw.c_str(),&endptr,10);
294
295                         if ((id_raw != "") && (*endptr == 0)) {
296                                 retval.license.id = numbervalue;
297                         }
298                 }
299                 else {
300                         errorstream << "readModStoreModDetails: missing license id" << std::endl;
301                         retval.valid = false;
302                 }
303
304                 if (details["license"]["short"].asString().size()) {
305                         retval.license.shortinfo = details["license"]["short"].asString();
306                 }
307                 else {
308                         errorstream << "readModStoreModDetails: missing license short" << std::endl;
309                         retval.valid = false;
310                 }
311
312                 if (details["license"]["link"].asString().size()) {
313                         retval.license.url = details["license"]["link"].asString();
314                 }
315
316         }
317
318         //titlepic
319         if (details["titlepic"].isObject()) {
320                 if (details["titlepic"]["id"].asString().size()) {
321
322                         std::string id_raw = details["titlepic"]["id"].asString();
323                         char* endptr = 0;
324                         int numbervalue = strtol(id_raw.c_str(),&endptr,10);
325
326                         if ((id_raw != "") && (*endptr == 0)) {
327                                 retval.titlepic.id = numbervalue;
328                         }
329                 }
330
331                 if (details["titlepic"]["file"].asString().size()) {
332                         retval.titlepic.file = details["titlepic"]["file"].asString();
333                 }
334
335                 if (details["titlepic"]["desc"].asString().size()) {
336                         retval.titlepic.description = details["titlepic"]["desc"].asString();
337                 }
338
339                 if (details["titlepic"]["mod"].asString().size()) {
340
341                         std::string mod_raw = details["titlepic"]["mod"].asString();
342                         char* endptr = 0;
343                         int numbervalue = strtol(mod_raw.c_str(),&endptr,10);
344
345                         if ((mod_raw != "") && (*endptr == 0)) {
346                                 retval.titlepic.mod = numbervalue;
347                         }
348                 }
349         }
350
351         //id
352         if (details["id"].asString().size()) {
353
354                 std::string id_raw = details["id"].asString();
355                 char* endptr = 0;
356                 int numbervalue = strtol(id_raw.c_str(),&endptr,10);
357
358                 if ((id_raw != "") && (*endptr == 0)) {
359                         retval.id = numbervalue;
360                 }
361         }
362         else {
363                 errorstream << "readModStoreModDetails: missing id" << std::endl;
364                 retval.valid = false;
365         }
366
367         //title
368         if (details["title"].asString().size()) {
369                 retval.title = details["title"].asString();
370         }
371         else {
372                 errorstream << "readModStoreModDetails: missing title" << std::endl;
373                 retval.valid = false;
374         }
375
376         //basename
377         if (details["basename"].asString().size()) {
378                 retval.basename = details["basename"].asString();
379         }
380         else {
381                 errorstream << "readModStoreModDetails: missing basename" << std::endl;
382                 retval.valid = false;
383         }
384
385         //description
386         if (details["desc"].asString().size()) {
387                 retval.description = details["desc"].asString();
388         }
389
390         //repository
391         if (details["replink"].asString().size()) {
392                 retval.repository = details["replink"].asString();
393         }
394
395         //value
396         if (details["rating"].asString().size()) {
397
398                 std::string id_raw = details["rating"].asString();
399                 char* endptr = 0;
400                 float numbervalue = strtof(id_raw.c_str(),&endptr);
401
402                 if ((id_raw != "") && (*endptr == 0)) {
403                         retval.rating = numbervalue;
404                 }
405         }
406         else {
407                 retval.rating = 0.0;
408         }
409
410         //depends
411         if (details["depends"].isArray()) {
412                 //TODO
413         }
414
415         //softdepends
416         if (details["softdep"].isArray()) {
417                 //TODO
418         }
419
420         //screenshot url
421         if (details["screenshot_url"].asString().size()) {
422                 retval.screenshot_url = details["screenshot_url"].asString();
423         }
424
425         return retval;
426 }