7661d4b6105ea7638d6dab8279b68ab712408889
[monky] / src / eve.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Copyright (c) 2008 Asbjørn Zweidorff Kjær
7  * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
8  *      (see AUTHORS)
9  * All rights reserved.
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include "config.h"
26 #include "text_object.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <utime.h>
34
35 #include <libxml/parser.h>
36 #include <libxml/tree.h>
37 #include <libxml/xmlwriter.h>
38
39 #include <curl/curl.h>
40 #include <curl/types.h>
41 #include <curl/easy.h>
42
43 #include <time.h>
44
45 #define MAXCHARS 4
46 #define EVE_UPDATE_DELAY 60
47
48 typedef struct {
49         char *charid;
50         char *skillname;
51         char *time;
52         char *lastOutput;
53
54         struct tm ends;
55         struct tm cache;
56
57         time_t delay;
58
59         int level;
60         int skill;
61 } Character;
62
63 struct xmlData {
64         char *data;
65         size_t size;
66 };
67
68 struct eve_data {
69         char apikey[64];
70         char charid[20];
71         char userid[20];
72 };
73
74 int num_chars = 0;
75 Character eveCharacters[MAXCHARS];
76
77 static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
78 {
79         size_t realsize = 0;
80         struct xmlData *data = 0;
81         data = (struct xmlData *)stream;
82         realsize = size * nmemb;
83
84         data->data = (char *)realloc(data->data, data->size + realsize + 1);
85         if (data->data) {
86                 memcpy(&(data->data[data->size]), ptr, realsize);
87                 data->size += realsize;
88                 data->data[data->size] = '\0';
89         }
90
91         return realsize;
92 }
93
94 int parseTrainingXml(char *data, Character * s)
95 {
96         char *skill, *level, *ends, *cache;
97         xmlNodePtr n;
98         xmlDocPtr doc = 0;
99         xmlNodePtr root = 0;
100         struct tm end_tm, cache_tm;
101
102         if (!data)
103                 return 1;
104
105         doc = xmlReadMemory(data, strlen(data), "", NULL, 0);
106         root = xmlDocGetRootElement(doc);
107         for (n = root->children; n; n = n->next) {
108                 if (n->type == XML_ELEMENT_NODE) {
109                         if (!strcasecmp((const char *)n->name, "error")) {
110                                 return 1;
111                         } else if (!strcasecmp((const char *)n->name, "result")) {
112                                 xmlNodePtr c;
113                                 for (c = n->children; c; c = c->next) {
114                                         if (!strcasecmp((const char *)c->name, "trainingEndTime")) {
115                                                 ends = (char *)c->children->content;
116                                         } else if (!strcasecmp((const char *)c->name, "trainingTypeID")) {
117                                                 if (c->children->content)
118                                                         skill = (char *)c->children->content;
119                                         } else if (!strcasecmp((const char *)c->name, "trainingToLevel")) {
120                                                 level = (char *)c->children->content;
121                                         }
122                                 }
123                         } else if (!strcasecmp((const char *)n->name, "cachedUntil")) {
124                                 cache = (char *)n->children->content;
125                         }
126                 }
127         }
128
129         strptime(ends, "%Y-%m-%d %H:%M:%S", &end_tm);
130         strptime(cache, "%Y-%m-%d %H:%M:%S", &cache_tm);
131         s->skill = atoi(skill);
132         s->level = atoi(level);
133         s->ends = end_tm;
134         s->cache = cache_tm;
135
136         xmlFreeDoc(doc);
137         return 0;
138 }
139
140 static char *getXmlFromAPI(const char *userid, const char *apikey, const char *charid, const char *url)
141 {
142         struct curl_httppost *post = NULL;
143         struct curl_httppost *last = NULL;
144         struct xmlData chr;
145         char *content;
146         CURL *curl_handle;
147         int rc = 0;
148
149         chr.data = NULL;
150         chr.size = 0;
151
152
153         curl_global_init(CURL_GLOBAL_ALL);
154         curl_handle = curl_easy_init();
155         curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
156         curl_easy_setopt(curl_handle, CURLOPT_URL, url);
157         curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
158         curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);
159         curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chr);
160
161         if (userid != NULL && apikey != NULL && charid != NULL) {
162                 curl_formadd(&post, &last, CURLFORM_COPYNAME, "userID", CURLFORM_COPYCONTENTS, userid, CURLFORM_END);
163                 curl_formadd(&post, &last, CURLFORM_COPYNAME, "apiKey", CURLFORM_COPYCONTENTS, apikey, CURLFORM_END);
164                 curl_formadd(&post, &last, CURLFORM_COPYNAME, "characterID", CURLFORM_COPYCONTENTS, charid, CURLFORM_END);
165
166                 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, post);
167         }
168
169         if ((rc = curl_easy_perform(curl_handle)) != CURLE_OK) {
170                 return NULL;
171         }
172
173         content = strdup(chr.data);
174         curl_easy_cleanup(curl_handle);
175
176         return content;
177 }
178
179 static void init_eve(void)
180 {
181         int i;
182
183         for (i = 0; i < MAXCHARS; i++) {
184                 eveCharacters[i].charid = NULL;
185                 eveCharacters[i].skillname = NULL;
186                 eveCharacters[i].time = NULL;
187                 eveCharacters[i].level = 0;
188                 eveCharacters[i].skill = 0;
189                 eveCharacters[i].delay = 0;
190         }
191 }
192
193 static int isCacheValid(struct tm cached)
194 {
195         struct timeval tv;
196         struct timezone tz;
197         double offset = 0;
198         time_t now = 0;
199         time_t cache = 0;
200         double diff = 0;
201
202         gettimeofday(&tv, &tz);
203         offset = (double)(tz.tz_minuteswest * 60);
204         now = time(NULL);
205         cache = mktime(&cached);
206         diff = difftime(cache, now);
207
208         if (diff < offset)
209                 return 0;
210         else
211                 return 1;
212 }
213
214 static char *formatTime(struct tm *ends)
215 {
216         struct timeval tv;
217         struct timezone tz;
218         double offset = 0;
219         time_t now = 0;
220         time_t tEnds = 0;
221         long lin = 0;
222         long lie = 0;
223         long diff = 0;
224         
225         gettimeofday(&tv, &tz);
226         offset = (double)(tz.tz_minuteswest * 60);
227         now = time(NULL);
228         tEnds = mktime(ends);
229         lin = (long)now;
230         lin += (long)offset;
231         lie = (long)tEnds;
232         diff = (lie - lin);
233
234         if (diff > 0) {
235                 int days = (int)(diff / 60 / 60 / 24);
236                 int hours = (int)((diff / 60 / 60) - (days * 24));
237                 int minutes = (int)((diff / 60) - ((hours * 60) + (days * 60 * 24)));
238                 int seconds = (int)(diff - ((minutes * 60) + (hours * 60 * 60) + (days * 60 * 60 * 24)));
239                 char *output = malloc(100 * sizeof(char));
240
241                 if (days > 0)
242                         sprintf(output, "%dd, %dh, %02dm and %02ds", days, hours, minutes, seconds);
243                 else if (hours > 0)
244                         sprintf(output, "%dh, %02dm and %02ds", hours, minutes, seconds);
245                 else
246                         sprintf(output, "%02dm and %02ds", minutes, seconds);
247
248                 return output;
249         } else {
250                 char *output = strdup("Done");
251                 return output;
252         }
253 }
254
255 static int file_exists(const char *filename)
256 {
257         struct stat fi;
258
259         if ((stat(filename, &fi)) == 0) {
260                 if (fi.st_size > 0)
261                         return 1;
262                 else
263                         return 0;
264         } else
265                 return 0;
266 }
267
268 static void writeSkilltree(char *content, const char *filename)
269 {
270         FILE *fp = fopen(filename, "w");
271         fwrite(content, sizeof(char), strlen(content), fp);
272         fclose(fp);
273 }
274
275 static char *getSkillname(const char *file, int skillid)
276 {
277         char *skilltree;
278         char *skill = NULL;
279         xmlNodePtr n;
280         xmlDocPtr doc = 0;
281         xmlNodePtr root = 0;
282
283         if (!file_exists(file)) {
284                 skilltree = getXmlFromAPI(NULL, NULL, NULL, EVEURL_SKILLTREE);
285                 writeSkilltree(skilltree, file);
286                 free(skilltree);
287         }
288
289         doc = xmlReadFile(file, NULL, 0);
290         if (!doc)
291                 return NULL;
292
293         root = xmlDocGetRootElement(doc);
294
295         for (n = root->children; n; n = n->next) {
296                 xmlNodePtr o;
297                 for (o = n->children; o; o = o->next) {
298                         xmlNodePtr p;
299                         for (p = o->children; p; p = p->next) {
300                                 xmlNodePtr q;
301                                 for (q = p->children; q; q = q->next) {
302                                         xmlNodePtr r;
303                                         for (r = q->children; r; r = r->next) {
304                                                 xmlElementPtr ele = (xmlElementPtr) r;
305                                                 xmlAttrPtr attr = (xmlAttrPtr) ele->attributes;
306                                                 char *mySkill;
307                                                 int id, assigned = 0;
308
309                                                 while (attr != NULL) {
310                                                         if (!strcasecmp((const char *)attr->name, "typeName")) {
311                                                                 mySkill = strdup((const char *)attr->children->content);
312                                                                 assigned = 1;
313                                                         } else if (!strcasecmp((const char *)attr->name, "typeID")) {
314                                                                 id = atoi((const char *)attr->children->content);
315                                                         }
316                                                         attr = attr->next;
317                                                 }
318
319                                                 if (id == skillid) {
320                                                         skill = strdup(mySkill);
321                                                         /* free(mySkill); */
322                                                         goto END;
323                                                 }
324                                                 if (assigned)
325                                                         free(mySkill);
326                                         }
327                                 }
328                         }
329                 }
330         }
331       END:
332         xmlFreeDoc(doc);
333
334         return skill;
335 }
336
337 static char *eve(char *userid, char *apikey, char *charid)
338 {
339         Character *chr = NULL;
340         const char *skillfile = "/tmp/.cesf";
341         int i = 0;
342         char *output = 0;
343         char *timel = 0;
344         char *skill = 0;
345         char *content = 0;
346         time_t now = 0;
347         char *error = 0;
348
349
350         for (i = 0; i < MAXCHARS; i++) {
351                 if (eveCharacters[i].charid != NULL) {
352                         if (strcasecmp(eveCharacters[i].charid, charid) == 0) {
353                                 chr = &eveCharacters[i];
354                                 break;
355                         }
356                 }
357         }
358
359         if (!chr) {
360                 if (num_chars == MAXCHARS - 1)
361                         return NULL;
362                 chr = &eveCharacters[num_chars];
363                 chr->charid = strdup(charid);
364                 num_chars++;
365         }
366
367         if (chr->delay > 0) {
368                 now = time(NULL);
369                 if (now < chr->delay) {
370                         output = strdup("Server error");
371                         return output;
372                 } else
373                         chr->delay = 0;
374         }
375
376         if (isCacheValid(chr->cache)) {
377                 output = (char *)malloc(200 * sizeof(char));
378                 timel = strdup(formatTime(&chr->ends));
379                 sprintf(output, EVE_OUTPUT_FORMAT, chr->skillname, chr->level, timel);
380                 free(timel);
381                 return output;
382         } else {
383                 content = getXmlFromAPI(userid, apikey, charid, EVEURL_TRAINING);
384                 if (content == NULL) {
385                         error = strdup("Server error");
386                         now = time(NULL);
387                         now += (time_t) 1800;
388                         chr->delay = now;
389                         return error;
390                 }
391
392                 if (parseTrainingXml(content, chr)) {
393                         output = (char *)malloc(200 * sizeof(char));
394                         sprintf(output, "API error");
395                         return output;
396                 }
397
398                 output = (char *)malloc(200 * sizeof(char));
399                 timel = formatTime(&chr->ends);
400                 skill = getSkillname(skillfile, chr->skill);
401
402                 chr->skillname = strdup(skill);
403
404                 sprintf(output, EVE_OUTPUT_FORMAT, chr->skillname, chr->level, timel);
405                 free(skill);
406                 return output;
407         }
408
409 }
410
411 void scan_eve(struct text_object *obj, const char *arg)
412 {
413         int argc;
414         struct eve_data *ed;
415
416         ed = malloc(sizeof(struct eve_data));
417         memset(ed, 0, sizeof(struct eve_data));
418
419         argc = sscanf(arg, "%20s %64s %20s", ed->userid, ed->apikey, ed->charid);
420
421         init_eve();
422         obj->data.opaque = ed;
423 }
424
425 void print_eve(struct text_object *obj, char *p, int p_max_size)
426 {
427         struct eve_data *ed = obj->data.opaque;
428
429         if (!ed)
430                 return;
431
432         snprintf(p, p_max_size, "%s", eve(ed->userid, ed->apikey, ed->charid));
433 }
434
435 void free_eve(struct text_object *obj)
436 {
437         if (obj->data.opaque) {
438                 free(obj->data.opaque);
439                 obj->data.opaque = NULL;
440         }
441 }