1 /*
2     Copyright (C) 2003 Robert Lipe, robertlipe@usa.net
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
17 
18  */
19 #include "defs.h"
20 #include "cet_util.h"
21 #if HAVE_LIBEXPAT
22 #include <expat.h>
23 static XML_Parser psr;
24 #endif
25 
26 static waypoint* wpt_tmp;
27 
28 static gbfile* fin, *fout;
29 
30 static char* noretired = NULL;
31 
32 static
33 arglist_t nav_args[] = {
34   {
35     "noretired", &noretired, "Suppress retired geocaches",
36     NULL, ARGTYPE_BOOL, ARG_NOMINMAX
37   },
38   ARG_TERMINATOR
39 };
40 
41 #define MYNAME "navicache"
42 #define MY_CBUF 4096
43 #define NC_URL	"http://www.navicache.com/cgi-bin/db/displaycache2.pl?CacheID="
44 
45 #if ! HAVE_LIBEXPAT
46 static void
nav_rd_init(const char * fname)47 nav_rd_init(const char* fname)
48 {
49   fatal(MYNAME ": This build excluded GPX support because expat was not installed.\n");
50 }
51 
52 static void
nav_read(void)53 nav_read(void)
54 {
55 }
56 #else
57 
58 static struct
59     nc_type_mapping {
60   geocache_type type;
61   const char* name;
62 } nc_type_map[] = {
63   { gt_unknown, "unknown" },
64   { gt_traditional, "normal" },
65   { gt_multi, "Multi-part" },
66   { gt_virtual, "Virtual" },
67   { gt_event, "event" }
68 };
69 
70 static struct
71     nc_container_mapping {
72   geocache_container type;
73   const char* name;
74 } nc_container_map[] = {
75   { gc_other, "Unknown" },
76   { gc_micro, "Micro" },
77   { gc_regular, "Normal" },
78   { gc_large, "Large" },
79   { gc_virtual, "Virtual" }
80 };
81 
82 static
83 geocache_type
nc_mktype(const char * t)84 nc_mktype(const char* t)
85 {
86   int i;
87   int sz = sizeof(nc_type_map) / sizeof(nc_type_map[0]);
88 
89   for (i = 0; i < sz; i++) {
90     if (0 == case_ignore_strcmp(t, nc_type_map[i].name)) {
91       return nc_type_map[i].type;
92     }
93   }
94   return gt_unknown;
95 }
96 
97 static
98 geocache_container
nc_mkcont(const char * t)99 nc_mkcont(const char* t)
100 {
101   int i;
102   int sz = sizeof(nc_container_map) / sizeof(nc_container_map[0]);
103 
104   for (i = 0; i < sz; i++) {
105     if (0 == case_ignore_strcmp(t, nc_container_map[i].name)) {
106       return nc_container_map[i].type;
107     }
108   }
109   return gc_unknown;
110 }
111 
112 static void
nav_start(void * data,const XML_Char * xml_el,const XML_Char ** xml_attr)113 nav_start(void* data, const XML_Char* xml_el, const XML_Char** xml_attr)
114 {
115   const char* el;
116   const char** attr;
117 
118   el = xml_convert_to_char_string(xml_el);
119   attr = xml_convert_attrs_to_char_string(xml_attr);
120   if (0 == strcmp(el, "CacheDetails")) {
121     const char** ap;
122     geocache_data* gc_data;
123     wpt_tmp = waypt_new();
124     gc_data = waypt_alloc_gc_data(wpt_tmp);
125 
126     for (ap = attr; *ap; ap+=2) {
127       if (0 == strcmp(ap[0], "cache_id")) {
128         int id;
129 
130         id = atoi(ap[1]);
131         xasprintf(&wpt_tmp->shortname, "N%05X", id);
132         xasprintf(&wpt_tmp->url, "%s%d", NC_URL, id);
133       } else if (0 == strcmp(ap[0], "name")) {
134         wpt_tmp->description = xstrdup(ap[1]);
135       } else if (0 == strcmp(ap[0], "user_name")) {
136         gc_data->placer = xstrdup(ap[1]);
137       } else if (0 == strcmp(ap[0], "latitude")) {
138         sscanf(ap[1], "%lf",
139                &wpt_tmp->latitude);
140       } else if (0 == strcmp(ap[0], "longitude")) {
141         sscanf(ap[1], "%lf",
142                &wpt_tmp->longitude);
143       } else if (0 == strcmp(ap[0], "longitude")) {
144         sscanf(ap[1], "%lf",
145                &wpt_tmp->longitude);
146       } else if (0 == strcmp(ap[0], "difficulty")) {
147         float x;
148         sscanf(ap[1], "%f", &x);
149         gc_data->diff = x * 10;
150       } else if (0 == strcmp(ap[0], "terrain")) {
151         float x;
152         sscanf(ap[1], "%f", &x);
153         gc_data->terr = x * 10;
154       } else if (0 == strcmp(ap[0], "cache_type")) {
155         gc_data->type = nc_mktype(ap[1]);
156         if (!strcmp(ap[1], "normal")) {
157           wpt_tmp->icon_descr = "Geocache-regular";
158         } else if (!strcmp(ap[1], "multi-part")) {
159           wpt_tmp->icon_descr = "Geocache-multi";
160         } else if (!strcmp(ap[1], "moving_travelling")) {
161           wpt_tmp->icon_descr = "Geocache-moving";
162         } else {
163           // WARNING: casting away const-ness.
164           xasprintf((char**)&wpt_tmp->icon_descr,
165                     "Geocache-%-.20s", ap[1]);
166         }
167       } else if (0 == strcmp(ap[0], "hidden_date")) {
168         struct tm tm;
169 
170         sscanf(ap[1], "%d-%d-%d",
171                &tm.tm_year,
172                &tm.tm_mon,
173                &tm.tm_mday);
174         tm.tm_mon -= 1;
175         tm.tm_year -= 1900;
176         tm.tm_isdst = 0;
177         tm.tm_hour = 0;
178         tm.tm_min = 0;
179         tm.tm_sec = 0;
180         wpt_tmp->creation_time = mktime(&tm);
181       } else if (0 == strcmp(ap[0], "retired")) {
182         if (!strcmp(ap[1], "yes") && noretired) {
183           xfree(wpt_tmp);
184           return;
185         }
186       } else if (0 == strcmp(ap[0], "cache_size")) {
187         gc_data->container = nc_mkcont(ap[1]);
188       }  else if (0 == strcmp(ap[0], "description")) {
189         gc_data->desc_long.is_html = 1;
190         gc_data->desc_long.utfstring = xstrdup(ap[1]);
191       } else if (0 == strcmp(ap[0], "comments")) {
192         gc_data->desc_short.is_html = 1;
193         gc_data->desc_short.utfstring = xstrdup(ap[1]);
194       }
195     }
196     waypt_add(wpt_tmp);
197   }
198 
199   xml_free_converted_attrs(attr);
200   xml_free_converted_string(el);
201 }
202 
203 static void
nav_end(void * data,const XML_Char * el)204 nav_end(void* data, const XML_Char* el)
205 {
206 }
207 
208 static void
nav_rd_init(const char * fname)209 nav_rd_init(const char* fname)
210 {
211   fin = gbfopen(fname, "r", MYNAME);
212 
213   psr = XML_ParserCreate(NULL);
214   if (!psr) {
215     fatal(MYNAME ":Cannot create XML parser\n");
216   }
217 
218   XML_SetUnknownEncodingHandler(psr, cet_lib_expat_UnknownEncodingHandler, NULL);
219   XML_SetElementHandler(psr, nav_start, nav_end);
220 }
221 
222 static void
nav_read(void)223 nav_read(void)
224 {
225   int len;
226   char buf[MY_CBUF];
227 
228   while ((len = gbfread(buf, 1, sizeof(buf), fin))) {
229     if (!XML_Parse(psr, buf, len, gbfeof(fin))) {
230       fatal(MYNAME ":Parse error at %d: %s\n",
231             (int) XML_GetCurrentLineNumber(psr),
232             XML_ErrorString(XML_GetErrorCode(psr)));
233     }
234   }
235 
236   XML_ParserFree(psr);
237 }
238 
239 #endif
240 
241 static void
nav_rd_deinit(void)242 nav_rd_deinit(void)
243 {
244   gbfclose(fin);
245 }
246 
247 static void
nav_wr_init(const char * fname)248 nav_wr_init(const char* fname)
249 {
250   fatal(MYNAME ": Does not support writing Navicache files.\n");
251   fout = gbfopen(fname, "w", MYNAME);
252 }
253 
254 static void
nav_wr_deinit(void)255 nav_wr_deinit(void)
256 {
257   gbfclose(fout);
258 }
259 
260 static void
nav_write(void)261 nav_write(void)
262 {
263 }
264 
265 ff_vecs_t navicache_vecs = {
266   ff_type_file,
267   { ff_cap_read, ff_cap_none, ff_cap_none },
268   nav_rd_init,
269   nav_wr_init,
270   nav_rd_deinit,
271   nav_wr_deinit,
272   nav_read,
273   nav_write,
274   NULL,
275   nav_args,
276   CET_CHARSET_ASCII, 0	/* CET-REVIEW */
277 };
278