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