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, "&amp;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