metroflip_scene_calypso.c 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435
  1. #include "metroflip_scene_calypso.h"
  2. #include "../metroflip_i.h"
  3. #include <datetime.h>
  4. #include <dolphin/dolphin.h>
  5. #include <notification/notification_messages.h>
  6. #include <locale/locale.h>
  7. #include <nfc/protocols/iso14443_4b/iso14443_4b_poller.h>
  8. #define TAG "Metroflip:Scene:Calypso"
  9. int select_new_app(
  10. int new_app_directory,
  11. int new_app,
  12. BitBuffer* tx_buffer,
  13. BitBuffer* rx_buffer,
  14. Iso14443_4bPoller* iso14443_4b_poller,
  15. Metroflip* app,
  16. MetroflipPollerEventType* stage) {
  17. select_app[5] = new_app_directory;
  18. select_app[6] = new_app;
  19. bit_buffer_reset(tx_buffer);
  20. bit_buffer_append_bytes(tx_buffer, select_app, sizeof(select_app));
  21. FURI_LOG_D(
  22. TAG,
  23. "SEND %02x %02x %02x %02x %02x %02x %02x %02x",
  24. select_app[0],
  25. select_app[1],
  26. select_app[2],
  27. select_app[3],
  28. select_app[4],
  29. select_app[5],
  30. select_app[6],
  31. select_app[7]);
  32. int error = iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  33. if(error != Iso14443_4bErrorNone) {
  34. FURI_LOG_I(TAG, "Select File: iso14443_4b_poller_send_block error %d", error);
  35. *stage = MetroflipPollerEventTypeFail;
  36. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  37. return error;
  38. }
  39. return 0;
  40. }
  41. int read_new_file(
  42. int new_file,
  43. BitBuffer* tx_buffer,
  44. BitBuffer* rx_buffer,
  45. Iso14443_4bPoller* iso14443_4b_poller,
  46. Metroflip* app,
  47. MetroflipPollerEventType* stage) {
  48. read_file[2] = new_file;
  49. bit_buffer_reset(tx_buffer);
  50. bit_buffer_append_bytes(tx_buffer, read_file, sizeof(read_file));
  51. FURI_LOG_D(
  52. TAG,
  53. "SEND %02x %02x %02x %02x %02x",
  54. read_file[0],
  55. read_file[1],
  56. read_file[2],
  57. read_file[3],
  58. read_file[4]);
  59. Iso14443_4bError error =
  60. iso14443_4b_poller_send_block(iso14443_4b_poller, tx_buffer, rx_buffer);
  61. if(error != Iso14443_4bErrorNone) {
  62. FURI_LOG_I(TAG, "Read File: iso14443_4b_poller_send_block error %d", error);
  63. *stage = MetroflipPollerEventTypeFail;
  64. view_dispatcher_send_custom_event(app->view_dispatcher, MetroflipCustomEventPollerFail);
  65. return error;
  66. }
  67. return 0;
  68. }
  69. int check_response(
  70. BitBuffer* rx_buffer,
  71. Metroflip* app,
  72. MetroflipPollerEventType* stage,
  73. size_t* response_length) {
  74. *response_length = bit_buffer_get_size_bytes(rx_buffer);
  75. if(bit_buffer_get_byte(rx_buffer, *response_length - 2) != apdu_success[0] ||
  76. bit_buffer_get_byte(rx_buffer, *response_length - 1) != apdu_success[1]) {
  77. int error_code_1 = bit_buffer_get_byte(rx_buffer, *response_length - 2);
  78. int error_code_2 = bit_buffer_get_byte(rx_buffer, *response_length - 1);
  79. FURI_LOG_E(TAG, "Select profile app/file failed: %02x%02x", error_code_1, error_code_2);
  80. if(error_code_1 == 0x6a && error_code_2 == 0x82) {
  81. FURI_LOG_E(TAG, "Wrong parameter(s) P1-P2 - File not found");
  82. } else if(error_code_1 == 0x69 && error_code_2 == 0x82) {
  83. FURI_LOG_E(TAG, "Command not allowed - Security status not satisfied");
  84. }
  85. *stage = MetroflipPollerEventTypeFail;
  86. view_dispatcher_send_custom_event(
  87. app->view_dispatcher, MetroflipCustomEventPollerFileNotFound);
  88. return 1;
  89. }
  90. return 0;
  91. }
  92. void update_page_info(void* context, FuriString* parsed_data) {
  93. Metroflip* app = context;
  94. CalypsoContext* ctx = app->calypso_context;
  95. if(ctx->page_id == 0 || ctx->page_id == 1 || ctx->page_id == 2 || ctx->page_id == 3) {
  96. switch(ctx->card->card_type) {
  97. case CALYPSO_CARD_NAVIGO: {
  98. furi_string_cat_printf(
  99. parsed_data,
  100. "\e#%s %u:\n",
  101. get_navigo_type(ctx->card->navigo->holder.card_status),
  102. ctx->card->card_number);
  103. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  104. show_navigo_contract_info(&ctx->card->navigo->contracts[ctx->page_id], parsed_data);
  105. break;
  106. }
  107. case CALYPSO_CARD_OPUS: {
  108. furi_string_cat_printf(parsed_data, "\e#Opus %u:\n", ctx->card->card_number);
  109. furi_string_cat_printf(parsed_data, "\e#Contract %d:\n", ctx->page_id + 1);
  110. show_opus_contract_info(&ctx->card->opus->contracts[ctx->page_id], parsed_data);
  111. break;
  112. }
  113. default: {
  114. furi_string_cat_printf(parsed_data, "\e#Unknown %u:\n", ctx->card->card_number);
  115. break;
  116. }
  117. }
  118. } else if(ctx->page_id == 4) {
  119. furi_string_cat_printf(parsed_data, "\e#Environment:\n");
  120. switch(ctx->card->card_type) {
  121. case CALYPSO_CARD_NAVIGO: {
  122. show_navigo_environment_info(&ctx->card->navigo->environment, parsed_data);
  123. break;
  124. }
  125. case CALYPSO_CARD_OPUS: {
  126. show_opus_environment_info(&ctx->card->opus->environment, parsed_data);
  127. break;
  128. }
  129. default: {
  130. break;
  131. }
  132. }
  133. } else if(ctx->page_id == 5 || ctx->page_id == 6 || ctx->page_id == 7) {
  134. furi_string_cat_printf(parsed_data, "\e#Event %d:\n", ctx->page_id - 4);
  135. switch(ctx->card->card_type) {
  136. case CALYPSO_CARD_NAVIGO: {
  137. show_navigo_event_info(
  138. &ctx->card->navigo->events[ctx->page_id - 5],
  139. ctx->card->navigo->contracts,
  140. parsed_data);
  141. break;
  142. }
  143. case CALYPSO_CARD_OPUS: {
  144. show_opus_event_info(
  145. &ctx->card->opus->events[ctx->page_id - 5],
  146. ctx->card->opus->contracts,
  147. parsed_data);
  148. break;
  149. }
  150. default: {
  151. break;
  152. }
  153. }
  154. }
  155. }
  156. void update_widget_elements(void* context) {
  157. Metroflip* app = context;
  158. CalypsoContext* ctx = app->calypso_context;
  159. Widget* widget = app->widget;
  160. if(ctx->page_id < 7) {
  161. widget_add_button_element(
  162. widget, GuiButtonTypeRight, "Next", metroflip_next_button_widget_callback, context);
  163. } else {
  164. widget_add_button_element(
  165. widget, GuiButtonTypeRight, "Exit", metroflip_next_button_widget_callback, context);
  166. }
  167. if(ctx->page_id > 0) {
  168. widget_add_button_element(
  169. widget, GuiButtonTypeLeft, "Back", metroflip_back_button_widget_callback, context);
  170. }
  171. }
  172. void metroflip_back_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  173. Metroflip* app = context;
  174. CalypsoContext* ctx = app->calypso_context;
  175. UNUSED(result);
  176. Widget* widget = app->widget;
  177. if(type == InputTypePress) {
  178. widget_reset(widget);
  179. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id - 1);
  180. if(ctx->page_id > 0) {
  181. if(ctx->page_id == 4 && ctx->card->contracts_count < 4) {
  182. ctx->page_id -= 1;
  183. }
  184. if(ctx->page_id == 3 && ctx->card->contracts_count < 3) {
  185. ctx->page_id -= 1;
  186. }
  187. if(ctx->page_id == 2 && ctx->card->contracts_count < 2) {
  188. ctx->page_id -= 1;
  189. }
  190. ctx->page_id -= 1;
  191. }
  192. FuriString* parsed_data = furi_string_alloc();
  193. // Ensure no nested mutexes
  194. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  195. update_page_info(app, parsed_data);
  196. furi_mutex_release(ctx->mutex);
  197. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  198. // widget_add_icon_element(widget, 0, 0, &I_RFIDDolphinReceive_97x61);
  199. // Ensure no nested mutexes
  200. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  201. update_widget_elements(app);
  202. furi_mutex_release(ctx->mutex);
  203. furi_string_free(parsed_data);
  204. }
  205. }
  206. void metroflip_next_button_widget_callback(GuiButtonType result, InputType type, void* context) {
  207. Metroflip* app = context;
  208. CalypsoContext* ctx = app->calypso_context;
  209. UNUSED(result);
  210. Widget* widget = app->widget;
  211. if(type == InputTypePress) {
  212. widget_reset(widget);
  213. FURI_LOG_I(TAG, "Page ID: %d -> %d", ctx->page_id, ctx->page_id + 1);
  214. if(ctx->page_id < 7) {
  215. if(ctx->page_id == 0 && ctx->card->contracts_count < 2) {
  216. ctx->page_id += 1;
  217. }
  218. if(ctx->page_id == 1 && ctx->card->contracts_count < 3) {
  219. ctx->page_id += 1;
  220. }
  221. if(ctx->page_id == 2 && ctx->card->contracts_count < 4) {
  222. ctx->page_id += 1;
  223. }
  224. ctx->page_id += 1;
  225. } else {
  226. ctx->page_id = 0;
  227. scene_manager_search_and_switch_to_previous_scene(
  228. app->scene_manager, MetroflipSceneStart);
  229. return;
  230. }
  231. FuriString* parsed_data = furi_string_alloc();
  232. // Ensure no nested mutexes
  233. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  234. update_page_info(app, parsed_data);
  235. furi_mutex_release(ctx->mutex);
  236. widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  237. // Ensure no nested mutexes
  238. furi_mutex_acquire(ctx->mutex, FuriWaitForever);
  239. update_widget_elements(app);
  240. furi_mutex_release(ctx->mutex);
  241. furi_string_free(parsed_data);
  242. }
  243. }
  244. void delay(int milliseconds) {
  245. furi_thread_flags_wait(0, FuriFlagWaitAny, milliseconds);
  246. }
  247. static NfcCommand metroflip_scene_navigo_poller_callback(NfcGenericEvent event, void* context) {
  248. furi_assert(event.protocol == NfcProtocolIso14443_4b);
  249. NfcCommand next_command = NfcCommandContinue;
  250. MetroflipPollerEventType stage = MetroflipPollerEventTypeStart;
  251. Metroflip* app = context;
  252. FuriString* parsed_data = furi_string_alloc();
  253. Widget* widget = app->widget;
  254. furi_string_reset(app->text_box_store);
  255. const Iso14443_4bPollerEvent* iso14443_4b_event = event.event_data;
  256. Iso14443_4bPoller* iso14443_4b_poller = event.instance;
  257. BitBuffer* tx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  258. BitBuffer* rx_buffer = bit_buffer_alloc(Metroflip_POLLER_MAX_BUFFER_SIZE);
  259. if(iso14443_4b_event->type == Iso14443_4bPollerEventTypeReady) {
  260. if(stage == MetroflipPollerEventTypeStart) {
  261. // Start Flipper vibration
  262. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  263. notification_message(notification, &sequence_set_vibro_on);
  264. delay(50);
  265. notification_message(notification, &sequence_reset_vibro);
  266. nfc_device_set_data(
  267. app->nfc_device, NfcProtocolIso14443_4b, nfc_poller_get_data(app->poller));
  268. Iso14443_4bError error;
  269. size_t response_length = 0;
  270. do {
  271. // Initialize the card data
  272. CalypsoCardData* card = malloc(sizeof(CalypsoCardData));
  273. // Select app ICC
  274. error = select_new_app(
  275. 0x00, 0x02, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  276. if(error != 0) {
  277. break;
  278. }
  279. // Check the response after selecting app
  280. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  281. break;
  282. }
  283. // Now send the read command for ICC
  284. error = read_new_file(0x01, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  285. if(error != 0) {
  286. break;
  287. }
  288. // Check the response after reading the file
  289. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  290. break;
  291. }
  292. char icc_bit_representation[response_length * 8 + 1];
  293. icc_bit_representation[0] = '\0';
  294. for(size_t i = 0; i < response_length; i++) {
  295. char bits[9];
  296. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  297. byte_to_binary(byte, bits);
  298. strlcat(icc_bit_representation, bits, sizeof(icc_bit_representation));
  299. }
  300. icc_bit_representation[response_length * 8] = '\0';
  301. int start = 128, end = 159;
  302. card->card_number = bit_slice_to_dec(icc_bit_representation, start, end);
  303. // Select app for ticketing
  304. error = select_new_app(
  305. 0x20, 0x00, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  306. if(error != 0) {
  307. FURI_LOG_E(TAG, "Failed to select app for ticketing");
  308. break;
  309. }
  310. // Check the response after selecting app
  311. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  312. FURI_LOG_E(TAG, "Failed to check response after selecting app for ticketing");
  313. break;
  314. }
  315. // Select app for environment
  316. error = select_new_app(
  317. 0x20, 0x1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  318. if(error != 0) {
  319. break;
  320. }
  321. // Check the response after selecting app
  322. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  323. break;
  324. }
  325. // read file 1
  326. error = read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  327. if(error != 0) {
  328. break;
  329. }
  330. // Check the response after reading the file
  331. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  332. break;
  333. }
  334. char environment_bit_representation[response_length * 8 + 1];
  335. environment_bit_representation[0] = '\0';
  336. for(size_t i = 0; i < response_length; i++) {
  337. char bits[9];
  338. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  339. byte_to_binary(byte, bits);
  340. strlcat(
  341. environment_bit_representation,
  342. bits,
  343. sizeof(environment_bit_representation));
  344. }
  345. // FURI_LOG_I(
  346. // TAG, "Environment bit_representation: %s", environment_bit_representation);
  347. start = 13;
  348. end = 16;
  349. int country_num =
  350. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  351. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  352. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  353. start = 25;
  354. end = 28;
  355. int network_num =
  356. bit_slice_to_dec(environment_bit_representation, start, end) * 100 +
  357. bit_slice_to_dec(environment_bit_representation, start + 4, end + 4) * 10 +
  358. bit_slice_to_dec(environment_bit_representation, start + 8, end + 8);
  359. if(guess_card_type(country_num, network_num) != CALYPSO_CARD_UNKNOWN) {
  360. card->card_type = guess_card_type(country_num, network_num);
  361. }
  362. switch(card->card_type) {
  363. case CALYPSO_CARD_NAVIGO: {
  364. card->navigo = malloc(sizeof(NavigoCardData));
  365. card->navigo->environment.country_num = country_num;
  366. card->navigo->environment.network_num = network_num;
  367. CalypsoApp* IntercodeEnvHolderStructure = get_intercode_env_holder_structure();
  368. // EnvApplicationVersionNumber
  369. const char* env_key = "EnvApplicationVersionNumber";
  370. int positionOffset = get_calypso_node_offset(
  371. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  372. int start = positionOffset,
  373. end = positionOffset +
  374. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  375. card->navigo->environment.app_version =
  376. bit_slice_to_dec(environment_bit_representation, start, end);
  377. // EnvApplicationValidityEndDate
  378. env_key = "EnvApplicationValidityEndDate";
  379. positionOffset = get_calypso_node_offset(
  380. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  381. start = positionOffset,
  382. end = positionOffset +
  383. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  384. float decimal_value =
  385. bit_slice_to_dec(environment_bit_representation, start, end);
  386. uint64_t end_validity_timestamp =
  387. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  388. datetime_timestamp_to_datetime(
  389. end_validity_timestamp, &card->navigo->environment.end_dt);
  390. // HolderDataCardStatus
  391. env_key = "HolderDataCardStatus";
  392. positionOffset = get_calypso_node_offset(
  393. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  394. start = positionOffset,
  395. end = positionOffset +
  396. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  397. card->navigo->holder.card_status =
  398. bit_slice_to_dec(environment_bit_representation, start, end);
  399. // HolderDataCommercialID
  400. env_key = "HolderDataCommercialID";
  401. positionOffset = get_calypso_node_offset(
  402. environment_bit_representation, env_key, IntercodeEnvHolderStructure);
  403. start = positionOffset,
  404. end = positionOffset +
  405. get_calypso_node_size(env_key, IntercodeEnvHolderStructure) - 1;
  406. card->navigo->holder.commercial_id =
  407. bit_slice_to_dec(environment_bit_representation, start, end);
  408. // Select app for contracts
  409. error = select_new_app(
  410. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  411. if(error != 0) {
  412. FURI_LOG_E(TAG, "Failed to select app for contracts");
  413. break;
  414. }
  415. // Check the response after selecting app
  416. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  417. FURI_LOG_E(
  418. TAG, "Failed to check response after selecting app for contracts");
  419. break;
  420. }
  421. // Prepare calypso structure
  422. CalypsoApp* IntercodeContractStructure = get_intercode_contract_structure();
  423. if(!IntercodeContractStructure) {
  424. FURI_LOG_E(TAG, "Failed to load Intercode Contract structure");
  425. break;
  426. }
  427. // Now send the read command for contracts
  428. for(size_t i = 1; i < 5; i++) {
  429. error = read_new_file(
  430. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  431. if(error != 0) {
  432. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  433. break;
  434. }
  435. // Check the response after reading the file
  436. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  437. FURI_LOG_E(
  438. TAG, "Failed to check response after reading contract %d", i);
  439. break;
  440. }
  441. char bit_representation[response_length * 8 + 1];
  442. bit_representation[0] = '\0';
  443. for(size_t i = 0; i < response_length; i++) {
  444. char bits[9];
  445. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  446. byte_to_binary(byte, bits);
  447. strlcat(bit_representation, bits, sizeof(bit_representation));
  448. }
  449. bit_representation[response_length * 8] = '\0';
  450. if(bit_slice_to_dec(
  451. bit_representation,
  452. 0,
  453. IntercodeContractStructure->container->elements[0].bitmap->size -
  454. 1) == 0) {
  455. break;
  456. }
  457. card->navigo->contracts[i - 1].present = 1;
  458. card->contracts_count++;
  459. // 2. ContractTariff
  460. const char* contract_key = "ContractTariff";
  461. if(is_calypso_node_present(
  462. bit_representation, contract_key, IntercodeContractStructure)) {
  463. int positionOffset = get_calypso_node_offset(
  464. bit_representation, contract_key, IntercodeContractStructure);
  465. int start = positionOffset,
  466. end = positionOffset +
  467. get_calypso_node_size(
  468. contract_key, IntercodeContractStructure) -
  469. 1;
  470. card->navigo->contracts[i - 1].tariff =
  471. bit_slice_to_dec(bit_representation, start, end);
  472. }
  473. // 3. ContractSerialNumber
  474. contract_key = "ContractSerialNumber";
  475. if(is_calypso_node_present(
  476. bit_representation, contract_key, IntercodeContractStructure)) {
  477. int positionOffset = get_calypso_node_offset(
  478. bit_representation, contract_key, IntercodeContractStructure);
  479. int start = positionOffset,
  480. end = positionOffset +
  481. get_calypso_node_size(
  482. contract_key, IntercodeContractStructure) -
  483. 1;
  484. card->navigo->contracts[i - 1].serial_number =
  485. bit_slice_to_dec(bit_representation, start, end);
  486. card->navigo->contracts[i - 1].serial_number_available = true;
  487. }
  488. // 8. ContractPayMethod
  489. contract_key = "ContractPayMethod";
  490. if(is_calypso_node_present(
  491. bit_representation, contract_key, IntercodeContractStructure)) {
  492. int positionOffset = get_calypso_node_offset(
  493. bit_representation, contract_key, IntercodeContractStructure);
  494. int start = positionOffset,
  495. end = positionOffset +
  496. get_calypso_node_size(
  497. contract_key, IntercodeContractStructure) -
  498. 1;
  499. card->navigo->contracts[i - 1].pay_method =
  500. bit_slice_to_dec(bit_representation, start, end);
  501. card->navigo->contracts[i - 1].pay_method_available = true;
  502. }
  503. // 10. ContractPriceAmount
  504. contract_key = "ContractPriceAmount";
  505. if(is_calypso_node_present(
  506. bit_representation, contract_key, IntercodeContractStructure)) {
  507. int positionOffset = get_calypso_node_offset(
  508. bit_representation, contract_key, IntercodeContractStructure);
  509. int start = positionOffset,
  510. end = positionOffset +
  511. get_calypso_node_size(
  512. contract_key, IntercodeContractStructure) -
  513. 1;
  514. card->navigo->contracts[i - 1].price_amount =
  515. bit_slice_to_dec(bit_representation, start, end) / 100.0;
  516. card->navigo->contracts[i - 1].price_amount_available = true;
  517. }
  518. // 13.0. ContractValidityStartDate
  519. contract_key = "ContractValidityStartDate";
  520. if(is_calypso_node_present(
  521. bit_representation, contract_key, IntercodeContractStructure)) {
  522. int positionOffset = get_calypso_node_offset(
  523. bit_representation, contract_key, IntercodeContractStructure);
  524. int start = positionOffset,
  525. end = positionOffset +
  526. get_calypso_node_size(
  527. contract_key, IntercodeContractStructure) -
  528. 1;
  529. float decimal_value =
  530. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  531. uint64_t start_validity_timestamp =
  532. (decimal_value + (float)epoch) + 3600;
  533. datetime_timestamp_to_datetime(
  534. start_validity_timestamp,
  535. &card->navigo->contracts[i - 1].start_date);
  536. }
  537. // 13.2. ContractValidityEndDate
  538. contract_key = "ContractValidityEndDate";
  539. if(is_calypso_node_present(
  540. bit_representation, contract_key, IntercodeContractStructure)) {
  541. int positionOffset = get_calypso_node_offset(
  542. bit_representation, contract_key, IntercodeContractStructure);
  543. int start = positionOffset,
  544. end = positionOffset +
  545. get_calypso_node_size(
  546. contract_key, IntercodeContractStructure) -
  547. 1;
  548. float decimal_value =
  549. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  550. uint64_t end_validity_timestamp =
  551. (decimal_value + (float)epoch) + 3600;
  552. datetime_timestamp_to_datetime(
  553. end_validity_timestamp, &card->navigo->contracts[i - 1].end_date);
  554. card->navigo->contracts[i - 1].end_date_available = true;
  555. }
  556. // 13.6. ContractValidityZones
  557. contract_key = "ContractValidityZones";
  558. if(is_calypso_node_present(
  559. bit_representation, contract_key, IntercodeContractStructure)) {
  560. int start = get_calypso_node_offset(
  561. bit_representation, contract_key, IntercodeContractStructure);
  562. // binary form is 00011111 for zones 5, 4, 3, 2, 1
  563. for(int j = 0; j < 5; j++) {
  564. card->navigo->contracts[i - 1].zones[j] = bit_slice_to_dec(
  565. bit_representation, start + 3 + j, start + 3 + j);
  566. }
  567. card->navigo->contracts[i - 1].zones_available = true;
  568. }
  569. // 13.7. ContractValidityJourneys -- pas sûr de le mettre lui
  570. // 15.0. ContractValiditySaleDate
  571. contract_key = "ContractValiditySaleDate";
  572. if(is_calypso_node_present(
  573. bit_representation, contract_key, IntercodeContractStructure)) {
  574. int positionOffset = get_calypso_node_offset(
  575. bit_representation, contract_key, IntercodeContractStructure);
  576. int start = positionOffset,
  577. end = positionOffset +
  578. get_calypso_node_size(
  579. contract_key, IntercodeContractStructure) -
  580. 1;
  581. float decimal_value =
  582. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  583. uint64_t sale_timestamp = (decimal_value + (float)epoch) + 3600;
  584. datetime_timestamp_to_datetime(
  585. sale_timestamp, &card->navigo->contracts[i - 1].sale_date);
  586. }
  587. // 15.2. ContractValiditySaleAgent - FIX NEEDED
  588. contract_key = "ContractValiditySaleAgent";
  589. /* if(is_calypso_node_present(
  590. bit_representation, contract_key, NavigoContractStructure)) { */
  591. int positionOffset = get_calypso_node_offset(
  592. bit_representation, contract_key, IntercodeContractStructure);
  593. int start = positionOffset,
  594. end = positionOffset +
  595. get_calypso_node_size(contract_key, IntercodeContractStructure) -
  596. 1;
  597. card->navigo->contracts[i - 1].sale_agent =
  598. bit_slice_to_dec(bit_representation, start, end);
  599. // }
  600. // 15.3. ContractValiditySaleDevice
  601. contract_key = "ContractValiditySaleDevice";
  602. if(is_calypso_node_present(
  603. bit_representation, contract_key, IntercodeContractStructure)) {
  604. int positionOffset = get_calypso_node_offset(
  605. bit_representation, contract_key, IntercodeContractStructure);
  606. int start = positionOffset,
  607. end = positionOffset +
  608. get_calypso_node_size(
  609. contract_key, IntercodeContractStructure) -
  610. 1;
  611. card->navigo->contracts[i - 1].sale_device =
  612. bit_slice_to_dec(bit_representation, start, end);
  613. }
  614. // 16. ContractStatus -- 0x1 ou 0xff
  615. contract_key = "ContractStatus";
  616. if(is_calypso_node_present(
  617. bit_representation, contract_key, IntercodeContractStructure)) {
  618. int positionOffset = get_calypso_node_offset(
  619. bit_representation, contract_key, IntercodeContractStructure);
  620. int start = positionOffset,
  621. end = positionOffset +
  622. get_calypso_node_size(
  623. contract_key, IntercodeContractStructure) -
  624. 1;
  625. card->navigo->contracts[i - 1].status =
  626. bit_slice_to_dec(bit_representation, start, end);
  627. }
  628. // 18. ContractAuthenticator
  629. contract_key = "ContractAuthenticator";
  630. if(is_calypso_node_present(
  631. bit_representation, contract_key, IntercodeContractStructure)) {
  632. int positionOffset = get_calypso_node_offset(
  633. bit_representation, contract_key, IntercodeContractStructure);
  634. int start = positionOffset,
  635. end = positionOffset +
  636. get_calypso_node_size(
  637. contract_key, IntercodeContractStructure) -
  638. 1;
  639. card->navigo->contracts[i - 1].authenticator =
  640. bit_slice_to_dec(bit_representation, start, end);
  641. }
  642. }
  643. // Free the calypso structure
  644. free_calypso_structure(IntercodeContractStructure);
  645. // Select app for counters (remaining tickets on Navigo Easy)
  646. error = select_new_app(
  647. 0x20, 0x69, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  648. if(error != 0) {
  649. break;
  650. }
  651. // Check the response after selecting app
  652. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  653. break;
  654. }
  655. // read file 1
  656. error =
  657. read_new_file(1, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  658. if(error != 0) {
  659. break;
  660. }
  661. // Check the response after reading the file
  662. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  663. break;
  664. }
  665. char counter_bit_representation[response_length * 8 + 1];
  666. counter_bit_representation[0] = '\0';
  667. for(size_t i = 0; i < response_length; i++) {
  668. char bits[9];
  669. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  670. byte_to_binary(byte, bits);
  671. strlcat(
  672. counter_bit_representation, bits, sizeof(counter_bit_representation));
  673. }
  674. // FURI_LOG_I(TAG, "Counter bit_representation: %s", counter_bit_representation);
  675. // Ticket counts (contracts 1-4)
  676. for(int i = 0; i < 4; i++) {
  677. start = 0;
  678. end = 5;
  679. card->navigo->contracts[i].counter.count = bit_slice_to_dec(
  680. counter_bit_representation, 24 * i + start, 24 * i + end);
  681. start = 6;
  682. end = 23;
  683. card->navigo->contracts[i].counter.relative_first_stamp_15mn =
  684. bit_slice_to_dec(
  685. counter_bit_representation, 24 * i + start, 24 * i + end);
  686. }
  687. // Select app for events
  688. error = select_new_app(
  689. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  690. if(error != 0) {
  691. break;
  692. }
  693. // Check the response after selecting app
  694. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  695. break;
  696. }
  697. // Load the calypso structure for events
  698. CalypsoApp* IntercodeEventStructure = get_intercode_event_structure();
  699. if(!IntercodeEventStructure) {
  700. FURI_LOG_E(TAG, "Failed to load Intercode Event structure");
  701. break;
  702. }
  703. // furi_string_cat_printf(parsed_data, "\e#Events :\n");
  704. // Now send the read command for events
  705. for(size_t i = 1; i < 4; i++) {
  706. error = read_new_file(
  707. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  708. if(error != 0) {
  709. break;
  710. }
  711. // Check the response after reading the file
  712. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  713. break;
  714. }
  715. char event_bit_representation[response_length * 8 + 1];
  716. event_bit_representation[0] = '\0';
  717. for(size_t i = 0; i < response_length; i++) {
  718. char bits[9];
  719. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  720. byte_to_binary(byte, bits);
  721. strlcat(
  722. event_bit_representation, bits, sizeof(event_bit_representation));
  723. }
  724. // furi_string_cat_printf(parsed_data, "Event 0%d :\n", i);
  725. /* int count = 0;
  726. int start = 25, end = 52;
  727. char bit_slice[end - start + 2];
  728. strncpy(bit_slice, event_bit_representation + start, end - start + 1);
  729. bit_slice[end - start + 1] = '\0';
  730. int* positions = get_bit_positions(bit_slice, &count);
  731. FURI_LOG_I(TAG, "Positions: ");
  732. for(int i = 0; i < count; i++) {
  733. FURI_LOG_I(TAG, "%d ", positions[i]);
  734. } */
  735. // 2. EventCode
  736. const char* event_key = "EventCode";
  737. if(is_calypso_node_present(
  738. event_bit_representation, event_key, IntercodeEventStructure)) {
  739. int positionOffset = get_calypso_node_offset(
  740. event_bit_representation, event_key, IntercodeEventStructure);
  741. int start = positionOffset,
  742. end = positionOffset +
  743. get_calypso_node_size(event_key, IntercodeEventStructure) -
  744. 1;
  745. int decimal_value =
  746. bit_slice_to_dec(event_bit_representation, start, end);
  747. card->navigo->events[i - 1].transport_type = decimal_value >> 4;
  748. card->navigo->events[i - 1].transition = decimal_value & 15;
  749. }
  750. // 4. EventServiceProvider
  751. event_key = "EventServiceProvider";
  752. if(is_calypso_node_present(
  753. event_bit_representation, event_key, IntercodeEventStructure)) {
  754. int positionOffset = get_calypso_node_offset(
  755. event_bit_representation, event_key, IntercodeEventStructure);
  756. start = positionOffset,
  757. end = positionOffset +
  758. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  759. card->navigo->events[i - 1].service_provider =
  760. bit_slice_to_dec(event_bit_representation, start, end);
  761. }
  762. // 8. EventLocationId
  763. event_key = "EventLocationId";
  764. if(is_calypso_node_present(
  765. event_bit_representation, event_key, IntercodeEventStructure)) {
  766. int positionOffset = get_calypso_node_offset(
  767. event_bit_representation, event_key, IntercodeEventStructure);
  768. start = positionOffset,
  769. end = positionOffset +
  770. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  771. int decimal_value =
  772. bit_slice_to_dec(event_bit_representation, start, end);
  773. card->navigo->events[i - 1].station_group_id = decimal_value >> 9;
  774. card->navigo->events[i - 1].station_id = (decimal_value >> 4) & 31;
  775. }
  776. // 9. EventLocationGate
  777. event_key = "EventLocationGate";
  778. if(is_calypso_node_present(
  779. event_bit_representation, event_key, IntercodeEventStructure)) {
  780. int positionOffset = get_calypso_node_offset(
  781. event_bit_representation, event_key, IntercodeEventStructure);
  782. start = positionOffset,
  783. end = positionOffset +
  784. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  785. card->navigo->events[i - 1].location_gate =
  786. bit_slice_to_dec(event_bit_representation, start, end);
  787. card->navigo->events[i - 1].location_gate_available = true;
  788. }
  789. // 10. EventDevice
  790. event_key = "EventDevice";
  791. if(is_calypso_node_present(
  792. event_bit_representation, event_key, IntercodeEventStructure)) {
  793. int positionOffset = get_calypso_node_offset(
  794. event_bit_representation, event_key, IntercodeEventStructure);
  795. start = positionOffset,
  796. end = positionOffset +
  797. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  798. int decimal_value =
  799. bit_slice_to_dec(event_bit_representation, start, end);
  800. card->navigo->events[i - 1].device = decimal_value;
  801. int bus_device = decimal_value >> 8;
  802. card->navigo->events[i - 1].door = bus_device / 2 + 1;
  803. card->navigo->events[i - 1].side = bus_device % 2;
  804. card->navigo->events[i - 1].device_available = true;
  805. }
  806. // 11. EventRouteNumber
  807. event_key = "EventRouteNumber";
  808. if(is_calypso_node_present(
  809. event_bit_representation, event_key, IntercodeEventStructure)) {
  810. int positionOffset = get_calypso_node_offset(
  811. event_bit_representation, event_key, IntercodeEventStructure);
  812. start = positionOffset,
  813. end = positionOffset +
  814. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  815. card->navigo->events[i - 1].route_number =
  816. bit_slice_to_dec(event_bit_representation, start, end);
  817. card->navigo->events[i - 1].route_number_available = true;
  818. }
  819. // 13. EventJourneyRun
  820. event_key = "EventJourneyRun";
  821. if(is_calypso_node_present(
  822. event_bit_representation, event_key, IntercodeEventStructure)) {
  823. int positionOffset = get_calypso_node_offset(
  824. event_bit_representation, event_key, IntercodeEventStructure);
  825. start = positionOffset,
  826. end = positionOffset +
  827. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  828. card->navigo->events[i - 1].mission =
  829. bit_slice_to_dec(event_bit_representation, start, end);
  830. card->navigo->events[i - 1].mission_available = true;
  831. }
  832. // 14. EventVehicleId
  833. event_key = "EventVehicleId";
  834. if(is_calypso_node_present(
  835. event_bit_representation, event_key, IntercodeEventStructure)) {
  836. int positionOffset = get_calypso_node_offset(
  837. event_bit_representation, event_key, IntercodeEventStructure);
  838. start = positionOffset,
  839. end = positionOffset +
  840. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  841. card->navigo->events[i - 1].vehicle_id =
  842. bit_slice_to_dec(event_bit_representation, start, end);
  843. card->navigo->events[i - 1].vehicle_id_available = true;
  844. }
  845. // 25. EventContractPointer
  846. event_key = "EventContractPointer";
  847. if(is_calypso_node_present(
  848. event_bit_representation, event_key, IntercodeEventStructure)) {
  849. int positionOffset = get_calypso_node_offset(
  850. event_bit_representation, event_key, IntercodeEventStructure);
  851. start = positionOffset,
  852. end = positionOffset +
  853. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  854. card->navigo->events[i - 1].used_contract =
  855. bit_slice_to_dec(event_bit_representation, start, end);
  856. card->navigo->events[i - 1].used_contract_available = true;
  857. }
  858. // EventDateStamp
  859. event_key = "EventDateStamp";
  860. int positionOffset = get_calypso_node_offset(
  861. event_bit_representation, event_key, IntercodeEventStructure);
  862. start = positionOffset,
  863. end = positionOffset +
  864. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  865. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  866. uint64_t date_timestamp = (decimal_value * 24 * 3600) + epoch + 3600;
  867. datetime_timestamp_to_datetime(
  868. date_timestamp, &card->navigo->events[i - 1].date);
  869. // EventTimeStamp
  870. event_key = "EventTimeStamp";
  871. positionOffset = get_calypso_node_offset(
  872. event_bit_representation, event_key, IntercodeEventStructure);
  873. start = positionOffset,
  874. end = positionOffset +
  875. get_calypso_node_size(event_key, IntercodeEventStructure) - 1;
  876. decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  877. card->navigo->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  878. card->navigo->events[i - 1].date.minute =
  879. ((decimal_value * 60) % 3600) / 60;
  880. card->navigo->events[i - 1].date.second =
  881. ((decimal_value * 60) % 3600) % 60;
  882. }
  883. // Free the calypso structure
  884. free_calypso_structure(IntercodeEventStructure);
  885. break;
  886. }
  887. case CALYPSO_CARD_OPUS: {
  888. card->opus = malloc(sizeof(OpusCardData));
  889. card->opus->environment.country_num = country_num;
  890. card->opus->environment.network_num = network_num;
  891. CalypsoApp* OpusEnvHolderStructure = get_opus_env_holder_structure();
  892. // EnvApplicationVersionNumber
  893. const char* env_key = "EnvApplicationVersionNumber";
  894. int positionOffset = get_calypso_node_offset(
  895. environment_bit_representation, env_key, OpusEnvHolderStructure);
  896. int start = positionOffset,
  897. end = positionOffset +
  898. get_calypso_node_size(env_key, OpusEnvHolderStructure) - 1;
  899. card->opus->environment.app_version =
  900. bit_slice_to_dec(environment_bit_representation, start, end);
  901. // EnvApplicationValidityEndDate
  902. env_key = "EnvApplicationValidityEndDate";
  903. positionOffset = get_calypso_node_offset(
  904. environment_bit_representation, env_key, OpusEnvHolderStructure);
  905. start = positionOffset,
  906. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  907. 1;
  908. float decimal_value =
  909. bit_slice_to_dec(environment_bit_representation, start, end);
  910. uint64_t end_validity_timestamp =
  911. (decimal_value * 24 * 3600) + (float)epoch + 3600;
  912. datetime_timestamp_to_datetime(
  913. end_validity_timestamp, &card->opus->environment.end_dt);
  914. // HolderDataCardStatus
  915. env_key = "HolderDataCardStatus";
  916. positionOffset = get_calypso_node_offset(
  917. environment_bit_representation, env_key, OpusEnvHolderStructure);
  918. start = positionOffset,
  919. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  920. 1;
  921. card->opus->holder.card_status =
  922. bit_slice_to_dec(environment_bit_representation, start, end);
  923. // HolderDataCommercialID
  924. env_key = "HolderDataCommercialID";
  925. positionOffset = get_calypso_node_offset(
  926. environment_bit_representation, env_key, OpusEnvHolderStructure);
  927. start = positionOffset,
  928. end = positionOffset + get_calypso_node_size(env_key, OpusEnvHolderStructure) -
  929. 1;
  930. card->opus->holder.commercial_id =
  931. bit_slice_to_dec(environment_bit_representation, start, end);
  932. // Select app for contracts
  933. error = select_new_app(
  934. 0x20, 0x20, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  935. if(error != 0) {
  936. FURI_LOG_E(TAG, "Failed to select app for contracts");
  937. break;
  938. }
  939. // Check the response after selecting app
  940. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  941. FURI_LOG_E(
  942. TAG, "Failed to check response after selecting app for contracts");
  943. break;
  944. }
  945. // Prepare calypso structure
  946. CalypsoApp* OpusContractStructure = get_opus_contract_structure();
  947. if(!OpusContractStructure) {
  948. FURI_LOG_E(TAG, "Failed to load Opus Contract structure");
  949. break;
  950. }
  951. // Now send the read command for contracts
  952. for(size_t i = 1; i < 5; i++) {
  953. error = read_new_file(
  954. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  955. if(error != 0) {
  956. FURI_LOG_E(TAG, "Failed to read contract %d", i);
  957. break;
  958. }
  959. // Check the response after reading the file
  960. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  961. FURI_LOG_E(
  962. TAG, "Failed to check response after reading contract %d", i);
  963. break;
  964. }
  965. char bit_representation[response_length * 8 + 1];
  966. bit_representation[0] = '\0';
  967. for(size_t i = 0; i < response_length; i++) {
  968. char bits[9];
  969. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  970. byte_to_binary(byte, bits);
  971. strlcat(bit_representation, bits, sizeof(bit_representation));
  972. }
  973. bit_representation[response_length * 8] = '\0';
  974. if(bit_slice_to_dec(
  975. bit_representation,
  976. 0,
  977. OpusContractStructure->container->elements[1].bitmap->size - 1) ==
  978. 0) {
  979. break;
  980. }
  981. card->opus->contracts[i - 1].present = 1;
  982. card->contracts_count++;
  983. // ContractProvider
  984. const char* contract_key = "ContractProvider";
  985. if(is_calypso_node_present(
  986. bit_representation, contract_key, OpusContractStructure)) {
  987. int positionOffset = get_calypso_node_offset(
  988. bit_representation, contract_key, OpusContractStructure);
  989. int start = positionOffset,
  990. end = positionOffset +
  991. get_calypso_node_size(contract_key, OpusContractStructure) -
  992. 1;
  993. card->opus->contracts[i - 1].provider =
  994. bit_slice_to_dec(bit_representation, start, end);
  995. }
  996. // ContractTariff
  997. contract_key = "ContractTariff";
  998. if(is_calypso_node_present(
  999. bit_representation, contract_key, OpusContractStructure)) {
  1000. int positionOffset = get_calypso_node_offset(
  1001. bit_representation, contract_key, OpusContractStructure);
  1002. int start = positionOffset,
  1003. end = positionOffset +
  1004. get_calypso_node_size(contract_key, OpusContractStructure) -
  1005. 1;
  1006. card->opus->contracts[i - 1].tariff =
  1007. bit_slice_to_dec(bit_representation, start, end);
  1008. }
  1009. // ContractStartDate
  1010. contract_key = "ContractStartDate";
  1011. if(is_calypso_node_present(
  1012. bit_representation, contract_key, OpusContractStructure)) {
  1013. int positionOffset = get_calypso_node_offset(
  1014. bit_representation, contract_key, OpusContractStructure);
  1015. int start = positionOffset,
  1016. end = positionOffset +
  1017. get_calypso_node_size(contract_key, OpusContractStructure) -
  1018. 1;
  1019. float decimal_value =
  1020. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1021. uint64_t start_validity_timestamp =
  1022. (decimal_value + (float)epoch) + 3600;
  1023. datetime_timestamp_to_datetime(
  1024. start_validity_timestamp,
  1025. &card->opus->contracts[i - 1].start_date);
  1026. }
  1027. // ContractEndDate
  1028. contract_key = "ContractEndDate";
  1029. if(is_calypso_node_present(
  1030. bit_representation, contract_key, OpusContractStructure)) {
  1031. int positionOffset = get_calypso_node_offset(
  1032. bit_representation, contract_key, OpusContractStructure);
  1033. int start = positionOffset,
  1034. end = positionOffset +
  1035. get_calypso_node_size(contract_key, OpusContractStructure) -
  1036. 1;
  1037. float decimal_value =
  1038. bit_slice_to_dec(bit_representation, start, end) * 24 * 3600;
  1039. uint64_t end_validity_timestamp =
  1040. (decimal_value + (float)epoch) + 3600;
  1041. datetime_timestamp_to_datetime(
  1042. end_validity_timestamp, &card->opus->contracts[i - 1].end_date);
  1043. }
  1044. // ContractStatus
  1045. contract_key = "ContractStatus";
  1046. if(is_calypso_node_present(
  1047. bit_representation, contract_key, OpusContractStructure)) {
  1048. int positionOffset = get_calypso_node_offset(
  1049. bit_representation, contract_key, OpusContractStructure);
  1050. int start = positionOffset,
  1051. end = positionOffset +
  1052. get_calypso_node_size(contract_key, OpusContractStructure) -
  1053. 1;
  1054. card->opus->contracts[i - 1].status =
  1055. bit_slice_to_dec(bit_representation, start, end);
  1056. }
  1057. // ContractSaleDate + ContractSaleTime
  1058. contract_key = "ContractSaleDate";
  1059. int positionOffset = get_calypso_node_offset(
  1060. bit_representation, contract_key, OpusContractStructure);
  1061. int start = positionOffset,
  1062. end = positionOffset +
  1063. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1064. uint64_t sale_date_timestamp =
  1065. (bit_slice_to_dec(bit_representation, start, end) + (float)epoch) +
  1066. 3600;
  1067. datetime_timestamp_to_datetime(
  1068. sale_date_timestamp, &card->opus->contracts[i - 1].sale_date);
  1069. contract_key = "ContractSaleTime";
  1070. positionOffset = get_calypso_node_offset(
  1071. bit_representation, contract_key, OpusContractStructure);
  1072. start = positionOffset,
  1073. end = positionOffset +
  1074. get_calypso_node_size(contract_key, OpusContractStructure) - 1;
  1075. int decimal_value = bit_slice_to_dec(bit_representation, start, end);
  1076. card->opus->contracts[i - 1].sale_date.hour = (decimal_value * 60) / 3600;
  1077. card->opus->contracts[i - 1].sale_date.minute =
  1078. ((decimal_value * 60) % 3600) / 60;
  1079. card->opus->contracts[i - 1].sale_date.second =
  1080. ((decimal_value * 60) % 3600) % 60;
  1081. }
  1082. // Free the calypso structure
  1083. free_calypso_structure(OpusContractStructure);
  1084. // Select app for events
  1085. error = select_new_app(
  1086. 0x20, 0x10, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1087. if(error != 0) {
  1088. break;
  1089. }
  1090. // Check the response after selecting app
  1091. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1092. break;
  1093. }
  1094. // Load the calypso structure for events
  1095. CalypsoApp* OpusEventStructure = get_opus_event_structure();
  1096. if(!OpusEventStructure) {
  1097. FURI_LOG_E(TAG, "Failed to load Opus Event structure");
  1098. break;
  1099. }
  1100. // Now send the read command for events
  1101. for(size_t i = 1; i < 4; i++) {
  1102. error = read_new_file(
  1103. i, tx_buffer, rx_buffer, iso14443_4b_poller, app, &stage);
  1104. if(error != 0) {
  1105. break;
  1106. }
  1107. // Check the response after reading the file
  1108. if(check_response(rx_buffer, app, &stage, &response_length) != 0) {
  1109. break;
  1110. }
  1111. char event_bit_representation[response_length * 8 + 1];
  1112. event_bit_representation[0] = '\0';
  1113. for(size_t i = 0; i < response_length; i++) {
  1114. char bits[9];
  1115. uint8_t byte = bit_buffer_get_byte(rx_buffer, i);
  1116. byte_to_binary(byte, bits);
  1117. strlcat(
  1118. event_bit_representation, bits, sizeof(event_bit_representation));
  1119. }
  1120. // EventServiceProvider
  1121. const char* event_key = "EventServiceProvider";
  1122. if(is_calypso_node_present(
  1123. event_bit_representation, event_key, OpusEventStructure)) {
  1124. int positionOffset = get_calypso_node_offset(
  1125. event_bit_representation, event_key, OpusEventStructure);
  1126. int start = positionOffset,
  1127. end = positionOffset +
  1128. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1129. card->opus->events[i - 1].service_provider =
  1130. bit_slice_to_dec(event_bit_representation, start, end);
  1131. }
  1132. // EventRouteNumber
  1133. event_key = "EventRouteNumber";
  1134. if(is_calypso_node_present(
  1135. event_bit_representation, event_key, OpusEventStructure)) {
  1136. int positionOffset = get_calypso_node_offset(
  1137. event_bit_representation, event_key, OpusEventStructure);
  1138. int start = positionOffset,
  1139. end = positionOffset +
  1140. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1141. card->opus->events[i - 1].route_number =
  1142. bit_slice_to_dec(event_bit_representation, start, end);
  1143. card->opus->events[i - 1].route_number_available = true;
  1144. }
  1145. // EventContractPointer
  1146. event_key = "EventContractPointer";
  1147. if(is_calypso_node_present(
  1148. event_bit_representation, event_key, OpusEventStructure)) {
  1149. int positionOffset = get_calypso_node_offset(
  1150. event_bit_representation, event_key, OpusEventStructure);
  1151. int start = positionOffset,
  1152. end = positionOffset +
  1153. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1154. card->opus->events[i - 1].used_contract =
  1155. bit_slice_to_dec(event_bit_representation, start, end);
  1156. card->opus->events[i - 1].used_contract_available = true;
  1157. }
  1158. // EventDate + EventTime
  1159. event_key = "EventDate";
  1160. int positionOffset = get_calypso_node_offset(
  1161. event_bit_representation, event_key, OpusEventStructure);
  1162. int start = positionOffset,
  1163. end = positionOffset +
  1164. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1165. uint64_t date_timestamp =
  1166. (bit_slice_to_dec(event_bit_representation, start, end) +
  1167. (float)epoch) +
  1168. 3600;
  1169. datetime_timestamp_to_datetime(
  1170. date_timestamp, &card->opus->events[i - 1].date);
  1171. event_key = "EventTime";
  1172. positionOffset = get_calypso_node_offset(
  1173. event_bit_representation, event_key, OpusEventStructure);
  1174. start = positionOffset,
  1175. end = positionOffset +
  1176. get_calypso_node_size(event_key, OpusEventStructure) - 1;
  1177. int decimal_value = bit_slice_to_dec(event_bit_representation, start, end);
  1178. card->opus->events[i - 1].date.hour = (decimal_value * 60) / 3600;
  1179. card->opus->events[i - 1].date.minute = ((decimal_value * 60) % 3600) / 60;
  1180. card->opus->events[i - 1].date.second = ((decimal_value * 60) % 3600) % 60;
  1181. }
  1182. // Free the calypso structure
  1183. free_calypso_structure(OpusEventStructure);
  1184. break;
  1185. }
  1186. default:
  1187. break;
  1188. }
  1189. widget_add_text_scroll_element(
  1190. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  1191. CalypsoContext* context = malloc(sizeof(CalypsoContext));
  1192. context->card = card;
  1193. context->page_id = 0;
  1194. context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  1195. app->calypso_context = context;
  1196. // Ensure no nested mutexes
  1197. furi_mutex_acquire(context->mutex, FuriWaitForever);
  1198. update_page_info(app, parsed_data);
  1199. furi_mutex_release(context->mutex);
  1200. widget_add_text_scroll_element(
  1201. widget, 0, 0, 128, 64, furi_string_get_cstr(parsed_data));
  1202. // Ensure no nested mutexes
  1203. furi_mutex_acquire(context->mutex, FuriWaitForever);
  1204. update_widget_elements(app);
  1205. furi_mutex_release(context->mutex);
  1206. furi_string_free(parsed_data);
  1207. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewWidget);
  1208. metroflip_app_blink_stop(app);
  1209. stage = MetroflipPollerEventTypeSuccess;
  1210. next_command = NfcCommandStop;
  1211. } while(false);
  1212. if(stage != MetroflipPollerEventTypeSuccess) {
  1213. next_command = NfcCommandStop;
  1214. }
  1215. }
  1216. }
  1217. bit_buffer_free(tx_buffer);
  1218. bit_buffer_free(rx_buffer);
  1219. return next_command;
  1220. }
  1221. void metroflip_scene_navigo_on_enter(void* context) {
  1222. Metroflip* app = context;
  1223. dolphin_deed(DolphinDeedNfcRead);
  1224. // Setup view
  1225. Popup* popup = app->popup;
  1226. popup_set_header(popup, "Apply\n card to\nthe back", 68, 30, AlignLeft, AlignTop);
  1227. popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
  1228. // Start worker
  1229. view_dispatcher_switch_to_view(app->view_dispatcher, MetroflipViewPopup);
  1230. nfc_scanner_alloc(app->nfc);
  1231. app->poller = nfc_poller_alloc(app->nfc, NfcProtocolIso14443_4b);
  1232. nfc_poller_start(app->poller, metroflip_scene_navigo_poller_callback, app);
  1233. metroflip_app_blink_start(app);
  1234. }
  1235. bool metroflip_scene_navigo_on_event(void* context, SceneManagerEvent event) {
  1236. Metroflip* app = context;
  1237. bool consumed = false;
  1238. if(event.type == SceneManagerEventTypeCustom) {
  1239. if(event.event == MetroflipPollerEventTypeCardDetect) {
  1240. Popup* popup = app->popup;
  1241. popup_set_header(popup, "Scanning..", 68, 30, AlignLeft, AlignTop);
  1242. consumed = true;
  1243. } else if(event.event == MetroflipCustomEventPollerFileNotFound) {
  1244. Popup* popup = app->popup;
  1245. popup_set_header(popup, "Read Error,\n wrong card", 68, 30, AlignLeft, AlignTop);
  1246. consumed = true;
  1247. } else if(event.event == MetroflipCustomEventPollerFail) {
  1248. Popup* popup = app->popup;
  1249. popup_set_header(popup, "Error, try\n again", 68, 30, AlignLeft, AlignTop);
  1250. consumed = true;
  1251. }
  1252. } else if(event.type == SceneManagerEventTypeBack) {
  1253. scene_manager_search_and_switch_to_previous_scene(app->scene_manager, MetroflipSceneStart);
  1254. consumed = true;
  1255. }
  1256. return consumed;
  1257. }
  1258. void metroflip_scene_navigo_on_exit(void* context) {
  1259. Metroflip* app = context;
  1260. if(app->poller) {
  1261. nfc_poller_stop(app->poller);
  1262. nfc_poller_free(app->poller);
  1263. }
  1264. metroflip_app_blink_stop(app);
  1265. widget_reset(app->widget);
  1266. // Clear view
  1267. popup_reset(app->popup);
  1268. if(app->calypso_context) {
  1269. CalypsoContext* ctx = app->calypso_context;
  1270. free(ctx->card->navigo);
  1271. free(ctx->card->opus);
  1272. free(ctx->card);
  1273. furi_mutex_free(ctx->mutex);
  1274. free(ctx);
  1275. app->calypso_context = NULL;
  1276. }
  1277. }