Contents of /trunk/src/gcvote.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 158 - (hide 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 harbaum 157 /*
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 harbaum 152
18 harbaum 157 #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 harbaum 158 /* 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 harbaum 157
50 harbaum 158 gcvote_t *votes = NULL, **cur = &votes;
51 harbaum 157
52 harbaum 158 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
53     if (cur_node->type == XML_ELEMENT_NODE) {
54 harbaum 157
55 harbaum 158 if(strcasecmp((char*)cur_node->name, "vote") == 0) {
56     *cur = g_new0(gcvote_t, 1);
57     (*cur)->quality = GCVOTE_NONE;
58 harbaum 157
59 harbaum 158 /* unhandled attributes: */
60     /* userName, voteAvg, voteUser, waypoint, vote1-5 */
61 harbaum 157
62 harbaum 158 /* 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 harbaum 157
69 harbaum 158 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 harbaum 157
75 harbaum 158 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 harbaum 157
81 harbaum 158 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 harbaum 157 }
87 harbaum 158 }
88     return votes;
89 harbaum 157 }
90    
91     /* parse root element */
92 harbaum 158 static gcvote_t *parse_root(xmlDocPtr doc, xmlNode *a_node) {
93 harbaum 157 xmlNode *cur_node = NULL;
94 harbaum 158 gcvote_t *votes = NULL;
95 harbaum 157
96     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
97     if (cur_node->type == XML_ELEMENT_NODE) {
98    
99 harbaum 158 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 harbaum 157 } else
105     printf("found unhandled %s\n", cur_node->name);
106     }
107     }
108 harbaum 158 return votes;
109 harbaum 157 }
110    
111 harbaum 158 static gcvote_t *parse_doc(xmlDocPtr doc) {
112 harbaum 157 /* Get the root element node */
113     xmlNode *root_element = xmlDocGetRootElement(doc);
114 harbaum 158 gcvote_t *votes = parse_root(doc, root_element);
115 harbaum 157
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 harbaum 158
125     return votes;
126 harbaum 157 }
127    
128 harbaum 158 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 harbaum 157
133 harbaum 158 printf("gcvote: using proxy %s:%d\n", proxy->host, proxy->port);
134 harbaum 157
135 harbaum 158 curl_easy_setopt(curl, CURLOPT_PROXY, proxy->host);
136     curl_easy_setopt(curl, CURLOPT_PROXYPORT, proxy->port);
137 harbaum 157
138 harbaum 158 if(proxy->use_authentication) {
139     printf("gcvote: use auth for user %s\n", proxy->authentication_user);
140 harbaum 157
141 harbaum 158 char *cred = g_strdup_printf("%s:%s",
142     proxy->authentication_user,
143     proxy->authentication_password);
144 harbaum 157
145 harbaum 158 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, cred);
146     g_free(cred);
147     }
148     }
149     }
150 harbaum 157
151 harbaum 158 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 harbaum 157 curl_formadd(&formpost, &lastptr,
250 harbaum 158 CURLFORM_COPYNAME, "version",
251     CURLFORM_COPYCONTENTS, APP VERSION,
252     CURLFORM_END);
253 harbaum 157
254     curl_formadd(&formpost, &lastptr,
255 harbaum 158 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 harbaum 157
265 harbaum 158 /* 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 harbaum 157 /* play nice and report some user agent */
274     curl_easy_setopt(curl, CURLOPT_USERAGENT, APP " " VERSION);
275 harbaum 158
276 harbaum 157 /* download to memory */
277 harbaum 158 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request->mem);
278 harbaum 157 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mem_write);
279    
280     /* what URL that receives this POST */
281 harbaum 158 curl_easy_setopt(curl, CURLOPT_URL,
282     "http://dosensuche.de/GCVote/getVotes.php");
283 harbaum 157 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
284    
285 harbaum 158 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 harbaum 157 /* always cleanup */
292     curl_easy_cleanup(curl);
293    
294 harbaum 158 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &request->response);
295    
296 harbaum 157 /* then cleanup the formpost chain */
297     curl_formfree(formpost);
298 harbaum 158
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 harbaum 157
310 harbaum 158 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 harbaum 157
329 harbaum 158 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 harbaum 157 }
337    
338 harbaum 158 return request;
339     }
340 harbaum 157