changes related to temperature and layout
[monky] / src / weather.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  * Please see COPYING for details
7  *
8  * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
9  *      (see AUTHORS)
10  * All rights reserved.
11  *
12  * This program is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  *
24  */
25
26 #include "config.h"
27 #include "conky.h"
28 #include "logging.h"
29 #include "weather.h"
30 #include "temphelper.h"
31 #include "text_object.h"
32 #include "ccurl_thread.h"
33 #include <time.h>
34 #include <ctype.h>
35 #ifdef MATH
36 #include <math.h>
37 #endif /* MATH */
38 #ifdef XOAP
39 #include <libxml/parser.h>
40 #include <libxml/xpath.h>
41 #endif /* XOAP */
42
43 /* WEATHER data */
44 typedef struct PWEATHER_ {
45         char lastupd[32];
46 #ifdef XOAP
47         char xoap_t[32];
48         char icon[3];
49 #endif /* XOAP */
50         int temp;
51         int dew;
52         int cc;
53         int bar;
54         int wind_s;
55         int wind_d;
56         int hmid;
57         int wc;
58 } PWEATHER;
59
60 #ifdef XOAP
61 #define FORECAST_DAYS 5
62 typedef struct PWEATHER_FORECAST_ {
63         int hi[FORECAST_DAYS];
64         int low[FORECAST_DAYS];
65         char icon[FORECAST_DAYS][3];
66         char xoap_t[FORECAST_DAYS][32];
67         char day[FORECAST_DAYS][9];
68         char date[FORECAST_DAYS][7];
69         int wind_s[FORECAST_DAYS];
70         int wind_d[FORECAST_DAYS];
71         int hmid[FORECAST_DAYS];
72         int ppcp[FORECAST_DAYS];
73 } PWEATHER_FORECAST;
74
75 /* Xpath expressions for XOAP xml parsing */
76 #define NUM_XPATH_EXPRESSIONS_CC 8
77 const char *xpath_expression_cc[NUM_XPATH_EXPRESSIONS_CC] = {
78         "/weather/cc/lsup", "/weather/cc/tmp", "/weather/cc/t",
79         "/weather/cc/bar/r", "/weather/cc/wind/s", "/weather/cc/wind/d",
80         "/weather/cc/hmid", "/weather/cc/icon"
81 };
82
83 #define NUM_XPATH_EXPRESSIONS_DF 10
84 const char *xpath_expression_df[NUM_XPATH_EXPRESSIONS_DF] = {
85         "/weather/dayf/day[*]/hi", "/weather/dayf/day[*]/low",
86         "/weather/dayf/day[*]/part[1]/icon", "/weather/dayf/day[*]/part[1]/t",
87         "/weather/dayf/day[*]/part[1]/wind/s","/weather/dayf/day[*]/part[1]/wind/d",
88         "/weather/dayf/day[*]/part[1]/ppcp", "/weather/dayf/day[*]/part[1]/hmid",
89         "/weather/dayf/day[*]/@t", "/weather/dayf/day[*]/@dt"
90 };
91 #endif /* XOAP */
92
93 /* Possible sky conditions */
94 #define NUM_CC_CODES 6
95 const char *CC_CODES[NUM_CC_CODES] = {
96         "SKC", "CLR", "FEW", "SCT", "BKN", "OVC"
97 };
98
99 /* Possible weather modifiers */
100 #define NUM_WM_CODES 9
101 const char *WM_CODES[NUM_WM_CODES] = {
102         "VC", "MI", "BC", "PR", "TS", "BL",
103         "SH", "DR", "FZ"
104 };
105
106 /* Possible weather conditions */
107 #define NUM_WC_CODES 17
108 const char *WC_CODES[NUM_WC_CODES] = {
109         "DZ", "RA", "GR", "GS", "SN", "SG",
110         "FG", "HZ", "FU", "BR", "DU", "SA",
111         "FC", "PO", "SQ", "SS", "DS"
112 };
113
114 static ccurl_location_t *locations_head_cc = 0;
115 #ifdef XOAP
116 static ccurl_location_t *locations_head_df = 0;
117 #endif
118
119 struct weather_data {
120         char uri[128];
121         char data_type[32];
122         int interval;
123 };
124
125 #ifdef XOAP
126 struct weather_forecast_data {
127         char uri[128];
128         unsigned int day;
129         char data_type[32];
130         int interval;
131 };
132 #endif
133
134 void weather_free_info(void)
135 {
136         ccurl_free_locations(&locations_head_cc);
137 #ifdef XOAP
138         ccurl_free_locations(&locations_head_df);
139 #endif
140 }
141
142 int rel_humidity(int dew_point, int air) {
143         const float a = 17.27f;
144         const float b = 237.7f;
145
146         float diff = a*(dew_point/(b+dew_point)-air/(b+air));
147 #ifdef MATH
148         return (int)(100.f*expf(diff));
149 #else
150         return (int)(16.666667163372f*(6.f+diff*(6.f+diff*(3.f+diff))));
151 #endif /* MATH */
152 }
153
154 #ifdef XOAP
155 static void parse_df(PWEATHER_FORECAST *res, xmlXPathContextPtr xpathCtx)
156 {
157         int i, j, k;
158         char *content;
159         xmlXPathObjectPtr xpathObj;
160
161         xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
162         if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
163                         xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
164                 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
165                 NORM_ERR("XOAP error: %s", content);
166                 xmlFree(content);
167                 xmlXPathFreeObject(xpathObj);
168                 return;
169         }
170         xmlXPathFreeObject(xpathObj);
171
172         for (i = 0; i < NUM_XPATH_EXPRESSIONS_DF; i++) {
173                 xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_df[i], xpathCtx);
174                 if (xpathObj != NULL) {
175                         xmlNodeSetPtr nodes = xpathObj->nodesetval;
176                         k = 0;
177                         for (j = 0; j < nodes->nodeNr; ++j) {
178                                 if (nodes->nodeTab[j]->type == XML_ELEMENT_NODE) {
179                                         content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
180                                         switch(i) {
181                                         case 0:
182                                                 res->hi[k] = atoi(content);
183                                                 break;
184                                         case 1:
185                                                 res->low[k] = atoi(content);
186                                                 break;
187                                         case 2:
188                                                 strncpy(res->icon[k], content, 2);
189                                         case 3:
190                                                 strncpy(res->xoap_t[k], content, 31);
191                                                 break;
192                                         case 4:
193                                                 res->wind_s[k] = atoi(content);
194                                                 break;
195                                         case 5:
196                                                 res->wind_d[k] = atoi(content);
197                                                 break;
198                                         case 6:
199                                                 res->ppcp[k] = atoi(content);
200                                                 break;
201                                         case 7:
202                                                 res->hmid[k] = atoi(content);
203                                         }
204                                 } else if (nodes->nodeTab[j]->type == XML_ATTRIBUTE_NODE) {
205                                         content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
206                                         switch(i) {
207                                         case 8:
208                                                 strncpy(res->day[k], content, 8);
209                                                 break;
210                                         case 9:
211                                                 strncpy(res->date[k], content, 6);
212                                         }
213                                 }
214                                 xmlFree(content);
215                                 if (++k == FORECAST_DAYS) break;
216                         }
217                 }
218                 xmlXPathFreeObject(xpathObj);
219         }
220         return;
221 }
222
223 static void parse_weather_forecast_xml(PWEATHER_FORECAST *res, const char *data)
224 {
225         xmlDocPtr doc;
226         xmlXPathContextPtr xpathCtx;
227
228         if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
229                 NORM_ERR("weather_forecast: can't read xml data");
230                 return;
231         }
232
233         xpathCtx = xmlXPathNewContext(doc);
234         if(xpathCtx == NULL) {
235                 NORM_ERR("weather_forecast: unable to create new XPath context");
236                 xmlFreeDoc(doc);
237                 return;
238         }
239
240         parse_df(res, xpathCtx);
241         xmlXPathFreeContext(xpathCtx);
242         xmlFreeDoc(doc);
243         return;
244 }
245
246 static void parse_cc(PWEATHER *res, xmlXPathContextPtr xpathCtx)
247 {
248         int i;
249         char *content;
250         xmlXPathObjectPtr xpathObj;
251
252         xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
253         if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
254                         xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
255                 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
256                 NORM_ERR("XOAP error: %s", content);
257                 xmlFree(content);
258                 xmlXPathFreeObject(xpathObj);
259                 return;
260         }
261         xmlXPathFreeObject(xpathObj);
262
263         for (i = 0; i < NUM_XPATH_EXPRESSIONS_CC; i++) {
264                 xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_cc[i], xpathCtx);
265                 if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr >0 &&
266                                 xpathObj->nodesetval->nodeTab[0]->type ==
267                                 XML_ELEMENT_NODE) {
268                         content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
269                         switch(i) {
270                                 case 0:
271                                         strncpy(res->lastupd, content, 31);
272                                         break;
273                                 case 1:
274                                         res->temp = atoi(content);
275                                         break;
276                                 case 2:
277                                         strncpy(res->xoap_t, content, 31);
278                                         break;
279                                 case 3:
280                                         res->bar = atoi(content);
281                                         break;
282                                 case 4:
283                                         res->wind_s = atoi(content);
284                                         break;
285                                 case 5:
286                                         res->wind_d = atoi(content);
287                                         break;
288                                 case 6:
289                                         res->hmid = atoi(content);
290                                         break;
291                                 case 7:
292                                         strncpy(res->icon, content, 2);
293                         }
294                         xmlFree(content);
295                 }
296                 xmlXPathFreeObject(xpathObj);
297         }
298         return;
299 }
300
301 static void parse_weather_xml(PWEATHER *res, const char *data)
302 {
303         xmlDocPtr doc;
304         xmlXPathContextPtr xpathCtx;
305
306         if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
307                 NORM_ERR("weather: can't read xml data");
308                 return;
309         }
310
311         xpathCtx = xmlXPathNewContext(doc);
312         if(xpathCtx == NULL) {
313                 NORM_ERR("weather: unable to create new XPath context");
314                 xmlFreeDoc(doc);
315                 return;
316         }
317
318         parse_cc(res, xpathCtx);
319         xmlXPathFreeContext(xpathCtx);
320         xmlFreeDoc(doc);
321         return;
322 }
323 #endif /* XOAP */
324
325 /*
326  * Horrible hack to avoid using regexes
327  *
328  */
329
330 static inline void parse_token(PWEATHER *res, char *token) {
331
332         int i;
333         char s_tmp[64];
334
335         switch (strlen(token)) {
336
337                 //Check all tokens 2 chars long
338                 case 2:
339
340                         //Check if token is a weather condition
341                         for (i=0; i<2; i++) {
342                                 if (!isalpha(token[i])) break;
343                         }
344                         if (i==2) {
345                                 for(i=0; i<NUM_WC_CODES; i++) {
346                                         if (!strncmp(token, WC_CODES[i], 2)) {
347                                                 res->wc=i+1;
348                                                 break;
349                                         }
350                                 }
351                                 return;
352                         }
353
354                         //Check for CB
355                         if (!strcmp(token, "CB")) {
356                                 res->cc = 8;
357                                 return;
358                         }
359
360                         break;
361
362                         //Check all tokens 3 chars long
363                 case 3:
364
365                         //Check if token is a modified weather condition
366                         if ((token[0] == '+') || (token[0] == '-')) {
367                                 for (i=1; i<3; i++) {
368                                         if (!isalpha(token[i])) break;
369                                 }
370                                 if (i==3) {
371                                         for(i=0; i<NUM_WC_CODES; i++) {
372                                                 if (!strncmp(&token[1], WC_CODES[i], 2)) {
373                                                         res->wc=i+1;
374                                                         break;
375                                                 }
376                                         }
377                                         return;
378                                 }
379                         }
380
381                         //Check for NCD or NSC
382                         if ((!strcmp(token, "NCD")) || (!strcmp(token, "NSC"))) {
383                                 res->cc = 1;
384                                 return;
385                         }
386
387                         //Check for TCU
388                         if (!strcmp(token, "TCU")) {
389                                 res->cc = 7;
390                                 return;
391                         }
392
393                         break;
394
395                         //Check all tokens 4 chars long
396                 case 4:
397
398                         //Check if token is a modified weather condition
399                         for(i=0; i<NUM_WM_CODES; i++) {
400                                 if (!strncmp(token, WM_CODES[i], 2)) {
401                                         for(i=0; i<NUM_WC_CODES; i++) {
402                                                 if (!strncmp(&token[2], WC_CODES[i], 2)) {
403                                                         res->wc=i+1;
404                                                         return;
405                                                 }
406                                         }
407                                         break;
408                                 }
409                         }
410
411                         break;
412
413                         //Check all tokens 5 chars long
414                 case 5:
415
416                         //Check for CAVOK
417                         if (!strcmp(token, "CAVOK")) {
418                                 res->cc = 1;
419                                 return;
420                         }
421
422                         //Check if token is the temperature
423                         for (i=0; i<2; i++) {
424                                 if (!isdigit(token[i])) break;
425                         }
426                         if ((i==2) && (token[2] == '/')) {
427                                 for (i=3; i<5; i++) {
428                                         if (!isdigit(token[i])) break;
429                                 }
430                                 if (i==5) {
431                                         //First 2 digits gives the air temperature
432                                         res->temp=atoi(token);
433
434                                         //4th and 5th digits gives the dew point temperature
435                                         res->dew=atoi(&token[3]);
436
437                                         //Compute humidity
438                                         res->hmid = rel_humidity(res->dew, res->temp);
439
440                                         return;
441                                 }
442                         }
443
444                         //Check if token is the pressure
445                         if ((token[0] == 'Q') || (token[0] == 'A')) {
446                                 for (i=1; i<5; i++) {
447                                         if (!isdigit(token[i])) break;
448                                 }
449                                 if (i==5) {
450                                         if (token[0] == 'A') {
451                                                 //Convert inches of mercury to mbar
452                                                 res->bar = (int)(atoi(&token[1])*0.338637526f);
453                                                 return;
454                                         }
455
456                                         //Last 4 digits is pressure im mbar
457                                         res->bar = atoi(&token[1]);
458                                         return;
459                                 }
460                         }
461
462                         //Check if token is a modified weather condition
463                         if ((token[0] == '+') || (token[0] == '-')) {
464                                 for(i=0; i<NUM_WM_CODES; i++) {
465                                         if (!strncmp(&token[1], WM_CODES[i], 2)) {
466                                                 for(i=0; i<NUM_WC_CODES; i++) {
467                                                         if (!strncmp(&token[3], WC_CODES[i], 2)) {
468                                                                 res->wc=i+1;
469                                                                 return;
470                                                         }
471                                                 }
472                                                 break;
473                                         }
474                                 }
475                         }
476                         break;
477
478                         //Check all tokens 6 chars long
479                 case 6:
480
481                         //Check if token is the cloud cover
482                         for (i=0; i<3; i++) {
483                                 if (!isalpha(token[i])) break;
484                         }
485                         if (i==3) {
486                                 for (i=3; i<6; i++) {
487                                         if (!isdigit(token[i])) break;
488                                 }
489                                 if (i==6) {
490                                         //Check if first 3 digits gives the cloud cover condition
491                                         for(i=0; i<NUM_CC_CODES; i++) {
492                                                 if (!strncmp(token, CC_CODES[i], 3)) {
493                                                         res->cc=i+1;
494                                                         break;
495                                                 }
496                                         }
497                                         return;
498                                 }
499                         }
500
501                         //Check if token is positive temp and negative dew
502                         for (i=0; i<2; i++) {
503                                 if (!isdigit(token[i])) break;
504                         }
505                         if ((i==2) && (token[2] == '/')  && (token[3] == 'M')) {
506                                 for (i=4; i<6; i++) {
507                                         if (!isdigit(token[i])) break;
508                                 }
509                                 if (i==6) {
510                                         //1st and 2nd digits gives the temperature
511                                         res->temp = atoi(token);
512
513                                         //5th and 6th digits gives the dew point temperature
514                                         res->dew = -atoi(&token[4]);
515
516                                         //Compute humidity
517                                         res->hmid = rel_humidity(res->dew, res->temp);
518
519                                         return;
520                                 }
521                         }
522
523                         break;
524
525                         //Check all tokens 7 chars long
526                 case 7:
527
528                         //Check if token is the observation time
529                         for (i=0; i<6; i++) {
530                                 if (!isdigit(token[i])) break;
531                         }
532                         if ((i==6) && (token[6] == 'Z')) return;
533
534                         //Check if token is the wind speed/direction in knots
535                         for (i=0; i<5; i++) {
536                                 if (!isdigit(token[i])) break;
537                         }
538                         if ((i==5) && (token[5] == 'K') &&  (token[6] == 'T')) {
539
540                                 //First 3 digits are wind direction
541                                 strncpy(s_tmp, token, 3);
542                                 s_tmp[3]='\0';
543                                 res->wind_d=atoi(s_tmp);
544
545                                 //4th and 5th digit are wind speed in knots (convert to km/hr)
546                                 res->wind_s = (int)(atoi(&token[3])*1.852);
547
548                                 return;
549                         }
550
551                         //Check if token is negative temperature
552                         if ((token[0] == 'M') && (token[4] == 'M')) {
553                                 for (i=1; i<3; i++) {
554                                         if (!isdigit(token[i])) break;
555                                 }
556                                 if ((i==3) && (token[3] == '/')) {
557                                         for (i=5; i<7; i++) {
558                                                 if (!isdigit(token[i])) break;
559                                         }
560                                         if (i==7) {
561                                                 //2nd and 3rd digits gives the temperature
562                                                 res->temp = -atoi(&token[1]);
563
564                                                 //6th and 7th digits gives the dew point temperature
565                                                 res->dew = -atoi(&token[5]);
566
567                                                 //Compute humidity
568                                                 res->hmid = rel_humidity(res->dew, res->temp);
569
570                                                 return;
571                                         }
572                                 }
573                         }
574
575                         //Check if token is wind variability
576                         for (i=0; i<3; i++) {
577                                 if (!isdigit(token[i])) break;
578                         }
579                         if ((i==3) && (token[3] == 'V')) {
580                                 for (i=4; i<7; i++) {
581                                         if (!isdigit(token[i])) break;
582                                 }
583                                 if (i==7) return;
584                         }
585
586                         break;
587
588                         //Check all tokens 8 chars long
589                 case 8:
590
591                         //Check if token is the wind speed/direction in m/s
592                         for (i=0; i<5; i++) {
593                                 if (!isdigit(token[i])) break;
594                         }
595                         if ((i==5)&&(token[5] == 'M')&&(token[6] == 'P')&&(token[7] == 'S')) {
596
597                                 //First 3 digits are wind direction
598                                 strncpy(s_tmp, token, 3);
599                                 s_tmp[3]='\0';
600                                 res->wind_d=atoi(s_tmp);
601
602                                 //4th and 5th digit are wind speed in m/s (convert to km/hr)
603                                 res->wind_s = (int)(atoi(&token[3])*3.6);
604
605                                 return;
606                         }
607
608                 default:
609
610                         //printf("token : %s\n", token);
611                         break;
612         }
613 }
614
615 #ifdef XOAP
616 void parse_weather_forecast(void *result, const char *data)
617 {
618         PWEATHER_FORECAST *res = (PWEATHER_FORECAST*)result;
619         /* Reset results */
620         memset(res, 0, sizeof(PWEATHER_FORECAST));
621
622         //Check if it is an xml file
623         if ( strncmp(data, "<?xml ", 6) == 0 ) {
624                 parse_weather_forecast_xml(res, data);
625         }
626 }
627 #endif /* XOAP */
628
629 void parse_weather(void *result, const char *data)
630 {
631         PWEATHER *res = (PWEATHER*)result;
632         /* Reset results */
633         memset(res, 0, sizeof(PWEATHER));
634
635 #ifdef XOAP
636         //Check if it is an xml file
637         if ( strncmp(data, "<?xml ", 6) == 0 ) {
638                 parse_weather_xml(res, data);
639         } else
640 #endif /* XOAP */
641         {
642                 //We assume its a text file
643                 char s_tmp[256];
644                 const char delim[] = " ";
645
646                 //Divide time stamp and metar data
647                 if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
648
649                         //Process all tokens
650                         char *p_tok = NULL;
651                         char *p_save = NULL;
652
653                         if ((strtok_r(s_tmp, delim, &p_save)) != NULL) {
654
655                                 //Jump first token, must be icao
656                                 p_tok = strtok_r(NULL, delim, &p_save);
657
658                                 do {
659
660                                         parse_token(res, p_tok);
661                                         p_tok = strtok_r(NULL, delim, &p_save);
662
663                                 } while (p_tok != NULL);
664                         }
665                         return;
666                 }
667                 else {
668                         return;
669                 }
670         }
671 }
672
673 void wind_deg_to_dir(char *p, int p_max_size, int wind_deg) {
674         if ((wind_deg >= 349) || (wind_deg < 12)) {
675                 strncpy(p, "N", p_max_size);
676         } else if (wind_deg < 33) {
677                 strncpy(p, "NNE", p_max_size);
678         } else if (wind_deg < 57) {
679                 strncpy(p, "NE", p_max_size);
680         } else if (wind_deg < 79) {
681                 strncpy(p, "ENE", p_max_size);
682         } else if (wind_deg < 102) {
683                 strncpy(p, "E", p_max_size);
684         } else if (wind_deg < 124) {
685                 strncpy(p, "ESE", p_max_size);
686         } else if (wind_deg < 147) {
687                 strncpy(p, "SE", p_max_size);
688         } else if (wind_deg < 169) {
689                 strncpy(p, "SSE", p_max_size);
690         } else if (wind_deg < 192) {
691                 strncpy(p, "S", p_max_size);
692         } else if (wind_deg < 214) {
693                 strncpy(p, "SSW", p_max_size);
694         } else if (wind_deg < 237) {
695                 strncpy(p, "SW", p_max_size);
696         } else if (wind_deg < 259) {
697                 strncpy(p, "WSW", p_max_size);
698         } else if (wind_deg < 282) {
699                         strncpy(p, "W", p_max_size);
700         } else if (wind_deg < 304) {
701                 strncpy(p, "WNW", p_max_size);
702         } else if (wind_deg < 327) {
703                 strncpy(p, "NW", p_max_size);
704         } else if (wind_deg < 349) {
705                 strncpy(p, "NNW", p_max_size);
706         };
707 }
708
709 #ifdef XOAP
710 static void weather_forecast_process_info(char *p, int p_max_size, char *uri, unsigned int day, char *data_type, int interval)
711 {
712         PWEATHER_FORECAST *data;
713
714         ccurl_location_t *curloc = ccurl_find_location(&locations_head_df, uri);
715         if (!curloc->p_timed_thread) {
716                 curloc->result = malloc(sizeof(PWEATHER_FORECAST));
717                 memset(curloc->result, 0, sizeof(PWEATHER_FORECAST));
718                 curloc->process_function = &parse_weather_forecast;
719                 ccurl_init_thread(curloc, interval);
720                 if (!curloc->p_timed_thread) {
721                         NORM_ERR("error setting up weather_forecast thread");
722                 }
723         }
724
725         timed_thread_lock(curloc->p_timed_thread);
726         data = (PWEATHER_FORECAST*)curloc->result;
727         if (strcmp(data_type, "hi") == EQUAL) {
728                 temp_print(p, p_max_size, data->hi[day], TEMP_CELSIUS);
729         } else if (strcmp(data_type, "low") == EQUAL) {
730                 temp_print(p, p_max_size, data->low[day], TEMP_CELSIUS);
731         } else if (strcmp(data_type, "icon") == EQUAL) {
732                 strncpy(p, data->icon[day], p_max_size);
733         } else if (strcmp(data_type, "forecast") == EQUAL) {
734                 strncpy(p, data->xoap_t[day], p_max_size);
735         } else if (strcmp(data_type, "wind_speed") == EQUAL) {
736                 snprintf(p, p_max_size, "%d", data->wind_s[day]);
737         } else if (strcmp(data_type, "wind_dir") == EQUAL) {
738                 wind_deg_to_dir(p, p_max_size, data->wind_d[day]);
739         } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
740                 snprintf(p, p_max_size, "%d", data->wind_d[day]);
741         } else if (strcmp(data_type, "humidity") == EQUAL) {
742                 snprintf(p, p_max_size, "%d", data->hmid[day]);
743         } else if (strcmp(data_type, "precipitation") == EQUAL) {
744                 snprintf(p, p_max_size, "%d", data->ppcp[day]);
745         } else if (strcmp(data_type, "day") == EQUAL) {
746                 strncpy(p, data->day[day], p_max_size);
747         } else if (strcmp(data_type, "date") == EQUAL) {
748                 strncpy(p, data->date[day], p_max_size);
749         }
750
751         timed_thread_unlock(curloc->p_timed_thread);
752 }
753 #endif /* XOAP */
754
755 static void weather_process_info(char *p, int p_max_size, char *uri, char *data_type, int interval)
756 {
757         static const char *wc[] = {
758                 "", "drizzle", "rain", "hail", "soft hail",
759                 "snow", "snow grains", "fog", "haze", "smoke",
760                 "mist", "dust", "sand", "funnel cloud tornado",
761                 "dust/sand", "squall", "sand storm", "dust storm"
762         };
763         PWEATHER *data;
764
765         ccurl_location_t *curloc = ccurl_find_location(&locations_head_cc, uri);
766         if (!curloc->p_timed_thread) {
767                 curloc->result = malloc(sizeof(PWEATHER));
768                 memset(curloc->result, 0, sizeof(PWEATHER));
769                 curloc->process_function = &parse_weather;
770                 ccurl_init_thread(curloc, interval);
771                 if (!curloc->p_timed_thread) {
772                         NORM_ERR("error setting up weather thread");
773                 }
774         }
775
776         timed_thread_lock(curloc->p_timed_thread);
777         data = (PWEATHER*)curloc->result;
778         if (strcmp(data_type, "last_update") == EQUAL) {
779                 strncpy(p, data->lastupd, p_max_size);
780         } else if (strcmp(data_type, "temperature") == EQUAL) {
781                 temp_print(p, p_max_size, data->temp, TEMP_CELSIUS);
782         } else if (strcmp(data_type, "cloud_cover") == EQUAL) {
783 #ifdef XOAP
784                 if (data->xoap_t[0] != '\0') {
785                         char *s = p;
786                         strncpy(p, data->xoap_t, p_max_size);
787                         while (*s) {
788                                 *s = tolower(*s);
789                                 s++;
790                         }
791                 } else
792 #endif /* XOAP */
793                         if (data->cc == 0) {
794                                 strncpy(p, "", p_max_size);
795                         } else if (data->cc < 3) {
796                                 strncpy(p, "clear", p_max_size);
797                         } else if (data->cc < 5) {
798                                 strncpy(p, "partly cloudy", p_max_size);
799                         } else if (data->cc == 5) {
800                                 strncpy(p, "cloudy", p_max_size);
801                         } else if (data->cc == 6) {
802                                 strncpy(p, "overcast", p_max_size);
803                         } else if (data->cc == 7) {
804                                 strncpy(p, "towering cumulus", p_max_size);
805                         } else  {
806                                 strncpy(p, "cumulonimbus", p_max_size);
807                         }
808 #ifdef XOAP
809         } else if (strcmp(data_type, "icon") == EQUAL) {
810                 strncpy(p, data->icon, p_max_size);
811 #endif /* XOAP */
812         } else if (strcmp(data_type, "pressure") == EQUAL) {
813                 snprintf(p, p_max_size, "%d", data->bar);
814         } else if (strcmp(data_type, "wind_speed") == EQUAL) {
815                 snprintf(p, p_max_size, "%d", data->wind_s);
816         } else if (strcmp(data_type, "wind_dir") == EQUAL) {
817                 wind_deg_to_dir(p, p_max_size, data->wind_d);
818         } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
819                 snprintf(p, p_max_size, "%d", data->wind_d);
820
821         } else if (strcmp(data_type, "humidity") == EQUAL) {
822                 snprintf(p, p_max_size, "%d", data->hmid);
823         } else if (strcmp(data_type, "weather") == EQUAL) {
824                 strncpy(p, wc[data->wc], p_max_size);
825         }
826
827         timed_thread_unlock(curloc->p_timed_thread);
828 }
829
830 #ifdef XOAP
831 /* xoap suffix for weather from weather.com */
832 static char *xoap_cc = NULL;
833 static char *xoap_df = NULL;
834 #endif /* XOAP */
835
836 static int process_weather_uri(char *uri, char *locID, int dayf UNUSED_ATTR)
837 {
838         /* locID MUST BE upper-case */
839         char *tmp_p = locID;
840
841         while (*tmp_p) {
842                 *tmp_p = toupper(*tmp_p);
843                 tmp_p++;
844         }
845
846         /* Construct complete uri */
847 #ifdef XOAP
848         if (strstr(uri, "xoap.weather.com")) {
849                 if ((dayf == 0) && (xoap_cc != NULL)) {
850                         strcat(uri, locID);
851                         strcat(uri, xoap_cc);
852                 } else if ((dayf == 1) && (xoap_df != NULL)) {
853                         strcat(uri, locID);
854                         strcat(uri, xoap_df);
855                 } else {
856                         free(uri);
857                         uri = NULL;
858                 }
859         } else
860 #endif /* XOAP */
861         if (strstr(uri, "weather.noaa.gov")) {
862                 strcat(uri, locID);
863                 strcat(uri, ".TXT");
864         } else  if (!strstr(uri, "localhost") && !strstr(uri, "127.0.0.1")) {
865                 return -1;
866         }
867         return 0;
868 }
869
870 #ifdef XOAP
871
872 /*
873  * TODO: make the xoap keys file readable from the config file
874  *       make the keys directly readable from the config file
875  *       make the xoap keys file giveable as a command line option
876  */
877 void load_xoap_keys(void)
878 {
879         FILE *fp;
880         char *par  = (char *) malloc(11 * sizeof(char));
881         char *key  = (char *) malloc(17 * sizeof(char));
882         char *xoap = (char *) malloc(64 * sizeof(char));
883
884         to_real_path(xoap, XOAP_FILE);
885         fp = fopen(xoap, "r");
886         if (fp != NULL) {
887                 if (fscanf(fp, "%10s %16s", par, key) == 2) {
888                         xoap_cc = (char *) malloc(128 * sizeof(char));
889                         xoap_df = (char *) malloc(128 * sizeof(char));
890
891                         strcpy(xoap_cc, "?cc=*&link=xoap&prod=xoap&par=");
892                         strcat(xoap_cc, par);
893                         strcat(xoap_cc, "&key=");
894                         strcat(xoap_cc, key);
895                         strcat(xoap_cc, "&unit=m");
896
897                         /* TODO: Use FORECAST_DAYS instead of 5 */
898                         strcpy(xoap_df, "?dayf=5&link=xoap&prod=xoap&par=");
899                         strcat(xoap_df, par);
900                         strcat(xoap_df, "&key=");
901                         strcat(xoap_df, key);
902                         strcat(xoap_df, "&unit=m");
903                 }
904                 fclose(fp);
905         }
906         free(par);
907         free(key);
908         free(xoap);
909 }
910
911 void scan_weather_forecast_arg(struct text_object *obj, const char *arg, void *free_at_crash)
912 {
913         int argc;
914         struct weather_forecast_data *wfd;
915         float interval = 0;
916         char *locID = (char *) malloc(9 * sizeof(char));
917
918         wfd = malloc(sizeof(struct weather_forecast_data));
919         memset(wfd, 0, sizeof(struct weather_forecast_data));
920
921         argc = sscanf(arg, "%119s %8s %1u %31s %f", wfd->uri, locID, &wfd->day, wfd->data_type, &interval);
922
923         if (argc < 4) {
924                 free(locID);
925                 free(wfd);
926                 CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather_forecast");
927         }
928         if (process_weather_uri(wfd->uri, locID, 1)) {
929                 free(locID);
930                 free(wfd);
931                 CRIT_ERR(obj, free_at_crash, \
932                                 "could not recognize the weather forecast uri");
933         }
934
935         /* Limit the day between 0 (today) and FORECAST_DAYS */
936         if (wfd->day >= FORECAST_DAYS) {
937                 wfd->day = FORECAST_DAYS-1;
938         }
939
940         /* Limit the data retrieval interval to 3 hours and an half */
941         if (interval < 210) {
942                 interval = 210;
943         }
944
945         /* Convert to seconds */
946         wfd->interval = interval * 60;
947         free(locID);
948
949         DBGP("weather_forecast: fetching %s for day %d from %s every %d seconds", \
950                         wfd->data_type, wfd->day, wfd->uri, wfd->interval);
951
952         obj->data.opaque = wfd;
953 }
954
955 void print_weather_forecast(struct text_object *obj, char *p, int p_max_size)
956 {
957         struct weather_forecast_data *wfd = obj->data.opaque;
958
959         if (!wfd || !wfd->uri) {
960                 NORM_ERR("error processing weather forecast data, check that you have a valid XOAP key if using XOAP.");
961                 return;
962         }
963         weather_forecast_process_info(p, p_max_size, wfd->uri, wfd->day, wfd->data_type, wfd->interval);
964 }
965 #endif /* XOAP */
966
967 void scan_weather_arg(struct text_object *obj, const char *arg, void *free_at_crash)
968 {
969         int argc;
970         struct weather_data *wd;
971         char *locID = (char *) malloc(9 * sizeof(char));
972         float interval = 0;
973
974         wd = malloc(sizeof(struct weather_data));
975         memset(wd, 0, sizeof(struct weather_data));
976
977         argc = sscanf(arg, "%119s %8s %31s %f", wd->uri, locID, wd->data_type, &interval);
978
979         if (argc < 3) {
980                 free(locID);
981                 free(wd);
982                 CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather");
983         }
984         if (process_weather_uri(wd->uri, locID, 0)) {
985                 free(locID);
986                 free(wd);
987                 CRIT_ERR(obj, free_at_crash, \
988                                 "could not recognize the weather uri");
989         }
990
991         /* Limit the data retrieval interval to half hour min */
992         if (interval < 30) {
993                 interval = 30;
994         }
995
996         /* Convert to seconds */
997         wd->interval = interval * 60;
998         free(locID);
999
1000         DBGP("weather: fetching %s from %s every %d seconds", \
1001                         wd->data_type, wd->uri, wd->interval);
1002
1003         obj->data.opaque = wd;
1004 }
1005
1006 void print_weather(struct text_object *obj, char *p, int p_max_size)
1007 {
1008         struct weather_data *wd = obj->data.opaque;
1009
1010         if (!wd || !wd->uri) {
1011                 NORM_ERR("error processing weather data, check that you have a valid XOAP key if using XOAP.");
1012                 return;
1013         }
1014         weather_process_info(p, p_max_size, wd->uri, wd->data_type, wd->interval);
1015 }
1016
1017 void free_weather(struct text_object *obj)
1018 {
1019         if (obj->data.opaque) {
1020                 free(obj->data.opaque);
1021                 obj->data.opaque = NULL;
1022         }
1023 }