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