metroflip_scene_navigo.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. #include "../metroflip_i.h"
  2. #include <datetime.h>
  3. #include <dolphin/dolphin.h>
  4. #include <locale/locale.h>
  5. #include "navigo.h"
  6. #include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
  7. #define TAG "Metroflip:Scene:Navigo"
  8. int eventSizes[] = {8, 24, 8, 8, 8, 8, 24, 16, 16, 8, 16, 16, 8, 16,
  9. 16, 8, 5, 240, 16, 8, 16, 16, 16, 16, 16, 5, 16, 5};
  10. int* get_bit_positions(const char* binary_string, int* count) {
  11. int length = strlen(binary_string);
  12. int* positions = malloc(length * sizeof(int));
  13. int pos_index = 0;
  14. for(int i = 0; i < length; i++) {
  15. if(binary_string[length - 1 - i] == '1') {
  16. positions[pos_index++] = i - 1;
  17. }
  18. }
  19. *count = pos_index;
  20. return positions;
  21. }
  22. int is_event_present(int* array, int size, int number) {
  23. for(int i = 0; i < size; i++) {
  24. if(array[i] == number) {
  25. return 1;
  26. }
  27. }
  28. return 0;
  29. }
  30. int check_events(int* array, int size, int number) {
  31. int total = 0;
  32. for(int i = 0; i < size; i++) {
  33. if(array[i] < number) {
  34. total += eventSizes[array[i]];
  35. }
  36. }
  37. return total + 53;
  38. }
  39. static NfcCommand metroflip_scene_navigo_poller_callback(NfcGenericEvent event, void* context) {
  40. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  41. NfcCommand next_command = NfcCommandContinue;
  42. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  43. Metroflip* app = context;
  44. FuriString* parsed_data = furi_string_alloc();
  45. Widget* widget = app->widget;
  46. furi_string_reset(app->text_box_store);
  47. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  48. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  49. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  50. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  51. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  52. if(stage == MetroflipPollerEventTypeStart) {
  53. nfc_device_set_data(
  54. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  55. Iso14443_4bError error;
  56. size_t response_length = 0;
  57. do {
  58. // Select app for contracts
  59. select_app[6] = 32;
  60. bit_buffer_reset(tx_buffer);
  61. bit_buffer_append_bytes(tx_buffer, select_app, sizeof(select_app));
  62. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  63. if(error != Iso14443_4bErrorNone) {
  64. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  65. stage = MetroflipPollerEventTypeFail;
  66. view_dispatcher_send_custom_event(
  67. app->view_dispatcher, MetroflipCustomEventPollerFail);
  68. break;
  69. }
  70. // Check the response after selecting app
  71. response_length = bit_buffer_get_size_bytes(rx_buffer);
  72. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  73. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  74. FURI_LOG_I(
  75. TAG,
  76. "Select profile app failed: %02x%02x",
  77. bit_buffer_get_byte(rx_buffer, response_length - 2),
  78. bit_buffer_get_byte(rx_buffer, response_length - 1));
  79. stage = MetroflipPollerEventTypeFail;
  80. view_dispatcher_send_custom_event(
  81. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  82. break;
  83. }
  84. // read file 1
  85. read_file[2] = 1;
  86. bit_buffer_reset(tx_buffer);
  87. bit_buffer_append_bytes(tx_buffer, read_file, sizeof(read_file));
  88. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  89. if(error != Iso14443_4bErrorNone) {
  90. FURI_LOG_I(TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  91. stage = MetroflipPollerEventTypeFail;
  92. view_dispatcher_send_custom_event(
  93. app->view_dispatcher, MetroflipCustomEventPollerFail);
  94. break;
  95. }
  96. // Check the response after reading the file
  97. response_length = bit_buffer_get_size_bytes(rx_buffer);
  98. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  99. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  100. FURI_LOG_I(
  101. TAG,
  102. "Read file failed: %02x%02x",
  103. bit_buffer_get_byte(rx_buffer, response_length - 2),
  104. bit_buffer_get_byte(rx_buffer, response_length - 1));
  105. stage = MetroflipPollerEventTypeFail;
  106. view_dispatcher_send_custom_event(
  107. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  108. break;
  109. }
  110. char bit_representation[response_length * 8 + 1];
  111. bit_representation[0] = '\0';
  112. for(size_t i = 0; i < response_length; i++) {
  113. char bits[9];
  114. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  115. byte_to_binary(byte, bits);
  116. strlcat(bit_representation, bits, sizeof(bit_representation));
  117. }
  118. bit_representation[response_length * 8] = '\0';
  119. int start = 55, end = 70;
  120. float decimal_value = bit_slice_to_dec(bit_representation, start, end);
  121. float balance = decimal_value / 100;
  122. furi_string_printf(parsed_data, "\e#Navigo:\n\n");
  123. furi_string_cat_printf(parsed_data, "\e#Contract 1:\n");
  124. furi_string_cat_printf(parsed_data, "Balance: %.2f EUR\n", (double)balance);
  125. start = 80, end = 93;
  126. decimal_value = bit_slice_to_dec(bit_representation, start, end);
  127. uint64_t start_date_timestamp = (decimal_value * 24 * 3600) + (float)epoch + 3600;
  128. DateTime start_dt = {0};
  129. datetime_timestamp_to_datetime(start_date_timestamp, &start_dt);
  130. furi_string_cat_printf(parsed_data, "\nStart Date:\n");
  131. locale_format_datetime_cat(parsed_data, &start_dt, false);
  132. furi_string_cat_printf(parsed_data, "\n");
  133. // Select app for environment
  134. select_app[6] = 1;
  135. bit_buffer_reset(tx_buffer);
  136. bit_buffer_append_bytes(tx_buffer, select_app, sizeof(select_app));
  137. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  138. if(error != Iso14443_4bErrorNone) {
  139. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  140. stage = MetroflipPollerEventTypeFail;
  141. view_dispatcher_send_custom_event(
  142. app->view_dispatcher, MetroflipCustomEventPollerFail);
  143. break;
  144. }
  145. // Check the response after selecting app
  146. response_length = bit_buffer_get_size_bytes(rx_buffer);
  147. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  148. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  149. FURI_LOG_I(
  150. TAG,
  151. "Select profile app failed: %02x%02x",
  152. bit_buffer_get_byte(rx_buffer, response_length - 2),
  153. bit_buffer_get_byte(rx_buffer, response_length - 1));
  154. stage = MetroflipPollerEventTypeFail;
  155. view_dispatcher_send_custom_event(
  156. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  157. break;
  158. }
  159. // read file 1
  160. read_file[2] = 1;
  161. bit_buffer_reset(tx_buffer);
  162. bit_buffer_append_bytes(tx_buffer, read_file, sizeof(read_file));
  163. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  164. if(error != Iso14443_4bErrorNone) {
  165. FURI_LOG_I(TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  166. stage = MetroflipPollerEventTypeFail;
  167. view_dispatcher_send_custom_event(
  168. app->view_dispatcher, MetroflipCustomEventPollerFail);
  169. break;
  170. }
  171. // Check the response after reading the file
  172. response_length = bit_buffer_get_size_bytes(rx_buffer);
  173. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  174. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  175. FURI_LOG_I(
  176. TAG,
  177. "Read file failed: %02x%02x",
  178. bit_buffer_get_byte(rx_buffer, response_length - 2),
  179. bit_buffer_get_byte(rx_buffer, response_length - 1));
  180. stage = MetroflipPollerEventTypeFail;
  181. view_dispatcher_send_custom_event(
  182. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  183. break;
  184. }
  185. char environment_bit_representation[response_length * 8 + 1];
  186. environment_bit_representation[0] = '\0';
  187. for(size_t i = 0; i < response_length; i++) {
  188. char bits[9];
  189. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  190. byte_to_binary(byte, bits);
  191. strlcat(
  192. environment_bit_representation,
  193. bits,
  194. sizeof(environment_bit_representation));
  195. }
  196. start = 45;
  197. end = 58;
  198. decimal_value = bit_slice_to_dec(environment_bit_representation, start, end);
  199. uint64_t end_validity_timestamp =
  200. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  201. DateTime end_dt = {0};
  202. datetime_timestamp_to_datetime(end_validity_timestamp, &end_dt);
  203. furi_string_cat_printf(parsed_data, "\nEnd Validity:\n");
  204. locale_format_datetime_cat(parsed_data, &end_dt, false);
  205. furi_string_cat_printf(parsed_data, "\n\n");
  206. // Select app for events
  207. select_app[6] = 16;
  208. bit_buffer_reset(tx_buffer);
  209. bit_buffer_append_bytes(tx_buffer, select_app, sizeof(select_app));
  210. error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  211. if(error != Iso14443_4bErrorNone) {
  212. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  213. stage = MetroflipPollerEventTypeFail;
  214. view_dispatcher_send_custom_event(
  215. app->view_dispatcher, MetroflipCustomEventPollerFail);
  216. break;
  217. }
  218. // Check the response after selecting app
  219. response_length = bit_buffer_get_size_bytes(rx_buffer);
  220. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  221. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  222. FURI_LOG_I(
  223. TAG,
  224. "Select events app failed: %02x%02x",
  225. bit_buffer_get_byte(rx_buffer, response_length - 2),
  226. bit_buffer_get_byte(rx_buffer, response_length - 1));
  227. stage = MetroflipPollerEventTypeFail;
  228. view_dispatcher_send_custom_event(
  229. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  230. break;
  231. }
  232. furi_string_cat_printf(parsed_data, "\e#Events:\n");
  233. // Now send the read command
  234. for(size_t i = 1; i < 4; i++) {
  235. read_file[2] = i;
  236. bit_buffer_reset(tx_buffer);
  237. bit_buffer_append_bytes(tx_buffer, read_file, sizeof(read_file));
  238. error =
  239. iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  240. if(error != Iso14443_4bErrorNone) {
  241. FURI_LOG_I(
  242. TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  243. stage = MetroflipPollerEventTypeFail;
  244. view_dispatcher_send_custom_event(
  245. app->view_dispatcher, MetroflipCustomEventPollerFail);
  246. break;
  247. }
  248. // Check the response after reading the file
  249. response_length = bit_buffer_get_size_bytes(rx_buffer);
  250. if(bit_buffer_get_byte(rx_buffer, response_length - 2) != apdu_success[0] ||
  251. bit_buffer_get_byte(rx_buffer, response_length - 1) != apdu_success[1]) {
  252. FURI_LOG_I(
  253. TAG,
  254. "Read file failed: %02x%02x",
  255. bit_buffer_get_byte(rx_buffer, response_length - 2),
  256. bit_buffer_get_byte(rx_buffer, response_length - 1));
  257. stage = MetroflipPollerEventTypeFail;
  258. view_dispatcher_send_custom_event(
  259. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  260. break;
  261. }
  262. char event_bit_representation[response_length * 8 + 1];
  263. event_bit_representation[0] = '\0';
  264. for(size_t i = 0; i < response_length; i++) {
  265. char bits[9];
  266. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  267. byte_to_binary(byte, bits);
  268. strlcat(event_bit_representation, bits, sizeof(event_bit_representation));
  269. }
  270. furi_string_cat_printf(parsed_data, "\nEvent 0%d:\n", i);
  271. int count = 0;
  272. int start = 25, end = 53;
  273. char bit_slice[end - start + 2];
  274. strncpy(bit_slice, event_bit_representation + start, end - start + 1);
  275. bit_slice[end - start + 1] = '\0';
  276. int* positions = get_bit_positions(bit_slice, &count);
  277. for(int i = 0; i < count; i++) {
  278. FURI_LOG_I(TAG, "%d ", positions[i]);
  279. }
  280. int event_number = 2;
  281. if(is_event_present(positions, count, event_number)) {
  282. int positionOffset = check_events(positions, count, event_number);
  283. int start = positionOffset, end = positionOffset + 7;
  284. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  285. int transport_type = decimal_value >> 4;
  286. int transition = decimal_value & 15;
  287. furi_string_cat_printf(
  288. parsed_data,
  289. "%s - %s\n",
  290. TRANSPORT_LIST[transport_type],
  291. TRANSITION_LIST[transition]);
  292. }
  293. event_number = 4;
  294. if(is_event_present(positions, count, event_number)) {
  295. int positionOffset = check_events(positions, count, event_number);
  296. start = positionOffset, end = positionOffset + 7;
  297. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  298. furi_string_cat_printf(
  299. parsed_data, "Provider: %s\n", SERVICE_PROVIDERS[decimal_value]);
  300. }
  301. event_number = 8;
  302. if(is_event_present(positions, count, event_number)) {
  303. int positionOffset = check_events(positions, count, event_number);
  304. start = positionOffset, end = positionOffset + 15;
  305. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  306. int line_id = decimal_value >> 9;
  307. int station_id = (decimal_value >> 4) & 31;
  308. furi_string_cat_printf(
  309. parsed_data,
  310. "Line: %s\nStation: %s\n",
  311. STATION_LIST[line_id][0],
  312. STATION_LIST[line_id][station_id]);
  313. }
  314. free(positions);
  315. start = 0, end = 13;
  316. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  317. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  318. DateTime dt = {0};
  319. datetime_timestamp_to_datetime(date_timestamp, &dt);
  320. furi_string_cat_printf(parsed_data, "Time: ");
  321. locale_format_datetime_cat(parsed_data, &dt, false);
  322. start = 14, end = 24;
  323. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  324. furi_string_cat_printf(
  325. parsed_data,
  326. " %02d:%02d:%02d\n\n",
  327. ((decimal_value * 60) / 3600),
  328. (((decimal_value * 60) % 3600) / 60),
  329. (((decimal_value * 60) % 3600) % 60));
  330. }
  331. UNUSED(TRANSITION_LIST);
  332. UNUSED(TRANSPORT_LIST);
  333. UNUSED(SERVICE_PROVIDERS);
  334. widget_add_text_scroll_element(
  335. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  336. widget_add_button_element(
  337. widget, GuiButtonTypeRight, "Exit", metroflip_exit_widget_callback, app);
  338. furi_string_free(parsed_data);
  339. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  340. metroflip_app_blink_stop(app);
  341. stage = MetroflipPollerEventTypeSuccess;
  342. next_command = NfcCommandStop;
  343. } while(false);
  344. if(stage != MetroflipPollerEventTypeSuccess) {
  345. next_command = NfcCommandStop;
  346. }
  347. }
  348. }
  349. bit_buffer_free(tx_buffer);
  350. bit_buffer_free(rx_buffer);
  351. return next_command;
  352. }
  353. void metroflip_scene_navigo_on_enter(void* context) {
  354. Metroflip* app = context;
  355. dolphin_deed(DolphinDeedNfcRead);
  356. // Setup view
  357. Popup* popup = app->popup;
  358. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  359. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  360. // Start worker
  361. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  362. nfc_scanner_alloc(app->nfc);
  363. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  364. nfc_poller_start(app->poller, metroflip_scene_navigo_poller_callback, app);
  365. metroflip_app_blink_start(app);
  366. }
  367. bool metroflip_scene_navigo_on_event(void* context, SceneManagerEvent event) {
  368. Metroflip* app = context;
  369. bool consumed = false;
  370. if(event.type == SceneManagerEventTypeCustom) {
  371. if(event.event == MetroflipPollerEventTypeCardDetect) {
  372. Popup* popup = app->popup;
  373. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  374. consumed = true;
  375. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  376. Popup* popup = app->popup;
  377. popup_set_header(popup, "Read Error,\n wrong card", 68, 30, AlignLeft, AlignTop);
  378. consumed = true;
  379. } else if(event.event == MetroflipCustomEventPollerFail) {
  380. Popup* popup = app->popup;
  381. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  382. consumed = true;
  383. }
  384. } else if(event.type == SceneManagerEventTypeBack) {
  385. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  386. consumed = true;
  387. }
  388. return consumed;
  389. }
  390. void metroflip_scene_navigo_on_exit(void* context) {
  391. Metroflip* app = context;
  392. if(app->poller) {
  393. nfc_poller_stop(app->poller);
  394. nfc_poller_free(app->poller);
  395. }
  396. metroflip_app_blink_stop(app);
  397. widget_reset(app->widget);
  398. // Clear view
  399. popup_reset(app->popup);
  400. }