Diff of /trunk/src/gcvote.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 152 by harbaum, Mon Nov 2 10:53:01 2009 UTC revision 161 by harbaum, Thu Nov 5 08:58:28 2009 UTC
# Line 1  Line 1 
1  curl -d version=2.0b -duserName=uglyDUMMYusernamesolution -d cacheIds=4e68a04e-161c-4351-9813-bec6f6a4ca8a -d password= http://dosensuche.de/GCVote/getVotes.php  /*
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) {
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          printf("gcvote:   use auth for user %s\n", proxy->authentication_user);
144    
145          char *cred = g_strdup_printf("%s:%s",
146                                       proxy->authentication_user,
147                                       proxy->authentication_password);
148    
149          curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, cred);
150          g_free(cred);
151        }
152      } else
153        printf("gcvote: no proxy configured\n");
154    }
155    
156    void gcvote_request_free(gcvote_request_t *request) {
157      /* decrease refcount and only free structure if no references are left */
158      request->refcount--;
159      if(request->refcount) {
160        printf("gcvote: still %d references, keeping request\n",
161               request->refcount);
162        return;
163      }
164    
165      printf("gcvote: no references left, freeing request\n");
166      if(request->url)  g_free(request->url);
167    
168      if(request->mem.ptr)
169        g_free(request->mem.ptr);
170    
171      g_free(request);
172    }
173    
174    /* gcvote net result handler */
175    static gboolean gcvote_result_handler(gpointer data) {
176      gcvote_request_t *request = (gcvote_request_t*)data;
177    
178      printf("gcvote: result handler\n");
179    
180      /* worker thread has already reduced its refcounter */
181      if(request->refcount < 1) {
182        printf("gcvote: main app isn't listening anymore\n");
183        return FALSE;
184      }
185    
186      if(request->res) {
187        printf("gcvote: curl failed\n");
188        request->cb(NULL, request->userdata);
189        return FALSE;
190      }
191    
192      /* parse the reply */
193      /* nothing could be parsed, just give up */
194      if(!request->mem.ptr || !request->mem.len) {
195        printf("gcvote: ignoring zero length reply\n");
196        request->cb(NULL, request->userdata);
197        return FALSE;
198      }
199    
200      /* start XML parser */
201      LIBXML_TEST_VERSION;
202    
203      /* parse the file and get the DOM */
204      xmlDoc *doc = xmlReadMemory(request->mem.ptr, request->mem.len,
205                                  NULL, NULL, 0);
206    
207      /* nothing could be parsed, just give up */
208      if(!doc) {
209        request->cb(NULL, request->userdata);
210        return FALSE;
211      }
212    
213      vote_t *vote = NULL;
214      gcvote_t *votes = parse_doc(doc);
215    
216      /* search for requested cache in reply and free chain */
217      while(votes) {
218        gcvote_t *next = votes->next;
219    
220        if(!vote && !strcmp(votes->id, request->id) && (votes->quality > 0)) {
221    
222          vote = g_new0(vote_t, 1);
223          vote->quality = votes->quality;
224          vote->votes = votes->votes;
225        }
226    
227        if(votes->id) g_free(votes->id);
228        g_free(votes);
229        votes = next;
230      }
231    
232      if(vote) {
233        printf("gcvote: worker found vote %d/%d\n", vote->quality, vote->votes);
234        request->cb(vote, request->userdata);
235      } else
236        printf("gcvote: no vote found\n");
237    
238      return FALSE;
239    }
240    
241    static void gcvotes_free(gcvote_t *votes) {
242      while(votes) {
243        gcvote_t *next = votes->next;
244        if(votes->id) g_free(votes->id);
245        g_free(votes);
246        votes = next;
247      }
248    }
249    
250    void gcvote_save(appdata_t *appdata, cache_t *cache, curl_mem_t *mem) {
251      if(!mem->len || !mem->ptr) return;
252    
253      /* save data to disk */
254      char *filename = g_strdup_printf("%s%s/gcvote.xml",
255                                       appdata->image_path,
256                                       cache->id);
257      if(checkdir(filename) != 0)
258        printf("gcvote: unable to create file path\n");
259      else {
260        printf("gcvote: write %d bytes to %s\n", mem->len, filename);
261    
262        int handle = g_open(filename, O_WRONLY | O_CREAT, 0644);
263        if(handle >= 0) {
264          int len = write(handle, mem->ptr, mem->len);
265          close(handle);
266    
267          /* if write failed, then remove the file */
268          if(len != mem->len)
269            g_remove(filename);
270        }
271      }
272    
273      free(filename);
274    }
275    
276    static void *worker_thread(void *ptr) {
277      gcvote_request_t *request = (gcvote_request_t*)ptr;
278      struct curl_httppost *formpost=NULL;
279      struct curl_httppost *lastptr=NULL;
280    
281      printf("gcvote: worker thread running\n");
282    
283      request->mem.ptr = NULL;
284      request->mem.len = 0;
285    
286      printf("gcvote: request to get votes for id %s\n", request->id);
287    
288      curl_formadd(&formpost, &lastptr,
289                   CURLFORM_COPYNAME, "version",
290                   CURLFORM_COPYCONTENTS, APP VERSION,
291                   CURLFORM_END);
292    
293      curl_formadd(&formpost, &lastptr,
294                   CURLFORM_COPYNAME, "cacheIds",
295                   CURLFORM_COPYCONTENTS, request->id,
296                   CURLFORM_END);
297    
298      /* try to init curl */
299      CURL *curl = curl_easy_init();
300      if(!curl) {
301        curl_formfree(formpost);
302        gcvote_request_free(request);
303    
304        /* callback anyway, so main loop can also clean up */
305        g_idle_add(gcvote_result_handler, request);
306    
307        return NULL;
308      }
309    
310      curl_set_proxy(curl, request->proxy);
311    
312      /* play nice and report some user agent */
313      curl_easy_setopt(curl, CURLOPT_USERAGENT, APP " " VERSION);
314    
315      /* download to memory */
316      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &request->mem);
317      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mem_write);
318    
319      /* what URL that receives this POST */
320      curl_easy_setopt(curl, CURLOPT_URL,
321                       "http://dosensuche.de/GCVote/getVotes.php");
322      curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
323    
324      request->res = curl_easy_perform(curl);
325      printf("gcvote: curl perform returned with %d\n", request->res);
326    
327      //  printf("received %d bytes\n", mem.len);
328      //  printf("data: %s\n", mem.ptr);
329    
330      /* always cleanup */
331      curl_easy_cleanup(curl);
332    
333      curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &request->response);
334    
335      /* then cleanup the formpost chain */
336      curl_formfree(formpost);
337    
338      printf("gcvote: worker thread done\n");
339      gcvote_request_free(request);
340    
341      /* cause gtk main loop to handle result */
342      g_idle_add(gcvote_result_handler, request);
343    
344      printf("gcvote: thread terminating\n");
345    
346      return NULL;
347    }
348    
349    gcvote_request_t *gcvote_request(appdata_t *appdata, gcvote_cb cb,
350                                   char *url, gpointer data) {
351      if(!url) return NULL;
352    
353      /* ------ prepare request for worker thread --------- */
354      gcvote_request_t *request = g_new0(gcvote_request_t, 1);
355      request->url = g_strdup(url);
356      request->id = strstr(request->url, ID_PATTERN);
357      if(!request->id) {
358        printf("gcvote: unable to find id\n");
359        gcvote_request_free(request);
360        return NULL;
361      }
362    
363      request->proxy = appdata->proxy;
364      request->id += strlen(ID_PATTERN);
365      request->refcount = 2;   // master and worker hold a reference
366      request->cb = cb;
367      request->userdata = data;
368    
369      if(!g_thread_create(&worker_thread, request, FALSE, NULL) != 0) {
370        g_warning("gcvote: failed to create the worker thread");
371    
372        /* free request and return error */
373        request->refcount--;    /* decrease by one for dead worker thread */
374        gcvote_request_free(request);
375        return NULL;
376      }
377    
378      return request;
379    }
380    
381    vote_t *gcvote_restore(appdata_t *appdata, cache_t *cache) {
382      /* load data from disk */
383      char *filename = g_strdup_printf("%s%s/gcvote.xml",
384                                       appdata->image_path,
385                                       cache->id);
386    
387      printf("gcvote: trying to restore from %s\n", filename);
388    
389      /* no such file? */
390      if(!g_file_test(filename, G_FILE_TEST_EXISTS)) {
391        printf("gcvote: no such file\n");
392        free(filename);
393        return NULL;
394      }
395    
396      LIBXML_TEST_VERSION;
397      xmlDoc *doc = xmlReadFile(filename, NULL, 0);
398    
399      if(doc == NULL) {
400        printf("gcvote: error, could not parse file %s\n", filename);
401        free(filename);
402        return NULL;
403      }
404    
405      free(filename);
406    
407      /* in this case this will sure only return one result */
408      gcvote_t *votes = parse_doc(doc);
409    
410      if(!votes) {
411        printf("gcvote: error, no vote found\n");
412        free(filename);
413        return NULL;
414      }
415    
416      vote_t *vote = g_new0(vote_t, 1);
417      vote->quality = votes->quality;
418      vote->votes = votes->votes;
419    
420      printf("gcvote: found vote %d/%d\n", vote->quality, vote->votes);
421    
422      gcvotes_free(votes);
423    
424      return vote;
425    }

Legend:
Removed from v.152  
changed lines
  Added in v.161