1 /*
2 Copyright (C) 2002-2014 Robert Lipe, robertlipe+source@gpsbabel.org
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 */
19
20
21 #include "defs.h"
22 #include "xmlgeneric.h"
23 #include <QtCore/QFile>
24 #include <QtCore/QTextStream>
25 #include <QtCore/QXmlStreamAttributes>
26
27 static char* encoded_points = NULL;
28 static char* encoded_levels = NULL;
29 static QString script;
30 static route_head** routehead;
31 static int* routecount;
32 static short_handle desc_handle;
33 static QString rd_fname;
34
35 static int serial = 0;
36
37 #define MYNAME "google"
38
39 static xg_callback goog_points, goog_levels, goog_poly_e, goog_script;
40 static xg_callback goog_segment_s, goog_segment, goog_td_s, goog_td_b;
41 static xg_callback goog_td_e;
42
43 static
44 xg_tag_mapping google_map[] = {
45 { goog_points, cb_cdata, "/page/directions/polyline/points" },
46 { goog_levels, cb_cdata, "/page/directions/polyline/levels" },
47 { goog_poly_e, cb_end, "/page/directions/polyline" },
48 { goog_script, cb_cdata, "/html/head/script" },
49 { goog_segment_s, cb_start, "/page/directions/segments/segment" },
50 { goog_segment, cb_cdata, "/page/directions/segments/segment" },
51 { goog_td_s, cb_start, "/div/table/tr/td" },
52 { goog_td_s, cb_start, "/div/div/table/tr/td" },
53 { goog_td_b, cb_cdata, "/div/table/tr/td/b" },
54 { goog_td_b, cb_cdata, "/div/div/table/tr/td/b" },
55 { goog_td_e, cb_end, "/div/table/tr/td" },
56 { goog_td_e, cb_end, "/div/div/table/tr/td" },
57 { NULL, (xg_cb_type)0, NULL }
58 };
59
goog_script(xg_string args,const QXmlStreamAttributes *)60 void goog_script(xg_string args, const QXmlStreamAttributes*)
61 {
62 script += args;
63 }
64
65
goog_points(xg_string args,const QXmlStreamAttributes *)66 void goog_points(xg_string args, const QXmlStreamAttributes*)
67 {
68 if (encoded_points) {
69 encoded_points = xstrappend(encoded_points, CSTRc(args));
70 } else {
71 encoded_points = xstrdup(args);
72 }
73 }
74
75
goog_levels(xg_string args,const QXmlStreamAttributes *)76 void goog_levels(xg_string args, const QXmlStreamAttributes*)
77 {
78 if (encoded_levels) {
79 encoded_levels = xstrappend(encoded_levels, CSTRc(args));
80 } else {
81 encoded_levels = xstrdup(args);
82 }
83 }
84
85 static char goog_segname[7];
86 static QString goog_realname;
87 static int goog_segroute = 0;
88
89 /*
90 * The segments contain an index into the points array. We use that
91 * index to find the waypoint and insert a better name for it.
92 */
goog_segment_s(xg_string args,const QXmlStreamAttributes * attrv)93 void goog_segment_s(xg_string args, const QXmlStreamAttributes* attrv)
94 {
95 QStringRef ptidx = attrv->value("pointIndex");
96 if (!ptidx.isEmpty()) {
97 snprintf(goog_segname, sizeof(goog_segname), "\\%5.5x",
98 ptidx.toString().toUInt());
99 }
100 }
101
goog_segment(xg_string args,const QXmlStreamAttributes *)102 void goog_segment(xg_string args, const QXmlStreamAttributes*)
103 {
104 Waypoint* wpt_tmp;
105
106 if (routehead[goog_segroute]) {
107 wpt_tmp = route_find_waypt_by_name(routehead[goog_segroute], goog_segname);
108 if (wpt_tmp) {
109 wpt_tmp->shortname = mkshort(desc_handle, args);
110 wpt_tmp->description = args;
111 }
112 }
113 }
114
goog_td_s(xg_string args,const QXmlStreamAttributes * attrv)115 void goog_td_s(xg_string args, const QXmlStreamAttributes* attrv)
116 {
117 bool isdesc = false, isseg = false;
118 QStringRef aclass = attrv->value("class");
119 QStringRef id = attrv->value("id");
120
121 if (aclass.isEmpty() || id.isEmpty()) {
122 return;
123 }
124
125 isdesc = (aclass == "desc");
126 isseg = (aclass == "dirsegtext");
127
128 if (isdesc) {
129 QStringRef subid(id.string(), id.position() + 6, id.length() - 6);
130
131 goog_segroute = 0;
132 snprintf(goog_segname, sizeof(goog_segname), "\\%5.5x",
133 subid.toString().toUInt());
134 } else if (isseg) {
135 QString idstr = id.toString();
136 int first_us;
137
138 goog_segroute = 0;
139
140 first_us = idstr.indexOf("_");
141 if (idstr.indexOf("_", first_us + 1) != -1) {
142 goog_segroute = idstr.mid(first_us + 1).toUInt();
143 }
144
145 snprintf(goog_segname, sizeof(goog_segname), "\\%5.5x",
146 idstr.mid(idstr.lastIndexOf("_") + 1).toUInt() +
147 routecount[goog_segroute]);
148 }
149 }
150
goog_td_b(xg_string args,const QXmlStreamAttributes *)151 void goog_td_b(xg_string args, const QXmlStreamAttributes*)
152 {
153 if (goog_segname[0] == '\\' && !strchr(CSTRc(args), '\xa0')) {
154 goog_realname = args;
155 }
156 }
157
goog_td_e(xg_string args,const QXmlStreamAttributes *)158 void goog_td_e(xg_string args, const QXmlStreamAttributes*)
159 {
160 if (goog_segname[0] == '\\' && !goog_realname.isEmpty()) {
161 goog_segment(goog_realname, NULL/*unused*/);
162 }
163 goog_segname[0] = '\0';
164 goog_realname.clear();
165 }
166
decode_goog64(char ** str)167 static long decode_goog64(char** str)
168 {
169 long result = 0;
170 unsigned char c = 0;
171 unsigned char shift = 0;
172
173 if (!(**str)) {
174 return 0;
175 }
176
177 do {
178 c = (unsigned char)(*(*str)++)-'?';
179 result |= (c & 31)<<shift;
180 shift += 5;
181 } while (c & ~31 && **str);
182
183 if (result & 1) {
184 result = ~result;
185 }
186 return result/2;
187 }
188
goog_poly_e(xg_string args,const QXmlStreamAttributes *)189 void goog_poly_e(xg_string args, const QXmlStreamAttributes*)
190 {
191 long lat = 0;
192 long lon = 0;
193 long level = 0;
194 long level1 = -9999;
195 long level2 = -9999;
196 char* str = encoded_points;
197 char* lstr = encoded_levels;
198
199 routehead[goog_segroute] = route_head_alloc();
200 route_add_head(routehead[goog_segroute]);
201 routecount[goog_segroute] = serial;
202
203 while (str && *str) {
204 lat += decode_goog64(&str);
205 lon += decode_goog64(&str);
206
207 level = -1;
208 level2 = level1;
209 if (lstr && *lstr) {
210 level1 = -decode_goog64(&lstr);
211 } else {
212 level1 = -9999;
213 }
214 level = (level1<level2)?level1:level2;
215
216 /* level of -9999 happens for endpoints */
217 if (level == -9999) {
218 level = 99999;
219 }
220
221 {
222 Waypoint* wpt_tmp = new Waypoint;
223 wpt_tmp->latitude = lat / 100000.0;
224 wpt_tmp->longitude = lon / 100000.0;
225 wpt_tmp->route_priority=level;
226 wpt_tmp->shortname = QString().sprintf( "\\%5.5x", serial++);
227 route_add_wpt(routehead[goog_segroute], wpt_tmp);
228 }
229 }
230
231 }
232
233 static void
google_rd_init(const QString & fname)234 google_rd_init(const QString& fname)
235 {
236 rd_fname = fname;
237
238 desc_handle = mkshort_new_handle();
239 setshort_length(desc_handle, 12);
240
241 xml_init(fname, google_map, NULL);
242 }
243
244 static void
goog_read_file(void)245 goog_read_file(void)
246 {
247 QFile src(rd_fname);
248
249 src.open(QIODevice::ReadOnly);
250
251 QTextStream tstr(&src);
252 tstr.setCodec("ISO-8859-1");
253
254 QString preamble = tstr.read(256);
255 QString needle("http-equiv=\"content-type\" content=\"text/html; charset=");
256
257 if (!preamble.contains(needle)) {
258 // let QXmlStreamReader do its best if we can't figure it out...
259 xml_read();
260 return;
261 }
262
263 int idx = preamble.indexOf(needle);
264 QString charset = preamble.mid(idx + needle.length());
265
266 int endq = charset.indexOf('"');
267 if (endq != -1) {
268 charset = charset.left(endq);
269 }
270
271 QString wholefile;
272 if (charset == "ISO-8859-1") {
273 wholefile = preamble + tstr.readAll();
274 } else {
275 tstr.reset();
276 tstr.seek(0);
277 tstr.setCodec(CSTR(charset));
278 wholefile = tstr.readAll();
279 }
280
281 xml_readunicode(wholefile);
282 }
283
284 static void
google_read(void)285 google_read(void)
286 {
287 routehead = (route_head**)xmalloc(sizeof(route_head*));
288 routecount = (int*)xmalloc(sizeof(int));
289 goog_segroute = 0;
290 routehead[goog_segroute] = NULL;
291
292 goog_read_file();
293
294 xfree(routehead);
295 xfree(routecount);
296 routehead = NULL;
297 routecount = NULL;
298
299 if (encoded_points) {
300 xfree(encoded_points);
301 encoded_points = NULL;
302 }
303 if (encoded_levels) {
304 xfree(encoded_levels);
305 encoded_levels = NULL;
306 }
307 if (!script.isEmpty()) {
308 // TODO: rethink with Qt to make this less dependent on strchr...
309 char* s = xstrdup(script);
310 char* xml = strchr(s, '\'');
311 char* dict = strstr(s, "({");
312 char* end = NULL;
313
314 if (xml && (!dict || (xml < dict))) {
315 routehead = (route_head**)xmalloc(sizeof(route_head*));
316 routecount = (int*)xmalloc(sizeof(int));
317 goog_segroute = 0;
318 xml++;
319 end = strchr(xml+1, '\'');
320 if (end) {
321 *end = '\0';
322 xml_deinit();
323 xml_init(NULL, google_map, NULL);
324 xml_readstring(xml);
325 if (encoded_points) {
326 xfree(encoded_points);
327 encoded_points = NULL;
328 }
329 if (encoded_levels) {
330 xfree(encoded_levels);
331 encoded_levels = NULL;
332 }
333 }
334 } else if (dict) {
335 char qc = '\'';
336 int ofs = 9;
337 int panelofs = 8;
338 int count = 0;
339 char* tmp = NULL;
340 char* start = NULL;
341
342 char* panel = strstr(dict, "panel: '");
343 encoded_points = strstr(dict, "points: '");
344 encoded_levels = strstr(dict, "levels: '");
345 if (!encoded_points) {
346 ofs = 10;
347 qc = '"';
348 encoded_points = strstr(dict, "\"points\":\"");
349 encoded_levels = strstr(dict, "\"levels\":\"");
350 if (!encoded_points) {
351 encoded_points = strstr(dict, "points:\"");
352 encoded_levels = strstr(dict, "levels:\"");
353 ofs = 8;
354 }
355 }
356
357 if (!panel) {
358 panel = strstr(dict, "panel:\"");
359 panelofs = 7;
360 }
361 tmp=encoded_points;
362 while (tmp) {
363 if (qc == '"') {
364 char* tmp1 = strstr(tmp, "\"points\":\"");
365 if (!tmp1) {
366 tmp1 = strstr(tmp, "points:\"");
367 }
368 tmp = tmp1;
369 } else {
370 tmp = strstr(tmp, "points: '");
371 }
372 if (tmp) {
373 count++;
374 tmp++;
375 }
376 }
377 routehead = (route_head**)xmalloc(sizeof(route_head*)*count);
378 routecount = (int*)xmalloc(sizeof(int)*count);
379 goog_segroute = 0;
380
381 do {
382
383 if (encoded_points && encoded_levels) {
384 encoded_points += ofs;
385 encoded_levels += ofs;
386 end = strchr(encoded_points, qc);
387 if (end) {
388 *end = '\0';
389 end = encoded_points;
390 while ((end = strstr(end, "\\\\"))) {
391 memmove(end, end+1, strlen(end)+1);
392 end++;
393 }
394 end = strchr(encoded_levels, qc);
395 if (end) {
396 start = end;
397 *end = '\0';
398 end = encoded_levels;
399 while ((end = strstr(end, "\\\\"))) {
400 memmove(end, end+1, strlen(end)+1);
401 end++;
402 }
403 goog_poly_e(NULL, NULL);
404
405 goog_segroute++;
406 start++;
407 {
408 encoded_points = strstr(start, "points: '");
409 encoded_levels = strstr(start, "levels: '");
410 }
411 if (!encoded_points) {
412 encoded_points = strstr(start, "\"points\":\"");
413 encoded_levels = strstr(start, "\"levels\":\"");
414 }
415 if (!encoded_points) {
416 encoded_points = strstr(start, "points:\"");
417 encoded_levels = strstr(start, "levels:\"");
418 }
419 }
420 }
421 }
422 } while (start && encoded_points && encoded_levels);
423 if (panel) {
424 panel += panelofs;
425 end = strstr(panel, "/table><div class=\\\"legal");
426 if (!end) {
427 end = strstr(panel, "/table\\x3e\\x3cdiv class=\\\"legal");
428 }
429 if (!end) {
430 end = strstr(panel, "/table><div class=\\042legal");
431 }
432 if (!end) {
433 end = strstr(panel, "/table\\u003e\\u003cdiv id=\\\"mrDragRouteTip\\\"");
434 }
435 if (end) {
436 strcpy(end,"/table></div>");
437 }
438 if (!end) {
439 end = strstr(panel, "/div><div class=\\042legal");
440 if (end) {
441 strcpy(end, "/div></div>");
442 }
443 }
444 if (end) {
445 char* to = panel;
446 char* from = panel;
447 while (*from) {
448 if (!strncmp(from, "\\\"", 2)) {
449 *to++ = '"';
450 from += 2;
451 if (*(to-2) != '=') {
452 *to++ = ' ';
453 }
454 } else if (!strncmp(from, "\\042", 4)) {
455 *to++ = '"';
456 from += 4;
457
458 if (*(to-2) != '=') {
459 *to++ = ' ';
460 }
461 } else if (!strncmp(from, "\\u0026utm", 9)) {
462 strcpy(to, "&utm");
463 to += 8;
464 from += 9;
465 } else if (!strncmp(from, "\\u0026", 6)) {
466 *to++='&';
467 from += 6;
468 } else if (!strncmp(from, "\\u003c", 6)) {
469 *to++='<';
470 from += 6;
471 } else if (!strncmp(from, "\\u003e", 6)) {
472 *to++='>';
473 from += 6;
474 } else if (!strncmp(from, "\\x", 2)) {
475 unsigned int c;
476 sscanf(from+2, "%2x", &c);
477 *to++ = (char)c;
478 from += 4;
479 } else if (!strncmp(from, "\\'", 2)) {
480 *to++ = '\'';
481 from += 2;
482 } else if (!strncmp(from, " nowrap ", 8)) {
483 *to++ = ' ';
484 from += 8;
485 } else if (!strncmp(from, "tr style=\\\"display:none", 23)) {
486 if (strcmp(to-5, "/tr><")) {
487 /* broken 6-26-07 missing </tr> that apparently doesn't bother browsers */
488 strcpy(to, "/tr><");
489 to += 5;
490 }
491 *to++ = *from++;
492 } else {
493 *to++ = *from++;
494 }
495 }
496 *to = '\0';
497
498 #if 0
499 {
500 FILE* foo = fopen("foo.xml", "w");
501 fprintf(foo, "<!DOCTYPE foo [%s]>\n", xhtml_entities);
502 fwrite(panel, sizeof(char), strlen(panel), foo);
503 fclose(foo);
504 }
505 #endif
506
507 xml_deinit();
508 xml_init(NULL, google_map, NULL);
509 xml_readprefixstring("<!DOCTYPE foo [");
510 xml_readprefixstring(xhtml_entities);
511 xml_readprefixstring("]>");
512 xml_readstring(panel);
513 }
514 }
515 }
516 script.clear();
517 xfree(routehead);
518 xfree(routecount);
519 xfree(s);
520 }
521
522 /*
523 * 'Tis better to leak than crash when we are merging and
524 * don't see an 'end' in the first file. This feels a bit
525 * like plastering over a deeper problem...
526 *
527 */
528 if (encoded_points) {
529 encoded_points = NULL;
530 }
531 if (encoded_levels) {
532 encoded_levels = NULL;
533 }
534 }
535
536 static void
google_rd_deinit(void)537 google_rd_deinit(void)
538 {
539 xml_deinit();
540 mkshort_del_handle(&desc_handle);
541 rd_fname.clear();
542 }
543
544 ff_vecs_t google_vecs = {
545 ff_type_file,
546 { ff_cap_none, ff_cap_read, ff_cap_none},
547 google_rd_init,
548 NULL,
549 google_rd_deinit,
550 NULL,
551 google_read,
552 NULL,
553 NULL,
554 NULL,
555 CET_CHARSET_UTF8, 1 /* CET-REVIEW */
556 };
557