spend some time with eve.{c,h}
[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-2009 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 "eve.h"
26 #include "config.h"
27 #include "text_object.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <utime.h>
35
36 #include <libxml/parser.h>
37 #include <libxml/tree.h>
38 #include <libxml/xmlwriter.h>
39
40 #include <curl/curl.h>
41 #include <curl/types.h>
42 #include <curl/easy.h>
43
44 #include <time.h>
45
46 #define MAXCHARS 4
47 #define EVE_UPDATE_DELAY 60
48
49 typedef struct {
50         char *charid;
51         char *skillname;
52         char *time;
53         char *lastOutput;
54
55         struct tm ends;
56         struct tm cache;
57
58         time_t delay;
59
60         int level;
61         int skill;
62 } Character;
63
64 struct xmlData {
65         char *data;
66         size_t size;
67 };
68
69 int num_chars = 0;
70 Character eveCharacters[MAXCHARS];
71
72 static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)
73 {
74         size_t realsize = 0;
75         struct xmlData *data = 0;
76         data = (struct xmlData *)stream;
77         realsize = size * nmemb;
78
79         data->data = (char *)realloc(data->data, data->size + realsize + 1);
80         if (data->data) {
81                 memcpy(&(data->data[data->size]), ptr, realsize);
82                 data->size += realsize;
83                 data->data[data->size] = '\0';
84         }
85
86         return realsize;
87 }
88
89 int parseTrainingXml(char *data, Character * s)
90 {
91         char *skill, *level, *ends, *cache;
92         xmlNodePtr n;
93         xmlDocPtr doc = 0;
94         xmlNodePtr root = 0;
95         struct tm end_tm, cache_tm;
96
97         if (!data)
98                 return 1;
99
100         doc = xmlReadMemory(data, strlen(data), "", NULL, 0);
101         root = xmlDocGetRootElement(doc);
102         for (n = root->children; n; n = n->next) {
103                 if (n->type == XML_ELEMENT_NODE) {
104                         if (!strcasecmp((const char *)n->name, "error")) {
105                                 return 1;
106                         } else if (!strcasecmp((const char *)n->name, "result")) {
107                                 xmlNodePtr c;
108                                 for (c = n->children; c; c = c->next) {
109                                         if (!strcasecmp((const char *)c->name, "trainingEndTime")) {
110                                                 ends = (char *)c->children->content;
111                                         } else if (!strcasecmp((const char *)c->name, "trainingTypeID")) {
112                                                 if (c->children->content)
113                                                         skill = (char *)c->children->content;
114                                         } else if (!strcasecmp((const char *)c->name, "trainingToLevel")) {
115                                                 level = (char *)c->children->content;
116                                         }
117                                 }
118                         } else if (!strcasecmp((const char *)n->name, "cachedUntil")) {
119                                 cache = (char *)n->children->content;
120                         }
121                 }
122         }
123
124         strptime(ends, "%Y-%m-%d %H:%M:%S", &end_tm);
125         strptime(cache, "%Y-%m-%d %H:%M:%S", &cache_tm);
126         s->skill = atoi(skill);
127         s->level = atoi(level);
128         s->ends = end_tm;
129         s->cache = cache_tm;
130
131         xmlFreeDoc(doc);
132         return 0;
133 }
134
135 static char *getXmlFromAPI(const char *userid, const char *apikey, const char *charid, const char *url)
136 {
137         struct curl_httppost *post = NULL;
138         struct curl_httppost *last = NULL;
139         struct xmlData chr;
140         char *content;
141         CURL *curl_handle;
142         int rc = 0;
143
144         chr.data = NULL;
145         chr.size = 0;
146
147
148         curl_global_init(CURL_GLOBAL_ALL);
149         curl_handle = curl_easy_init();
150         curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
151         curl_easy_setopt(curl_handle, CURLOPT_URL, url);
152         curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
153         curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);
154         curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chr);
155
156         if (userid != NULL && apikey != NULL && charid != NULL) {
157                 curl_formadd(&post, &last, CURLFORM_COPYNAME, "userID", CURLFORM_COPYCONTENTS, userid, CURLFORM_END);
158                 curl_formadd(&post, &last, CURLFORM_COPYNAME, "apiKey", CURLFORM_COPYCONTENTS, apikey, CURLFORM_END);
159                 curl_formadd(&post, &last, CURLFORM_COPYNAME, "characterID", CURLFORM_COPYCONTENTS, charid, CURLFORM_END);
160
161                 curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, post);
162         }
163
164         if ((rc = curl_easy_perform(curl_handle)) != CURLE_OK) {
165                 return NULL;
166         }
167
168         content = strdup(chr.data);
169         curl_easy_cleanup(curl_handle);
170
171         return content;
172 }
173
174 static void init_eve(void)
175 {
176         int i;
177
178         for (i = 0; i < MAXCHARS; i++) {
179                 eveCharacters[i].charid = NULL;
180                 eveCharacters[i].skillname = NULL;
181                 eveCharacters[i].time = NULL;
182                 eveCharacters[i].level = 0;
183                 eveCharacters[i].skill = 0;
184                 eveCharacters[i].delay = 0;
185         }
186 }
187
188 static int isCacheValid(struct tm cached)
189 {
190         struct timeval tv;
191         struct timezone tz;
192         double offset = 0;
193         time_t now = 0;
194         time_t cache = 0;
195         double diff = 0;
196
197         gettimeofday(&tv, &tz);
198         offset = (double)(tz.tz_minuteswest * 60);
199         now = time(NULL);
200         cache = mktime(&cached);
201         diff = difftime(cache, now);
202
203         if (diff < offset)
204                 return 0;
205         else
206                 return 1;
207 }
208
209 static char *formatTime(struct tm *ends)
210 {
211         struct timeval tv;
212         struct timezone tz;
213         double offset = 0;
214         time_t now = 0;
215         time_t tEnds = 0;
216         long lin = 0;
217         long lie = 0;
218         long diff = 0;
219         
220         gettimeofday(&tv, &tz);
221         offset = (double)(tz.tz_minuteswest * 60);
222         now = time(NULL);
223         tEnds = mktime(ends);
224         lin = (long)now;
225         lin += (long)offset;
226         lie = (long)tEnds;
227         diff = (lie - lin);
228
229         if (diff > 0) {
230                 int days = (int)(diff / 60 / 60 / 24);
231                 int hours = (int)((diff / 60 / 60) - (days * 24));
232                 int minutes = (int)((diff / 60) - ((hours * 60) + (days * 60 * 24)));
233                 int seconds = (int)(diff - ((minutes * 60) + (hours * 60 * 60) + (days * 60 * 60 * 24)));
234                 char *output = malloc(100 * sizeof(char));
235
236                 if (days > 0)
237                         sprintf(output, "%dd, %dh, %02dm and %02ds", days, hours, minutes, seconds);
238                 else if (hours > 0)
239                         sprintf(output, "%dh, %02dm and %02ds", hours, minutes, seconds);
240                 else
241                         sprintf(output, "%02dm and %02ds", minutes, seconds);
242
243                 return output;
244         } else {
245                 char *output = strdup("Done");
246                 return output;
247         }
248 }
249
250 static int file_exists(const char *filename)
251 {
252         struct stat fi;
253
254         if ((stat(filename, &fi)) == 0) {
255                 if (fi.st_size > 0)
256                         return 1;
257                 else
258                         return 0;
259         } else
260                 return 0;
261 }
262
263 static void writeSkilltree(char *content, const char *filename)
264 {
265         FILE *fp = fopen(filename, "w");
266         fwrite(content, sizeof(char), strlen(content), fp);
267         fclose(fp);
268 }
269
270 static char *getSkillname(const char *file, int skillid)
271 {
272         char *skilltree;
273         char *skill = NULL;
274         xmlNodePtr n;
275         xmlDocPtr doc = 0;
276         xmlNodePtr root = 0;
277
278         if (!file_exists(file)) {
279                 skilltree = getXmlFromAPI(NULL, NULL, NULL, EVEURL_SKILLTREE);
280                 writeSkilltree(skilltree, file);
281                 free(skilltree);
282         }
283
284         doc = xmlReadFile(file, NULL, 0);
285         if (!doc)
286                 return NULL;
287
288         root = xmlDocGetRootElement(doc);
289
290         for (n = root->children; n; n = n->next) {
291                 xmlNodePtr o;
292                 for (o = n->children; o; o = o->next) {
293                         xmlNodePtr p;
294                         for (p = o->children; p; p = p->next) {
295                                 xmlNodePtr q;
296                                 for (q = p->children; q; q = q->next) {
297                                         xmlNodePtr r;
298                                         for (r = q->children; r; r = r->next) {
299                                                 xmlElementPtr ele = (xmlElementPtr) r;
300                                                 xmlAttrPtr attr = (xmlAttrPtr) ele->attributes;
301                                                 char *mySkill;
302                                                 int id, assigned = 0;
303
304                                                 while (attr != NULL) {
305                                                         if (!strcasecmp((const char *)attr->name, "typeName")) {
306                                                                 mySkill = strdup((const char *)attr->children->content);
307                                                                 assigned = 1;
308                                                         } else if (!strcasecmp((const char *)attr->name, "typeID")) {
309                                                                 id = atoi((const char *)attr->children->content);
310                                                         }
311                                                         attr = attr->next;
312                                                 }
313
314                                                 if (id == skillid) {
315                                                         skill = strdup(mySkill);
316                                                         /* free(mySkill); */
317                                                         goto END;
318                                                 }
319                                                 if (assigned)
320                                                         free(mySkill);
321                                         }
322                                 }
323                         }
324                 }
325         }
326       END:
327         xmlFreeDoc(doc);
328
329         return skill;
330 }
331
332 static char *eve(char *userid, char *apikey, char *charid)
333 {
334         Character *chr = NULL;
335         const char *skillfile = "/tmp/.cesf";
336         int i = 0;
337         char *output = 0;
338         char *timel = 0;
339         char *skill = 0;
340         char *content = 0;
341         time_t now = 0;
342         char *error = 0;
343
344
345         for (i = 0; i < MAXCHARS; i++) {
346                 if (eveCharacters[i].charid != NULL) {
347                         if (strcasecmp(eveCharacters[i].charid, charid) == 0) {
348                                 chr = &eveCharacters[i];
349                                 break;
350                         }
351                 }
352         }
353
354         if (!chr) {
355                 if (num_chars == MAXCHARS - 1)
356                         return NULL;
357                 chr = &eveCharacters[num_chars];
358                 chr->charid = strdup(charid);
359                 num_chars++;
360         }
361
362         if (chr->delay > 0) {
363                 now = time(NULL);
364                 if (now < chr->delay) {
365                         output = strdup("Server error");
366                         return output;
367                 } else
368                         chr->delay = 0;
369         }
370
371         if (isCacheValid(chr->cache)) {
372                 output = (char *)malloc(200 * sizeof(char));
373                 timel = strdup(formatTime(&chr->ends));
374                 sprintf(output, EVE_OUTPUT_FORMAT, chr->skillname, chr->level, timel);
375                 free(timel);
376                 return output;
377         } else {
378                 content = getXmlFromAPI(userid, apikey, charid, EVEURL_TRAINING);
379                 if (content == NULL) {
380                         error = strdup("Server error");
381                         now = time(NULL);
382                         now += (time_t) 1800;
383                         chr->delay = now;
384                         return error;
385                 }
386
387                 if (parseTrainingXml(content, chr)) {
388                         output = (char *)malloc(200 * sizeof(char));
389                         sprintf(output, "API error");
390                         return output;
391                 }
392
393                 output = (char *)malloc(200 * sizeof(char));
394                 timel = formatTime(&chr->ends);
395                 skill = getSkillname(skillfile, chr->skill);
396
397                 chr->skillname = strdup(skill);
398
399                 sprintf(output, EVE_OUTPUT_FORMAT, chr->skillname, chr->level, timel);
400                 free(skill);
401                 return output;
402         }
403
404 }
405
406 void scan_eve(struct text_object *obj, const char *arg)
407 {
408         int argc;
409         char *userid = (char *) malloc(20 * sizeof(char));
410         char *apikey = (char *) malloc(64 * sizeof(char));
411         char *charid = (char *) malloc(20 * sizeof(char));
412
413         argc = sscanf(arg, "%20s %64s %20s", userid, apikey, charid);
414         obj->data.eve.charid = charid;
415         obj->data.eve.userid = userid;
416         obj->data.eve.apikey = apikey;
417
418         init_eve();
419 }
420
421 void print_eve(struct text_object *obj, char *p, int p_max_size)
422 {
423         snprintf(p, p_max_size, "%s",
424                         eve(obj->data.eve.userid,
425                                 obj->data.eve.apikey, obj->data.eve.charid));
426 }