Contents of /trunk/src/gcvote.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 158 - (show annotations)
Wed Nov 4 14:54:52 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 9172 byte(s)
Async gcvote
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 #ifndef LIBXML_TREE_ENABLED
26 #error "Tree not enabled in libxml"
27 #endif
28
29 #include "gpxview.h"
30
31 #define ID_PATTERN "guid="
32
33 static size_t mem_write(void *ptr, size_t size, size_t nmemb,
34 void *stream) {
35 curl_mem_t *p = (curl_mem_t*)stream;
36
37 p->ptr = g_realloc(p->ptr, p->len + size*nmemb + 1);
38 if(p->ptr) {
39 memcpy(p->ptr+p->len, ptr, size*nmemb);
40 p->len += size*nmemb;
41 p->ptr[p->len] = 0;
42 }
43 return nmemb;
44 }
45
46 /* return list of all votes in this file */
47 static gcvote_t *votes_parse(xmlDocPtr doc, xmlNode *a_node) {
48 xmlNode *cur_node = NULL;
49
50 gcvote_t *votes = NULL, **cur = &votes;
51
52 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
53 if (cur_node->type == XML_ELEMENT_NODE) {
54
55 if(strcasecmp((char*)cur_node->name, "vote") == 0) {
56 *cur = g_new0(gcvote_t, 1);
57 (*cur)->quality = GCVOTE_NONE;
58
59 /* unhandled attributes: */
60 /* userName, voteAvg, voteUser, waypoint, vote1-5 */
61
62 /* parse votes attributes */
63 char *prop_str = (char*)xmlGetProp(cur_node, BAD_CAST "voteMedian");
64 if(prop_str) {
65 (*cur)->quality = atoi(prop_str);
66 xmlFree(prop_str);
67 }
68
69 prop_str = (char*)xmlGetProp(cur_node, BAD_CAST "voteCnt");
70 if(prop_str) {
71 (*cur)->votes = atoi(prop_str);
72 xmlFree(prop_str);
73 }
74
75 prop_str = (char*)xmlGetProp(cur_node, BAD_CAST "cacheId");
76 if(prop_str) {
77 (*cur)->id = g_strdup(prop_str);
78 xmlFree(prop_str);
79 }
80
81 cur = &(*cur)->next;
82 } else if(strcasecmp((char*)cur_node->name, "errorstring") == 0) {
83 /* ignore for now ... */
84 } else
85 printf("found unhandled votes/%s\n", cur_node->name);
86 }
87 }
88 return votes;
89 }
90
91 /* parse root element */
92 static gcvote_t *parse_root(xmlDocPtr doc, xmlNode *a_node) {
93 xmlNode *cur_node = NULL;
94 gcvote_t *votes = NULL;
95
96 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
97 if (cur_node->type == XML_ELEMENT_NODE) {
98
99 if(strcasecmp((char*)cur_node->name, "votes") == 0) {
100 if(!votes)
101 votes = votes_parse(doc, cur_node);
102 else
103 printf("gcvote: ignoring multiple votes lists\n");
104 } else
105 printf("found unhandled %s\n", cur_node->name);
106 }
107 }
108 return votes;
109 }
110
111 static gcvote_t *parse_doc(xmlDocPtr doc) {
112 /* Get the root element node */
113 xmlNode *root_element = xmlDocGetRootElement(doc);
114 gcvote_t *votes = parse_root(doc, root_element);
115
116 /*free the document */
117 xmlFreeDoc(doc);
118
119 /*
120 * Free the global variables that may
121 * have been allocated by the parser.
122 */
123 xmlCleanupParser();
124
125 return votes;
126 }
127
128 static void curl_set_proxy(CURL *curl, proxy_t *proxy) {
129 if(proxy) {
130 if(proxy->ignore_hosts)
131 printf("WARNING: Proxy \"ignore_hosts\" unsupported!\n");
132
133 printf("gcvote: using proxy %s:%d\n", proxy->host, proxy->port);
134
135 curl_easy_setopt(curl, CURLOPT_PROXY, proxy->host);
136 curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy->port);
137
138 if(proxy->use_authentication) {
139 printf("gcvote: use auth for user %s\n", proxy->authentication_user);
140
141 char *cred = g_strdup_printf("%s:%s",
142 proxy->authentication_user,
143 proxy->authentication_password);
144
145 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, cred);
146 g_free(cred);
147 }
148 }
149 }
150
151 void gcvote_request_free(gcvote_request_t *request) {
152 /* decrease refcount and only free structure if no references are left */
153 request->refcount--;
154 if(request->refcount) {
155 printf("gcvote: still %d references, keeping request\n",
156 request->refcount);
157 return;
158 }
159
160 printf("gcvote: no references left, freeing request\n");
161 if(request->url) g_free(request->url);
162
163 g_free(request);
164 }
165
166 /* gcvote net result handler */
167 static gboolean gcvote_result_handler(gpointer data) {
168 gcvote_request_t *request = (gcvote_request_t*)data;
169
170 printf("gcvote: result handler\n");
171
172 /* worker thread has already reduced its refcounter */
173 if(request->refcount < 1) {
174 printf("gcvote: main app isn't listening anymore\n");
175 g_free(request->mem.ptr);
176 return FALSE;
177 }
178
179 if(request->res) {
180 printf("gcvote: curl failed\n");
181 g_free(request->mem.ptr);
182 request->cb(NULL, request->userdata);
183 return FALSE;
184 }
185
186 /* parse the reply */
187 /* nothing could be parsed, just give up */
188 if(!request->mem.ptr || !request->mem.len) {
189 printf("gcvote: ignoring zero length reply\n");
190 g_free(request->mem.ptr);
191 request->cb(NULL, request->userdata);
192 return FALSE;
193 }
194
195 /* start XML parser */
196 LIBXML_TEST_VERSION;
197
198 /* parse the file and get the DOM */
199 xmlDoc *doc = xmlReadMemory(request->mem.ptr, request->mem.len,
200 NULL, NULL, 0);
201 g_free(request->mem.ptr);
202
203 /* nothing could be parsed, just give up */
204 if(!doc) {
205 request->cb(NULL, request->userdata);
206 return FALSE;
207 }
208
209 vote_t *vote = NULL;
210 gcvote_t *votes = parse_doc(doc);
211
212 /* search for requested cache in reply and free chain */
213 while(votes) {
214 gcvote_t *next = votes->next;
215
216 if(!vote && !strcmp(votes->id, request->id) && (votes->quality > 0)) {
217
218 vote = g_new0(vote_t, 1);
219 vote->quality = votes->quality;
220 vote->votes = votes->votes;
221 }
222
223 if(votes->id) g_free(votes->id);
224 g_free(votes);
225 votes = next;
226 }
227
228 if(vote) {
229 printf("gcvote: worker found vote %d/%d\n", vote->quality, vote->votes);
230 request->cb(vote, request->userdata);
231 } else
232 printf("gcvote: no vote found\n");
233
234 return FALSE;
235 }
236
237 static void *worker_thread(void *ptr) {
238 gcvote_request_t *request = (gcvote_request_t*)ptr;
239 struct curl_httppost *formpost=NULL;
240 struct curl_httppost *lastptr=NULL;
241
242 printf("gcvote: worker thread running\n");
243
244 request->mem.ptr = NULL;
245 request->mem.len = 0;
246
247 printf("gcvote: request to get votes for id %s\n", request->id);
248
249 curl_formadd(&formpost, &lastptr,
250 CURLFORM_COPYNAME, "version",
251 CURLFORM_COPYCONTENTS, APP VERSION,
252 CURLFORM_END);
253
254 curl_formadd(&formpost, &lastptr,
255 CURLFORM_COPYNAME, "cacheIds",
256 CURLFORM_COPYCONTENTS, request->id,
257 CURLFORM_END);
258
259 /* try to init curl */
260 CURL *curl = curl_easy_init();
261 if(!curl) {
262 curl_formfree(formpost);
263 gcvote_request_free(request);
264
265 /* callback anyway, so main loop can also clean up */
266 g_idle_add(gcvote_result_handler, request);
267
268 return NULL;
269 }
270
271 curl_set_proxy(curl, request->proxy);
272
273 /* play nice and report some user agent */
274 curl_easy_setopt(curl, CURLOPT_USERAGENT, APP " " VERSION);
275
276 /* download to memory */
277 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request->mem);
278 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mem_write);
279
280 /* what URL that receives this POST */
281 curl_easy_setopt(curl, CURLOPT_URL,
282 "http://dosensuche.de/GCVote/getVotes.php");
283 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
284
285 request->res = curl_easy_perform(curl);
286 printf("gcvote: curl perform returned with %d\n", request->res);
287
288 // printf("received %d bytes\n", mem.len);
289 // printf("data: %s\n", mem.ptr);
290
291 /* always cleanup */
292 curl_easy_cleanup(curl);
293
294 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &request->response);
295
296 /* then cleanup the formpost chain */
297 curl_formfree(formpost);
298
299 printf("gcvote: worker thread done\n");
300 gcvote_request_free(request);
301
302 /* cause gtk main loop to handle result */
303 g_idle_add(gcvote_result_handler, request);
304
305 printf("gcvote: thread terminating\n");
306
307 return NULL;
308 }
309
310 gcvote_request_t *gcvote_request(appdata_t *appdata, gcvote_cb cb,
311 char *url, gpointer data) {
312 if(!url) return NULL;
313
314 /* ------ prepare request for worker thread --------- */
315 gcvote_request_t *request = g_new0(gcvote_request_t, 1);
316 request->url = g_strdup(url);
317 request->id = strstr(request->url, ID_PATTERN);
318 if(!request->id) {
319 printf("gcvote: unable to find id\n");
320 gcvote_request_free(request);
321 return NULL;
322 }
323
324 request->id += strlen(ID_PATTERN);
325 request->refcount = 2; // master and worker hold a reference
326 request->cb = cb;
327 request->userdata = data;
328
329 if(!g_thread_create(&worker_thread, request, FALSE, NULL) != 0) {
330 g_warning("gcvote: failed to create the worker thread");
331
332 /* free request and return error */
333 request->refcount--; /* decrease by one for dead worker thread */
334 gcvote_request_free(request);
335 return NULL;
336 }
337
338 return request;
339 }
340