You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

350 lines
8.3KB

  1. /*
  2. Read Netstumbler data files.
  3. Copyright (C) 2004, 2005 Robert Lipe, robertlipe+source@gpsbabel.org and
  4. John Temples; gpsns@xargs.com
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  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. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  16. */
  17. #include "defs.h"
  18. #include "cet_util.h"
  19. #include "csv_util.h"
  20. #include <ctype.h>
  21. #include <stdlib.h>
  22. #include <stdio.h>
  23. static gbfile* file_in;
  24. static char* nseicon = NULL;
  25. static char* nsneicon = NULL;
  26. static char* seicon = NULL;
  27. static char* sneicon = NULL;
  28. static char* snmac = NULL;
  29. static int macstumbler;
  30. static void fix_netstumbler_dupes(void);
  31. #define MYNAME "NETSTUMBLER"
  32. static
  33. arglist_t netstumbler_args[] = {
  34. {
  35. "nseicon", &nseicon, "Non-stealth encrypted icon name",
  36. "Red Square", ARGTYPE_STRING, ARG_NOMINMAX
  37. },
  38. {
  39. "nsneicon", &nsneicon, "Non-stealth non-encrypted icon name",
  40. "Green Square", ARGTYPE_STRING, ARG_NOMINMAX
  41. },
  42. {
  43. "seicon", &seicon, "Stealth encrypted icon name",
  44. "Red Diamond", ARGTYPE_STRING, ARG_NOMINMAX
  45. },
  46. {
  47. "sneicon", &sneicon, "Stealth non-encrypted icon name",
  48. "Green Diamond", ARGTYPE_STRING, ARG_NOMINMAX
  49. },
  50. {
  51. "snmac", &snmac, "Shortname is MAC address", NULL, ARGTYPE_BOOL,
  52. ARG_NOMINMAX
  53. },
  54. ARG_TERMINATOR
  55. };
  56. static void
  57. rd_init(const QString& fname)
  58. {
  59. file_in = gbfopen(fname, "rb", MYNAME);
  60. macstumbler = 0;
  61. }
  62. static void
  63. rd_deinit(void)
  64. {
  65. gbfclose(file_in);
  66. }
  67. static void
  68. data_read(void)
  69. {
  70. char* ibuf;
  71. char ssid[2 + 32 + 2 + 1]; /* "( " + SSID + " )" + null */
  72. char mac[2 + 17 + 2 + 1]; /* "( " + MAC + " )" + null */
  73. char desc[sizeof ssid - 1 + 15 + 1]; /* room for channel/speed */
  74. double lat = 0, lon = 0;
  75. Waypoint* wpt_tmp;
  76. int line_no = 0;
  77. int stealth_num = 0, whitespace_num = 0;
  78. long flags = 0;
  79. int speed = 0, channel = 0;
  80. struct tm tm;
  81. int line = 0;
  82. memset(&tm, 0, sizeof(tm));
  83. while ((ibuf = gbfgetstr(file_in))) {
  84. char* field;
  85. int field_num, len, i, stealth = 0;
  86. if ((line++ == 0) && file_in->unicode) {
  87. cet_convert_init(CET_CHARSET_UTF8, 1);
  88. }
  89. ibuf = lrtrim(ibuf);
  90. /* A sharp in column zero might be a comment. Or it might be
  91. * something useful, like the date.
  92. */
  93. if (ibuf[0] == '#') {
  94. if (strncmp(&ibuf[2], "$DateGMT:", 9) == 0) {
  95. tm.tm_year = atoi(&ibuf[12]) - 1900;
  96. tm.tm_mon = atoi(&ibuf[17]) - 1;
  97. tm.tm_mday = atoi(&ibuf[20]);
  98. }
  99. /*
  100. * Mac stumbler files are the same, except
  101. * use DDMM.mmm instad of DD.DDDD.
  102. */
  103. if (strstr(ibuf, "Creator: MacStumbler")) {
  104. macstumbler = 1;
  105. }
  106. continue;
  107. }
  108. field_num = 0;
  109. line_no++;
  110. field = csv_lineparse(ibuf, "\t", "", line_no);
  111. while (field) {
  112. switch (field_num) {
  113. case 0: /* lat */
  114. lat = atof(&field[2]);
  115. if (field[0] == 'S') {
  116. lat = -lat;
  117. }
  118. if (macstumbler) {
  119. lat = ddmm2degrees(lat);
  120. }
  121. break;
  122. case 1: /* long */
  123. lon = atof(&field[2]);
  124. if (field[0] == 'W') {
  125. lon = -lon;
  126. }
  127. if (macstumbler) {
  128. lon = ddmm2degrees(lon);
  129. }
  130. break;
  131. case 2: /* ( SSID ) */
  132. strcpy(ssid, &field[2]); /* zap "( " */
  133. len = strlen(ssid) - 2;
  134. ssid[len] = 0; /* zap " )" */
  135. stealth = (len == 0);
  136. /* don't alter SSID in snmac mode */
  137. if (!snmac) {
  138. int found = 0;
  139. /* check for all whitespace */
  140. for (i = 0; i < len && !found; i++) {
  141. if (!isspace(ssid[i])) {
  142. found = 1;
  143. }
  144. }
  145. if (!stealth && !found) {
  146. snprintf(ssid, sizeof ssid, "Whitespace/%d", ++whitespace_num);
  147. }
  148. }
  149. break;
  150. case 4: /* ( MAC address ) */
  151. strcpy(mac, &field[2]); /* zap "( " */
  152. mac[strlen(mac) - 2] = 0;/* zap " )" */
  153. break;
  154. case 5: /* time */
  155. tm.tm_hour = atoi(field);
  156. tm.tm_min = atoi(&field[3]);
  157. tm.tm_sec = atoi(&field[6]);
  158. break;
  159. case 8: /* flags */
  160. flags = strtol(field, NULL, 16);
  161. break;
  162. case 11: /* data rate */
  163. speed = atoi(field) / 10;
  164. break;
  165. case 12: /* last channel */
  166. channel = atoi(field);
  167. break;
  168. case 3: /* type */
  169. case 6: /* SNR/sig/noise */
  170. case 7: /* name */
  171. case 9: /* channel bits */
  172. case 10: /* beacon interval */
  173. default:
  174. break;
  175. }
  176. field_num++;
  177. field = csv_lineparse(NULL, "\t", "", line_no);
  178. }
  179. if (lat == 0 && lon == 0) { /* skip records with no GPS data */
  180. continue;
  181. }
  182. wpt_tmp = new Waypoint;
  183. if (stealth) {
  184. if (!snmac) {
  185. snprintf(ssid, sizeof ssid, "Stealth/%d", ++stealth_num);
  186. }
  187. if (flags & 0x0010) { /* encrypted? */
  188. wpt_tmp->icon_descr = seicon;
  189. } else {
  190. wpt_tmp->icon_descr = sneicon;
  191. }
  192. } else {
  193. if (flags & 0x0010) { /* encrypted? */
  194. wpt_tmp->icon_descr = nseicon;
  195. } else {
  196. wpt_tmp->icon_descr = nsneicon;
  197. }
  198. }
  199. if (snmac) {
  200. snprintf(desc, sizeof desc, "%s/%d Mbps/Ch %d", ssid, speed, channel);
  201. wpt_tmp->shortname = (mac);
  202. } else {
  203. snprintf(desc, sizeof desc, "%d Mbps/Ch %d/%s", speed, channel, mac);
  204. wpt_tmp->shortname = (ssid);
  205. }
  206. wpt_tmp->description = desc;
  207. wpt_tmp->longitude = lon;
  208. wpt_tmp->latitude = lat;
  209. wpt_tmp->SetCreationTime(mktime(&tm));
  210. waypt_add(wpt_tmp);
  211. }
  212. fix_netstumbler_dupes();
  213. }
  214. typedef struct {
  215. unsigned long crc;
  216. Waypoint* wpt;
  217. } htable_t;
  218. static
  219. int
  220. compare(const void* a, const void* b)
  221. {
  222. unsigned long crc_a = ((const htable_t*)a)->crc;
  223. unsigned long crc_b = ((const htable_t*)b)->crc;
  224. /* we can't just return crc_a - crc_b because the return type is
  225. * signed.
  226. * */
  227. if (crc_a < crc_b) {
  228. return -1;
  229. } else if (crc_a > crc_b) {
  230. return 1;
  231. } else {
  232. /* CRCs are equal; we need to subsort on the description (which
  233. * includes the MAC address) to guarantee the same ordering of
  234. * the output for any qsort() implementation. this is strictly
  235. * to make the testo script happy.
  236. * */
  237. Waypoint* wpt_a = ((const htable_t*)a)->wpt;
  238. Waypoint* wpt_b = ((const htable_t*)b)->wpt;
  239. return wpt_a->description.compare(wpt_b->description);
  240. }
  241. }
  242. /* netstumbler data will have a lot of duplicate shortnames if the SSID
  243. * is used as the shortname (the default). salvage the dupes by
  244. * synthesizing a (hopefully) unique shortname.
  245. * */
  246. static
  247. void
  248. fix_netstumbler_dupes(void)
  249. {
  250. int i, ct = waypt_count(), serial = 0;
  251. htable_t* htable, *bh;
  252. unsigned long last_crc;
  253. htable = (htable_t*) xmalloc(ct * sizeof *htable);
  254. bh = htable;
  255. i = 0;
  256. #if NEWQ
  257. // Why, oh, why is this format running over the entire waypoint list and
  258. // modifying it? This seems wrong.
  259. extern QList<Waypoint*> waypt_list;
  260. foreach(Waypoint* waypointp, waypt_list) {
  261. bh->wpt = waypointp;
  262. #else
  263. queue* elem, *tmp;
  264. extern queue waypt_head;
  265. QUEUE_FOR_EACH(&waypt_head, elem, tmp) {
  266. bh->wpt = (Waypoint*) elem;
  267. #endif
  268. QString snptr = bh->wpt->shortname;
  269. QString tmp_sn = snptr.toLower();
  270. bh->crc = get_crc32(CSTR(tmp_sn), tmp_sn.length());
  271. i ++;
  272. bh ++;
  273. }
  274. qsort(htable, ct, sizeof *htable, compare);
  275. last_crc = htable[0].crc + 1; /* force mismatch */
  276. for (i = 0, bh = htable; i < ct; i++, bh++) {
  277. if (last_crc == bh->crc) {
  278. bh->wpt->shortname += QString("/%1").arg(++serial);
  279. } else {
  280. last_crc = bh->crc;
  281. }
  282. }
  283. xfree(htable);
  284. }
  285. ff_vecs_t netstumbler_vecs = {
  286. ff_type_file,
  287. { ff_cap_read, ff_cap_none, ff_cap_none },
  288. rd_init,
  289. NULL,
  290. rd_deinit,
  291. NULL,
  292. data_read,
  293. NULL,
  294. NULL,
  295. netstumbler_args,
  296. CET_CHARSET_ASCII, 0 /* CET-REVIEW */
  297. };