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