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