Contents of /trunk/src/gcvote.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 194 - (show annotations)
Wed Nov 18 11:40:31 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 11399 byte(s)
Some geotoad work
1 /*
2 * This file is part of GPXView.
3 *
4 * GPXView is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * GPXView is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <curl/curl.h>
19 #include <curl/types.h>
20 #include <curl/easy.h>
21
22 #include <libxml/parser.h>
23 #include <libxml/tree.h>
24
25 #include <glib/gstdio.h>
26
27 #include <fcntl.h>
28
29 #ifndef LIBXML_TREE_ENABLED
30 #error "Tree not enabled in libxml"
31 #endif
32
33 #include "gpxview.h"
34
35 #define ID_PATTERN "guid="
36
37 static size_t mem_write(void *ptr, size_t size, size_t nmemb,
38 void *stream) {
39 curl_mem_t *p = (curl_mem_t*)stream;
40
41 p->ptr = g_realloc(p->ptr, p->len + size*nmemb + 1);
42 if(p->ptr) {
43 memcpy(p->ptr+p->len, ptr, size*nmemb);
44 p->len += size*nmemb;
45 p->ptr[p->len] = 0;
46 }
47 return nmemb;
48 }
49
50 /* return list of all votes in this file */
51 static gcvote_t *votes_parse(xmlDocPtr doc, xmlNode *a_node) {
52 xmlNode *cur_node = NULL;
53
54 gcvote_t *votes = NULL, **cur = &votes;
55
56 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
57 if (cur_node->type == XML_ELEMENT_NODE) {
58
59 if(strcasecmp((char*)cur_node->name, "vote") == 0) {
60 *cur = g_new0(gcvote_t, 1);
61 (*cur)->quality = GCVOTE_NONE;
62
63 /* unhandled attributes: */
64 /* userName, voteAvg, voteUser, waypoint, vote1-5 */
65
66 /* parse votes attributes */
67 char *prop_str = (char*)xmlGetProp(cur_node, BAD_CAST "voteMedian");
68 if(prop_str) {
69 (*cur)->quality = atoi(prop_str);
70 xmlFree(prop_str);
71 }
72
73 prop_str = (char*)xmlGetProp(cur_node, BAD_CAST "voteCnt");
74 if(prop_str) {
75 (*cur)->votes = atoi(prop_str);
76 xmlFree(prop_str);
77 }
78
79 prop_str = (char*)xmlGetProp(cur_node, BAD_CAST "cacheId");
80 if(prop_str) {
81 (*cur)->id = g_strdup(prop_str);
82 xmlFree(prop_str);
83 }
84
85 cur = &(*cur)->next;
86 } else if(strcasecmp((char*)cur_node->name, "errorstring") == 0) {
87 /* ignore for now ... */
88 } else
89 printf("found unhandled votes/%s\n", cur_node->name);
90 }
91 }
92 return votes;
93 }
94
95 /* parse root element */
96 static gcvote_t *parse_root(xmlDocPtr doc, xmlNode *a_node) {
97 xmlNode *cur_node = NULL;
98 gcvote_t *votes = NULL;
99
100 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
101 if (cur_node->type == XML_ELEMENT_NODE) {
102
103 if(strcasecmp((char*)cur_node->name, "votes") == 0) {
104 if(!votes)
105 votes = votes_parse(doc, cur_node);
106 else
107 printf("gcvote: ignoring multiple votes lists\n");
108 } else
109 printf("found unhandled %s\n", cur_node->name);
110 }
111 }
112 return votes;
113 }
114
115 static gcvote_t *parse_doc(xmlDocPtr doc) {
116 /* Get the root element node */
117 xmlNode *root_element = xmlDocGetRootElement(doc);
118 gcvote_t *votes = parse_root(doc, root_element);
119
120 /*free the document */
121 xmlFreeDoc(doc);
122
123 /*
124 * Free the global variables that may
125 * have been allocated by the parser.
126 */
127 xmlCleanupParser();
128
129 return votes;
130 }
131
132 static void curl_set_proxy(CURL *curl, proxy_t *proxy) {
133 if(proxy && proxy->host) {
134 if(proxy->ignore_hosts)
135 printf("WARNING: Proxy \"ignore_hosts\" unsupported!\n");
136
137 printf("gcvote: using proxy %s:%d\n", proxy->host, proxy->port);
138
139 curl_easy_setopt(curl, CURLOPT_PROXY, proxy->host);
140 curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy->port);
141
142 if(proxy->use_authentication &&
143 proxy->authentication_user && proxy->authentication_password) {
144 printf("gcvote: use auth for user %s\n", proxy->authentication_user);
145
146 char *cred = g_strdup_printf("%s:%s",
147 proxy->authentication_user,
148 proxy->authentication_password);
149
150 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, cred);
151 g_free(cred);
152 }
153 } else
154 printf("gcvote: no proxy configured\n");
155 }
156
157 void gcvote_request_free(gcvote_request_t *request) {
158 /* decrease refcount and only free structure if no references are left */
159 request->refcount--;
160 if(request->refcount > 0) {
161 printf("gcvote: still %d references, keeping request\n",
162 request->refcount);
163 return;
164 }
165
166 printf("gcvote: no references left, freeing request\n");
167 if(request->url) g_free(request->url);
168
169 if(request->mem.ptr)
170 g_free(request->mem.ptr);
171
172 g_free(request);
173 }
174
175 /* gcvote net result handler */
176 static gboolean gcvote_result_handler(gpointer data) {
177 gcvote_request_t *request = (gcvote_request_t*)data;
178
179 printf("gcvote: result handler\n");
180
181 if(request->refcount < 2) {
182 printf("gcvote: main app isn't listening anymore\n");
183 gcvote_request_free(request);
184 return FALSE;
185 }
186
187 if(request->res) {
188 printf("gcvote: curl failed\n");
189 request->cb(NULL, request->userdata);
190 gcvote_request_free(request);
191 return FALSE;
192 }
193
194 /* parse the reply */
195 /* nothing could be parsed, just give up */
196 if(!request->mem.ptr || !request->mem.len) {
197 printf("gcvote: ignoring zero length reply\n");
198 request->cb(NULL, request->userdata);
199 gcvote_request_free(request);
200 return FALSE;
201 }
202
203 /* start XML parser */
204 LIBXML_TEST_VERSION;
205
206 /* parse the file and get the DOM */
207 xmlDoc *doc = xmlReadMemory(request->mem.ptr, request->mem.len,
208 NULL, NULL, 0);
209
210 /* nothing could be parsed, just give up */
211 if(!doc) {
212 request->cb(NULL, request->userdata);
213 gcvote_request_free(request);
214 return FALSE;
215 }
216
217 vote_t *vote = NULL;
218 gcvote_t *votes = parse_doc(doc);
219
220 /* search for requested cache in reply and free chain */
221 while(votes) {
222 gcvote_t *next = votes->next;
223
224 if(!vote && !strcmp(votes->id, request->id) && (votes->quality > 0)) {
225
226 vote = g_new0(vote_t, 1);
227 vote->quality = votes->quality;
228 vote->votes = votes->votes;
229 }
230
231 if(votes->id) g_free(votes->id);
232 g_free(votes);
233 votes = next;
234 }
235
236 if(vote) {
237 printf("gcvote: worker found vote %d/%d\n", vote->quality, vote->votes);
238 request->cb(vote, request->userdata);
239 } else
240 printf("gcvote: no vote found\n");
241
242 gcvote_request_free(request);
243 return FALSE;
244 }
245
246 static void gcvotes_free(gcvote_t *votes) {
247 while(votes) {
248 gcvote_t *next = votes->next;
249 if(votes->id) g_free(votes->id);
250 g_free(votes);
251 votes = next;
252 }
253 }
254
255 void gcvote_save(appdata_t *appdata, cache_t *cache, curl_mem_t *mem) {
256 if(!mem->len || !mem->ptr) return;
257
258 /* save data to disk */
259 char *filename = g_strdup_printf("%s%s/gcvote.xml",
260 appdata->image_path,
261 cache->id);
262 if(checkdir(filename) != 0)
263 printf("gcvote: unable to create file path\n");
264 else {
265 printf("gcvote: write %d bytes to %s\n", mem->len, filename);
266
267 int handle = g_open(filename, O_WRONLY | O_CREAT, 0644);
268 if(handle >= 0) {
269 int len = write(handle, mem->ptr, mem->len);
270 close(handle);
271
272 /* if write failed, then remove the file */
273 if(len != mem->len)
274 g_remove(filename);
275 }
276 }
277
278 free(filename);
279 }
280
281 static void *worker_thread(void *ptr) {
282 gcvote_request_t *request = (gcvote_request_t*)ptr;
283 struct curl_httppost *formpost=NULL;
284 struct curl_httppost *lastptr=NULL;
285
286 printf("gcvote: worker thread running\n");
287
288 request->mem.ptr = NULL;
289 request->mem.len = 0;
290
291 printf("gcvote: request to get votes for id %s\n", request->id);
292
293 curl_formadd(&formpost, &lastptr,
294 CURLFORM_COPYNAME, "version",
295 CURLFORM_COPYCONTENTS, APP VERSION,
296 CURLFORM_END);
297
298 curl_formadd(&formpost, &lastptr,
299 CURLFORM_COPYNAME, "cacheIds",
300 CURLFORM_COPYCONTENTS, request->id,
301 CURLFORM_END);
302
303 /* try to init curl */
304 CURL *curl = curl_easy_init();
305 if(!curl) {
306 curl_formfree(formpost);
307
308 /* callback anyway, so main loop can also clean up */
309 g_idle_add(gcvote_result_handler, request);
310
311 return NULL;
312 }
313
314 curl_set_proxy(curl, request->proxy);
315
316 /* play nice and report some user agent */
317 curl_easy_setopt(curl, CURLOPT_USERAGENT, APP " " VERSION);
318
319 /* download to memory */
320 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request->mem);
321 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mem_write);
322
323 /* what URL that receives this POST */
324 curl_easy_setopt(curl, CURLOPT_URL,
325 "http://dosensuche.de/GCVote/getVotes.php");
326 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
327
328 request->res = curl_easy_perform(curl);
329 printf("gcvote: curl perform returned with %d\n", request->res);
330
331 // printf("received %d bytes\n", mem.len);
332 // printf("data: %s\n", mem.ptr);
333
334 /* always cleanup */
335 curl_easy_cleanup(curl);
336
337 printf("gcvote: 1\n");
338
339 /* then cleanup the formpost chain */
340 curl_formfree(formpost);
341
342 printf("gcvote: 2\n");
343
344 /* cause gtk main loop to handle result only if main loop */
345 /* is still interested. Don't free request then, since the */
346 /* gcvote_result_handler will do this */
347 if(request->refcount > 1)
348 g_idle_add(gcvote_result_handler, request);
349 else
350 gcvote_request_free(request);
351
352 printf("gcvote: worker thread done\n");
353
354 return NULL;
355 }
356
357 gcvote_request_t *gcvote_request(appdata_t *appdata, gcvote_cb cb,
358 char *url, gpointer data) {
359 if(!url) return NULL;
360
361 /* ------ prepare request for worker thread --------- */
362 gcvote_request_t *request = g_new0(gcvote_request_t, 1);
363 request->url = g_strdup(url);
364 request->id = strstr(request->url, ID_PATTERN);
365 if(!request->id) {
366 printf("gcvote: unable to find id\n");
367 gcvote_request_free(request);
368 return NULL;
369 }
370
371 request->proxy = appdata->proxy;
372 request->id += strlen(ID_PATTERN);
373 request->refcount = 2; // master and worker hold a reference
374 request->cb = cb;
375 request->userdata = data;
376
377 if(!g_thread_create(&worker_thread, request, FALSE, NULL) != 0) {
378 g_warning("gcvote: failed to create the worker thread");
379
380 /* free request and return error */
381 request->refcount--; /* decrease by one for dead worker thread */
382 gcvote_request_free(request);
383 return NULL;
384 }
385
386 return request;
387 }
388
389 vote_t *gcvote_restore(appdata_t *appdata, cache_t *cache) {
390 /* load data from disk */
391 char *filename = g_strdup_printf("%s%s/gcvote.xml",
392 appdata->image_path,
393 cache->id);
394
395 printf("gcvote: trying to restore from %s\n", filename);
396
397 /* no such file? */
398 if(!g_file_test(filename, G_FILE_TEST_EXISTS)) {
399 printf("gcvote: no such file\n");
400 free(filename);
401 return NULL;
402 }
403
404 LIBXML_TEST_VERSION;
405 xmlDoc *doc = xmlReadFile(filename, NULL, 0);
406
407 if(doc == NULL) {
408 printf("gcvote: error, could not parse file %s\n", filename);
409 free(filename);
410 return NULL;
411 }
412
413 free(filename);
414
415 /* in this case this will sure only return one result */
416 gcvote_t *votes = parse_doc(doc);
417
418 if(!votes) {
419 printf("gcvote: error, no vote found\n");
420 free(filename);
421 return NULL;
422 }
423
424 vote_t *vote = g_new0(vote_t, 1);
425 vote->quality = votes->quality;
426 vote->votes = votes->votes;
427
428 printf("gcvote: found vote %d/%d\n", vote->quality, vote->votes);
429
430 gcvotes_free(votes);
431
432 return vote;
433 }