p≡p engine
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.

1825 lines
68 KiB

3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. // This file is under GNU General Public License 3.0
  2. // see LICENSE.txt
  3. // generate state machine code
  4. // Copyleft (c) 2016-2020, p≡p foundation
  5. // Written by Volker Birk
  6. include yslt.yml2
  7. tstylesheet {
  8. include standardlib.ysl2
  9. include ./functions.ysl2
  10. template "/protocol" {
  11. if "count(fsm/state)>0" document "generated/{@name}_event.h", "text"
  12. ||
  13. /**
  14. * @file «@name»_event.h
  15. * @brief Structures and functions for «@name» events.
  16. * @generated from ../sync/gen_statemachine.ysl2
  17. *
  18. * @license GNU General Public License 3.0 - see LICENSE.txt
  19. */
  20. #ifndef «yml:ucase(@name)»_EVENT_H
  21. #define «yml:ucase(@name)»_EVENT_H
  22. #include "pEpEngine.h"
  23. #ifdef __cplusplus
  24. extern "C" {
  25. #endif
  26. /**
  27. * «@name» is also defined as «yml:ucase(@name)» for ASN.1 reasons and
  28. * forward declaration to avoid the need for header inclusion everywhere.
  29. * @see struct «@name» in «@name».h
  30. */
  31. typedef struct «@name» «yml:ucase(@name)»;
  32. /** forward declaration - @see enum «@name»_PR in «@name».h */
  33. typedef int «yml:ucase(@name)»_PR;
  34. /**
  35. * Data for an event related to a message
  36. */
  37. typedef struct «@name»_event {
  38. // state machine data
  39. «yml:ucase(@name)»_PR fsm; /*!< state machine type associated with this event */
  40. int event; /*!< type of event */
  41. «yml:ucase(@name)» *msg; /*!< the «@name» messaged caused by (or causing???) this event */
  42. // transport data
  43. pEp_identity *from; /*!< identity of the message sender */
  44. char *sender_fpr; /*!< fpr of key used by the message sender */
  45. identity_list *own_identities; /*!< List of our own identities */
  46. } «@name»_event_t;
  47. /**
  48. * <!-- new_«@name»_event() -->
  49. *
  50. * @brief allocate a new «@name»_event
  51. *
  52. * @param[in] fsm finite state machine the event is for
  53. * @param[in] event event or None
  54. * @param[in] msg message to compute event from
  55. *
  56. * @retval pointer to new event
  57. * @retval NULL in case of failure
  58. *
  59. */
  60. DYNAMIC_API «@name»_event_t *new_«@name»_event(«yml:ucase(@name)»_PR fsm, int event, «yml:ucase(@name)» *msg);
  61. #define «yml:ucase(@name)»_TIMEOUT_EVENT new_«@name»_event(«@name»_PR_NOTHING, 0, NULL);
  62. /**
  63. * <!-- free_«@name»_event() -->
  64. *
  65. * @brief free memory occupied by event
  66. *
  67. * @param[in] ev event to free
  68. *
  69. */
  70. DYNAMIC_API void free_«@name»_event(«@name»_event_t *ev);
  71. #ifdef __cplusplus
  72. }
  73. #endif
  74. #endif
  75. ||
  76. if "count(fsm/state)>0" document "generated/{@name}_event.c", "text"
  77. ||
  78. /**
  79. * @file «@name»_event.c
  80. * @brief Allocation, fsm drivers, and handling for «@name» events.
  81. * @generated from ../sync/gen_statemachine.ysl2
  82. *
  83. * @license GNU General Public License 3.0 - see LICENSE.txt
  84. */
  85. #include "platform.h"
  86. #include "pEp_internal.h"
  87. #include "«@name»_event.h"
  88. #include "«@name»_func.h"
  89. `` for "fsm" | #include "«@name»_fsm.h"
  90. DYNAMIC_API «@name»_event_t *new_«@name»_event(«yml:ucase(@name)»_PR fsm, int event, «@name»_t *msg)
  91. {
  92. «@name»_event_t *ev = («@name»_event_t *) calloc(1, sizeof(«@name»_event_t));
  93. assert(ev);
  94. if (!ev)
  95. return NULL;
  96. ev->fsm = fsm;
  97. ev->event = event;
  98. ev->msg = msg;
  99. if (msg) {
  100. switch (fsm) {
  101. `` apply "fsm", 3, mode=event
  102. default:
  103. // unknown protocol
  104. free(ev);
  105. return NULL;
  106. }
  107. }
  108. return ev;
  109. }
  110. DYNAMIC_API void free_«@name»_event(«@name»_event_t *ev)
  111. {
  112. if (ev) {
  113. free_identity_list(ev->own_identities);
  114. free_«@name»_message(ev->msg);
  115. free_identity(ev->from);
  116. free(ev->sender_fpr);
  117. free(ev);
  118. }
  119. }
  120. ||
  121. if "count(fsm/state)>0" document "generated/{@name}_impl.h", "text" {
  122. ||
  123. /**
  124. * @file «@name»_impl.h
  125. * @brief «@name» protocol implementation declarations
  126. * @generated from ../sync/gen_statemachine.ysl2
  127. *
  128. * @license GNU General Public License 3.0 - see LICENSE.txt
  129. */
  130. #ifndef «yml:ucase(@name)»_IMPL_H
  131. #define «yml:ucase(@name)»_IMPL_H
  132. #include "fsm_common.h"
  133. #include "«@name»_event.h"
  134. #include "message_api.h"
  135. #include "../asn.1/«@name».h"
  136. #define «yml:ucase(@name)»_THRESHOLD «@threshold»
  137. `` for "fsm[count(state)>0]" | #define «yml:ucase(@name)»_THRESHOLD «@threshold»
  138. #ifdef __cplusplus
  139. extern "C" {
  140. #endif
  141. /////////////////////////////////////////////////////////////////////
  142. // conditions
  143. /////////////////////////////////////////////////////////////////////
  144. ||
  145. for "func:distinctName(*//condition)" {
  146. | /**
  147. | * @brief Evaluate condition: Is «@name» true?
  148. | * @param[in] session the session
  149. | * @param[out] result true if «@name», else false
  150. | * @retval status
  151. | */
  152. | PEP_STATUS «@name»(PEP_SESSION session, bool * result);
  153. }
  154. ||
  155. /////////////////////////////////////////////////////////////////////
  156. // actions
  157. /////////////////////////////////////////////////////////////////////
  158. ||
  159. for "func:distinctName(*//action)" {
  160. | /**
  161. | * @brief Action: Do «@name»
  162. | * @param[in] session the session
  163. | * @retval status
  164. | */
  165. | PEP_STATUS «@name»(PEP_SESSION session);
  166. }
  167. ||
  168. /////////////////////////////////////////////////////////////////////
  169. // timeout handlers
  170. /////////////////////////////////////////////////////////////////////
  171. ||
  172. for "fsm[@threshold > 0]" {
  173. | /**
  174. | * <!-- «@name»TimeoutHandler() -->
  175. | *
  176. | * @brief Handle timeouts for «@name» state machine
  177. | *
  178. | * @param[in] session the session
  179. | * @retval status
  180. | */
  181. | PEP_STATUS «@name»TimeoutHandler(PEP_SESSION session);
  182. }
  183. ||
  184. /////////////////////////////////////////////////////////////////////
  185. // «@name» state machine driver, message sending, and event receipt
  186. /////////////////////////////////////////////////////////////////////
  187. /**
  188. * <!-- send_«@name»_message() -->
  189. *
  190. * @brief send message about «@name» event to communication partners using state
  191. *
  192. * @param[in] session the session
  193. * @param[in] fsm the finite state machine from which to get state
  194. * @param[in] message_type type of message to send
  195. *
  196. * @retval status of message send
  197. *
  198. */
  199. PEP_STATUS send_«@name»_message(
  200. PEP_SESSION session,
  201. «@name»_PR fsm,
  202. int message_type
  203. );
  204. /**
  205. * <!-- recv_«@name»_event() -->
  206. *
  207. * @brief receive «@name» message and store it in state
  208. *
  209. * @param[in] session the session
  210. * @param[in] ev the event to process
  211. *
  212. * @retval status of event processing/storage
  213. *
  214. */
  215. PEP_STATUS recv_«@name»_event(
  216. PEP_SESSION session,
  217. «@name»_event_t *ev
  218. );
  219. /**
  220. * <!-- «@name»_driver() -->
  221. *
  222. * @brief «@name» state machine driver
  223. *
  224. * @param[in] session the session
  225. * @param[in] fsm finite state machine to drive
  226. * @param[in] ev the event to process???
  227. *
  228. * @retval ???
  229. *
  230. * @note if fsm or event set to 0 use fields in src if present
  231. *
  232. */
  233. PEP_STATUS «@name»_driver(
  234. PEP_SESSION session,
  235. «@name»_PR fsm,
  236. int event
  237. );
  238. /////////////////////////////////////////////////////////////////////////////
  239. // API used by the engine internally //
  240. /////////////////////////////////////////////////////////////////////////////
  241. /**
  242. * <!-- signal_«@name»_event() -->
  243. *
  244. * @brief Internal engine API: call this if you need to signal an external «@name» event
  245. *
  246. * @param[in] session the session
  247. * @param[in] fsm finite state machine
  248. * @param[in] event event type
  249. * @param[out] own_identities list of own identities
  250. *
  251. * @retval status
  252. *
  253. * @ownership the ownership of own_identities goes to the callee
  254. *
  255. */
  256. PEP_STATUS signal_«@name»_event(
  257. PEP_SESSION session,
  258. «@name»_PR fsm,
  259. int event,
  260. identity_list *own_identities
  261. );
  262. /**
  263. * <!-- signal_«@name»_message() -->
  264. *
  265. * @brief Internal engine API: to be called by transports receiving a «@name» message
  266. *
  267. * @param[in] session the session
  268. * @param[in] rating rating of the decrypted «@name» message
  269. * @param[out] data payload of «@name» message
  270. * @param[out] from identity of the message sender
  271. * @param[out] fpr fingerprint of the sender key used to sign the message
  272. *
  273. * @retval status
  274. *
  275. */
  276. PEP_STATUS signal_«@name»_message(
  277. PEP_SESSION session,
  278. PEP_rating rating,
  279. const char *data,
  280. size_t size,
  281. const pEp_identity *from,
  282. const char *sender_fpr
  283. );
  284. #ifdef __cplusplus
  285. }
  286. #endif
  287. #endif
  288. ||
  289. }
  290. if "count(fsm/state)>0" document "generated/{@name}_impl.c", "text" {
  291. ||
  292. /**
  293. * @file «@name»_impl.c
  294. * @brief «@name» protocol implementation: driver, event handling, message signalling/sending/receipt, etc.
  295. * @generated from ../sync/gen_statemachine.ysl2
  296. *
  297. * @license GNU General Public License 3.0 - see LICENSE.txt
  298. */
  299. #include "«@name»_impl.h"
  300. #include "pEp_internal.h"
  301. #include "«@name»_event.h"
  302. #include "«yml:lcase(@name)»_codec.h"
  303. #include "baseprotocol.h"
  304. #include "security_checks.h"
  305. `` for "fsm[count(state)>0]" | #include "«@name»_fsm.h"
  306. `` apply "fsm[count(state)>0]", 0, mode=timeout
  307. PEP_STATUS «@name»_driver(
  308. PEP_SESSION session,
  309. «@name»_PR fsm,
  310. int event
  311. )
  312. {
  313. assert(session);
  314. if (!session)
  315. return PEP_ILLEGAL_VALUE;
  316. switch (fsm) {
  317. case None:
  318. if (!event) {
  319. // timeout occured
  320. `` for "fsm[count(state)>0]" |>>>> «../@name»_driver(session, «../@name»_PR_«yml:lcase(@name)», None);
  321. return PEP_STATUS_OK;
  322. }
  323. return PEP_ILLEGAL_VALUE;
  324. `` apply "fsm[count(state)>0]", mode=reset_state_machine;
  325. default:
  326. return PEP_ILLEGAL_VALUE;
  327. }
  328. int next_state = None;
  329. do {
  330. switch (fsm) {
  331. `` apply "fsm[count(state)>0]", 3, mode=driver
  332. default:
  333. return PEP_ILLEGAL_VALUE;
  334. }
  335. } while (next_state);
  336. return PEP_STATUS_OK;
  337. }
  338. PEP_STATUS signal_«@name»_event(
  339. PEP_SESSION session,
  340. «@name»_PR fsm,
  341. int event,
  342. identity_list *own_identities
  343. )
  344. {
  345. «@name»_t *msg = NULL;
  346. «@name»_event_t *ev = NULL;
  347. assert(session && fsm > 0 && event > None);
  348. if (!(session && fsm > 0 && event > None))
  349. return PEP_ILLEGAL_VALUE;
  350. PEP_STATUS status = PEP_STATUS_OK;
  351. if (!session->inject_«yml:lcase(@name)»_event)
  352. return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
  353. if (event < Extra) {
  354. // FIXME: there should be a mapping between event and message type
  355. // with current implementation they've got an offset of 1
  356. msg = new_«@name»_message(fsm, event - 1);
  357. if (!msg) {
  358. status = PEP_OUT_OF_MEMORY;
  359. goto the_end;
  360. }
  361. status = update_«@name»_message(session, msg);
  362. if (status)
  363. goto the_end;
  364. }
  365. ev = new_«@name»_event(fsm, event, msg);
  366. if (!ev) {
  367. status = PEP_OUT_OF_MEMORY;
  368. goto the_end;
  369. }
  370. status = set_all_userids_to_own(session, own_identities);
  371. if (status != PEP_STATUS_OK)
  372. goto the_end;
  373. ev->own_identities = own_identities;
  374. int result = session->inject_«yml:lcase(@name)»_event(ev,
  375. session->«yml:lcase(@name)»_management);
  376. if (result) {
  377. status = PEP_STATEMACHINE_ERROR;
  378. goto the_end;
  379. }
  380. return PEP_STATUS_OK;
  381. the_end:
  382. free_«@name»_event(ev); // msg gets freed here
  383. return status;
  384. }
  385. PEP_STATUS signal_«@name»_message(
  386. PEP_SESSION session,
  387. PEP_rating rating,
  388. const char *data,
  389. size_t size,
  390. const pEp_identity *from,
  391. const char *sender_fpr
  392. )
  393. {
  394. assert(session && data && size);
  395. if (!(session && data && size))
  396. return PEP_ILLEGAL_VALUE;
  397. if (!session->inject_«yml:lcase(@name)»_event)
  398. return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
  399. PEP_STATUS status = PEP_STATUS_OK;
  400. «@name»_event_t *ev = NULL;
  401. «@name»_t *msg = NULL;
  402. status = decode_«@name»_message(data, size, &msg);
  403. if (status)
  404. return status;
  405. «@name»_PR fsm = msg->present;
  406. int event = 0;
  407. bool is_own_key = false;
  408. switch (fsm) {
  409. `` apply "fsm[count(state)>0]", 2, mode=signal_message
  410. default:
  411. status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
  412. goto the_end;
  413. }
  414. ev = new_«@name»_event(fsm, event, msg);
  415. if (!ev) {
  416. status = PEP_OUT_OF_MEMORY;
  417. goto the_end;
  418. }
  419. // add transport data
  420. if (from) {
  421. ev->from = identity_dup(from);
  422. if (!ev->from) {
  423. status = PEP_OUT_OF_MEMORY;
  424. goto the_end;
  425. }
  426. }
  427. if (sender_fpr) {
  428. ev->sender_fpr = strdup(sender_fpr);
  429. assert(ev->sender_fpr);
  430. if (!ev->sender_fpr) {
  431. status = PEP_OUT_OF_MEMORY;
  432. goto the_end;
  433. }
  434. }
  435. int result = session->inject_«yml:lcase(@name)»_event(ev,
  436. session->«yml:lcase(@name)»_management);
  437. if (result) {
  438. status = PEP_STATEMACHINE_ERROR;
  439. goto the_end;
  440. }
  441. return PEP_STATUS_OK;
  442. the_end:
  443. free_«@name»_event(ev); // msg gets freed here
  444. return status;
  445. }
  446. PEP_STATUS send_«@name»_message(
  447. PEP_SESSION session,
  448. «@name»_PR fsm,
  449. int message_type
  450. )
  451. {
  452. PEP_STATUS status = PEP_STATUS_OK;
  453. assert(session && (int) fsm > None && message_type > None);
  454. if (!(session && (int) fsm > None && message_type > None))
  455. return PEP_ILLEGAL_VALUE;
  456. time_t now = time(NULL);
  457. switch (fsm) {
  458. ||
  459. apply "fsm[count(state)>0]", 2, mode=send;
  460. ||
  461. }
  462. «@name»_t *msg = new_«@name»_message(fsm, message_type);
  463. if (!msg)
  464. return PEP_OUT_OF_MEMORY;
  465. char *data = NULL;
  466. message *m = NULL;
  467. identity_list *channels = NULL;
  468. char *key_data = NULL;
  469. size_t key_data_size = 0;
  470. stringlist_t *extra = NULL;
  471. bool transaction;
  472. status = update_«@name»_message(session, msg);
  473. if (status)
  474. goto the_end;
  475. size_t size = 0;
  476. status = encode_«@name»_message(msg, &data, &size);
  477. if (status)
  478. goto the_end;
  479. // we never use this
  480. if (session->«yml:lcase(@name)»_state.comm_partner.identity
  481. && session->«yml:lcase(@name)»_state.comm_partner.identity->fpr) {
  482. free(session->«yml:lcase(@name)»_state.comm_partner.identity->fpr);
  483. session->«yml:lcase(@name)»_state.comm_partner.identity->fpr = NULL;
  484. }
  485. // if we have this we always use this
  486. if (session->«yml:lcase(@name)»_state.comm_partner.sender_fpr) {
  487. free(session->«yml:lcase(@name)»_state.transport.sender_fpr);
  488. session->«yml:lcase(@name)»_state.transport.sender_fpr =
  489. strdup(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
  490. assert(session->«yml:lcase(@name)»_state.transport.sender_fpr);
  491. if (!session->«yml:lcase(@name)»_state.transport.sender_fpr) {
  492. status = PEP_OUT_OF_MEMORY;
  493. goto the_end;
  494. }
  495. }
  496. switch (fsm) {
  497. ||
  498. apply "fsm[count(state)>0]", 2, mode=send2;
  499. ||
  500. default:
  501. break;
  502. }
  503. for (identity_list *li = channels; li && li->ident ; li = li->next) {
  504. message *_m = NULL;
  505. char *_data = NULL;
  506. _data = malloc(size);
  507. assert(_data);
  508. if (!_data) {
  509. status = PEP_OUT_OF_MEMORY;
  510. goto the_end;
  511. }
  512. memcpy(_data, data, size);
  513. switch (message_type) {
  514. `` for "fsm/message[@security='unencrypted' and ../@name!='KeySync']" | #error unencrypted only allowed with KeySync
  515. `` for "fsm/message[@security='unencrypted' and ../@name='KeySync']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
  516. status = try_base_prepare_message(
  517. session,
  518. li->ident,
  519. li->ident,
  520. BASE_SYNC,
  521. _data,
  522. size,
  523. li->ident->fpr,
  524. &_m
  525. );
  526. if (status) {
  527. free(_data);
  528. if (status == PEP_OUT_OF_MEMORY)
  529. goto the_end;
  530. continue;
  531. }
  532. attach_own_key(session, _m);
  533. decorate_message(session, _m, PEP_rating_undefined, NULL, true, true);
  534. m = _m;
  535. break;
  536. `` for "fsm/message[@security='untrusted']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
  537. // add fpr of key of comm partner
  538. if (!session->«yml:lcase(@name)»_state.transport.sender_fpr) {
  539. status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
  540. continue;
  541. }
  542. extra = new_stringlist(session->«yml:lcase(@name)»_state.transport.sender_fpr);
  543. if (!extra) {
  544. status = PEP_OUT_OF_MEMORY;
  545. goto the_end;
  546. }
  547. status = base_prepare_message(
  548. session,
  549. li->ident,
  550. li->ident,
  551. BASE_SYNC,
  552. _data,
  553. size,
  554. NULL,
  555. &_m
  556. );
  557. if (status) {
  558. if (status == PEP_OUT_OF_MEMORY)
  559. goto the_end;
  560. free(_data);
  561. continue;
  562. }
  563. status = try_encrypt_message(session, _m, extra, &m, PEP_enc_PEP, 0);
  564. if (status) {
  565. if (status == PEP_OUT_OF_MEMORY)
  566. goto the_end;
  567. status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
  568. continue;
  569. }
  570. add_opt_field(m, "pEp-auto-consume", "yes");
  571. m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
  572. free_message(_m);
  573. break;
  574. // attach own keys for new member
  575. `` for "fsm/message[@security='attach_own_keys_for_new_member']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
  576. // check if we had a former negotiation
  577. transaction = false;
  578. for (int i=0; i < session->«yml:lcase(@name)»_state.own.negotiation.size; i++) {
  579. if (session->«yml:lcase(@name)»_state.own.negotiation.buf[i]) {
  580. transaction = true;
  581. break;
  582. }
  583. }
  584. // if it is a former negotiation check if the key
  585. // is fully trusted and the sender key of this
  586. // transaction; if so add the sender key to extra
  587. // keys allowing this new partner to read the
  588. // secret keys
  589. if (transaction) {
  590. if (!(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr &&
  591. session->«yml:lcase(@name)»_state.transport.from &&
  592. session->«yml:lcase(@name)»_state.transport.from->user_id))
  593. {
  594. status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
  595. continue;
  596. }
  597. // test if this is a green channel
  598. pEp_identity *ident = new_identity(NULL,
  599. session->«yml:lcase(@name)»_state.comm_partner.sender_fpr,
  600. session->«yml:lcase(@name)»_state.transport.from->user_id,
  601. NULL
  602. );
  603. if (!ident) {
  604. status = PEP_OUT_OF_MEMORY;
  605. goto the_end;
  606. }
  607. status = get_trust(session, ident);
  608. if (status) {
  609. free_identity(ident);
  610. goto the_end;
  611. }
  612. assert(ident->comm_type == PEP_ct_pEp); // we don't deliver otherwise
  613. if (ident->comm_type != PEP_ct_pEp) {
  614. free_identity(ident);
  615. status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
  616. continue;
  617. }
  618. free_identity(ident);
  619. // test if we accepted this as own key already
  620. bool is_own_key = false;
  621. status = own_key_is_listed(session,
  622. session->«yml:lcase(@name)»_state.comm_partner.sender_fpr,
  623. &is_own_key);
  624. assert(!status);
  625. if (status)
  626. goto the_end;
  627. if (!is_own_key) {
  628. status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
  629. continue;
  630. }
  631. // if so add key of comm partner to extra keys
  632. extra = new_stringlist(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
  633. if (!extra) {
  634. status = PEP_OUT_OF_MEMORY;
  635. goto the_end;
  636. }
  637. }
  638. status = base_prepare_message(
  639. session,
  640. li->ident,
  641. li->ident,
  642. BASE_SYNC,
  643. _data,
  644. size,
  645. NULL,
  646. &_m
  647. );
  648. if (status) {
  649. free(_data);
  650. if (status == PEP_OUT_OF_MEMORY)
  651. goto the_end;
  652. continue;
  653. }
  654. // export secret keys into memory
  655. key_data = strdup("");
  656. assert(key_data);
  657. if (!key_data) {
  658. free(_data);
  659. free_message(_m);
  660. status = PEP_OUT_OF_MEMORY;
  661. goto the_end;
  662. }
  663. key_data_size = 1; // N.B. If null termination makes us happy for debugging, fine, but
  664. // if we include this in the size, libetpan will null terminate and
  665. // go bananas. We can't have a NUL in the mime text.
  666. for (stringlist_t *sl = session->«yml:lcase(@name)»_state.own.keys;
  667. sl && sl->value ; sl = sl->next)
  668. {
  669. char *_key_data = NULL;
  670. size_t _size = 0;
  671. status = export_secret_key(session, sl->value, &_key_data, &_size);
  672. if (status && status != PEP_KEY_NOT_FOUND) {
  673. free(_data);
  674. free_message(_m);
  675. goto the_end;
  676. }
  677. if (status != PEP_KEY_NOT_FOUND) {
  678. assert(_key_data && _size);
  679. char *n = realloc(key_data, key_data_size + _size);
  680. if (!n) {
  681. free(_data);
  682. free_message(_m);
  683. status = PEP_OUT_OF_MEMORY;
  684. goto the_end;
  685. }
  686. key_data = n;
  687. key_data_size += _size;
  688. strlcat(key_data, _key_data, key_data_size);
  689. free(_key_data);
  690. _key_data = NULL;
  691. }
  692. status = export_key(session, sl->value, &_key_data, &_size);
  693. if (status && status != PEP_KEY_NOT_FOUND) {
  694. free(_data);
  695. free_message(_m);
  696. goto the_end;
  697. }
  698. if (status != PEP_KEY_NOT_FOUND) {
  699. assert(_key_data && _size);
  700. char *n = realloc(key_data, key_data_size + _size);
  701. if (!n) {
  702. free(_data);
  703. free_message(_m);
  704. status = PEP_OUT_OF_MEMORY;
  705. goto the_end;
  706. }
  707. key_data = n;
  708. key_data_size += _size;
  709. strlcat(key_data, _key_data, key_data_size);
  710. free(_key_data);
  711. _key_data = NULL;
  712. }
  713. }
  714. // add secret key data as attachment
  715. // N.B. The -1 makes sure we do NOT add a NUL into the mime stream!
  716. bloblist_t *bl = bloblist_add(_m->attachments, key_data, key_data_size - 1,
  717. "application/octet-stream", "file://own.key");
  718. if (!bl) {
  719. free(_data);
  720. free_message(_m);
  721. status = PEP_OUT_OF_MEMORY;
  722. goto the_end;
  723. }
  724. key_data = NULL;
  725. status = try_encrypt_message(session, _m, extra, &m, PEP_enc_PEP, 0);
  726. if (status) {
  727. if (status == PEP_OUT_OF_MEMORY)
  728. goto the_end;
  729. status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
  730. continue;
  731. }
  732. add_opt_field(m, "pEp-auto-consume", "yes");
  733. m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
  734. free_message(_m);
  735. break;
  736. // attach own keys for group
  737. `` for "fsm/message[@security='attach_own_keys_for_group']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
  738. status = base_prepare_message(
  739. session,
  740. li->ident,
  741. li->ident,
  742. BASE_SYNC,
  743. _data,
  744. size,
  745. NULL,
  746. &_m
  747. );
  748. if (status) {
  749. free(_data);
  750. if (status == PEP_OUT_OF_MEMORY)
  751. goto the_end;
  752. continue;
  753. }
  754. // export secret keys into memory
  755. key_data = strdup("");
  756. assert(key_data);
  757. if (!key_data) {
  758. free(_data);
  759. free_message(_m);
  760. status = PEP_OUT_OF_MEMORY;
  761. goto the_end;
  762. }
  763. key_data_size = 1; // N.B. If null termination makes us happy for debugging, fine, but
  764. // if we include this in the size, libetpan will null terminate and
  765. // go bananas. We can't have a NUL in the mime text.
  766. for (stringlist_t *sl = session->«yml:lcase(@name)»_state.own.keys;
  767. sl && sl->value ; sl = sl->next)
  768. {
  769. char *_key_data = NULL;
  770. size_t _size = 0;
  771. status = export_secret_key(session, sl->value, &_key_data, &_size);
  772. if (status && status != PEP_KEY_NOT_FOUND) {
  773. free(_data);
  774. free_message(_m);
  775. goto the_end;
  776. }
  777. if (status != PEP_KEY_NOT_FOUND) {
  778. assert(_key_data && _size);
  779. char *n = realloc(key_data, key_data_size + _size);
  780. if (!n) {
  781. free(_data);
  782. free_message(_m);
  783. status = PEP_OUT_OF_MEMORY;
  784. goto the_end;
  785. }
  786. key_data = n;
  787. key_data_size += _size;
  788. strlcat(key_data, _key_data, key_data_size);
  789. free(_key_data);
  790. _key_data = NULL;
  791. }
  792. status = export_key(session, sl->value, &_key_data, &_size);
  793. if (status && status != PEP_KEY_NOT_FOUND) {
  794. free(_data);
  795. free_message(_m);
  796. goto the_end;
  797. }
  798. if (status != PEP_KEY_NOT_FOUND) {
  799. assert(_key_data && _size);
  800. char *n = realloc(key_data, key_data_size + _size);
  801. if (!n) {
  802. free(_data);
  803. free_message(_m);
  804. status = PEP_OUT_OF_MEMORY;
  805. goto the_end;
  806. }
  807. key_data = n;
  808. key_data_size += _size;
  809. strlcat(key_data, _key_data, key_data_size);
  810. free(_key_data);
  811. _key_data = NULL;
  812. }
  813. }
  814. // add secret key data as attachment
  815. // N.B. The -1 makes sure we do NOT add a NUL into the mime stream!
  816. bl = bloblist_add(_m->attachments, key_data, key_data_size - 1,
  817. "application/octet-stream", "file://own.key");
  818. if (!bl) {
  819. free(_data);
  820. free_message(_m);
  821. status = PEP_OUT_OF_MEMORY;
  822. goto the_end;
  823. }
  824. key_data = NULL;
  825. // we do not support extra keys here and will only encrypt to ourselves
  826. status = try_encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
  827. if (status) {
  828. if (status == PEP_OUT_OF_MEMORY)
  829. goto the_end;
  830. status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
  831. continue;
  832. }
  833. add_opt_field(m, "pEp-auto-consume", "yes");
  834. m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
  835. free_message(_m);
  836. break;
  837. default: // security=trusted only
  838. status = base_prepare_message(
  839. session,
  840. li->ident,
  841. li->ident,
  842. BASE_SYNC,
  843. _data,
  844. size,
  845. NULL,
  846. &_m
  847. );
  848. if (status) {
  849. free(_data);
  850. if (status == PEP_OUT_OF_MEMORY)
  851. goto the_end;
  852. continue;
  853. }
  854. status = try_encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
  855. if (status) {
  856. status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
  857. if (status == PEP_OUT_OF_MEMORY)
  858. goto the_end;
  859. continue;
  860. }
  861. add_opt_field(m, "pEp-auto-consume", "yes");
  862. m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
  863. free_message(_m);
  864. }
  865. status = session->messageToSend(m);
  866. m = NULL;
  867. }
  868. ||
  869. if "fsm/message[@ratelimit>0]" {
  870. ||
  871. switch (fsm) {
  872. ||
  873. for "fsm[message/@ratelimit>0]" {
  874. ||
  875. case Sync_PR_«yml:lcase(@name)»:
  876. switch (message_type) {
  877. ||
  878. for "message[@ratelimit>0]"
  879. ||
  880. case «../@name»_PR_«yml:mixedCase(@name)»:
  881. session->«yml:lcase(../../@name)»_state.own.last_«../@name»_«@name» = now;
  882. break;
  883. ||
  884. ||
  885. default:
  886. break;
  887. }
  888. break;
  889. ||
  890. }
  891. ||
  892. default:
  893. break;
  894. }
  895. ||
  896. }
  897. ||
  898. the_end:
  899. free_stringlist(extra);
  900. free_identity_list(channels);
  901. free_message(m);
  902. free(data);
  903. free(key_data);
  904. free_«@name»_message(msg);
  905. if (status)
  906. SERVICE_ERROR_LOG(session, "send_«@name»_message()", status);
  907. return status;
  908. }
  909. PEP_STATUS recv_«@name»_event(
  910. PEP_SESSION session,
  911. «@name»_event_t *ev
  912. )
  913. {
  914. assert(session && ev);
  915. if (!(session && ev))
  916. return PEP_ILLEGAL_VALUE;
  917. PEP_STATUS status = PEP_STATUS_OK;
  918. «@name»_PR fsm = (int) None;
  919. int event = None;
  920. if (ev->event > None && ev->event < Extra) {
  921. status = update_«@name»_state(session, ev->msg, &fsm, &event);
  922. if (status)
  923. goto the_end;
  924. if (ev->fsm) {
  925. if (ev->fsm != fsm |`> |` ev->event != event) {
  926. status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
  927. goto the_end;
  928. }
  929. }
  930. else if (ev->event) {
  931. status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
  932. goto the_end;
  933. }
  934. }
  935. else {
  936. fsm = ev->fsm;
  937. event = ev->event;
  938. }
  939. // update transport data
  940. if (ev->from) {
  941. free_identity(session->«yml:lcase(@name)»_state.transport.from);
  942. session->«yml:lcase(@name)»_state.transport.from = ev->from;
  943. ev->from = NULL;
  944. }
  945. if (ev->sender_fpr) {
  946. free(session->«yml:lcase(@name)»_state.transport.sender_fpr);
  947. session->«yml:lcase(@name)»_state.transport.sender_fpr = ev->sender_fpr;
  948. /* Removed for temp ENGINE-647 fix. Will be reenabled once better sync debugging is in.
  949. // Check against saved comm_partner sender_fpr state, if there is one yet
  950. if (session->«yml:lcase(@name)»_state.comm_partner.sender_fpr) {
  951. // 1. Does it match sender_fpr?
  952. if (strcasecmp(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr, ev->sender_fpr) != 0) {
  953. // 2. If not, is it a group key?
  954. bool is_own_key = false;
  955. status = own_key_is_listed(session, ev->sender_fpr, &is_own_key);
  956. if (status)
  957. goto the_end;
  958. if (!is_own_key) {
  959. status = PEP_ILLEGAL_VALUE;
  960. goto the_end;
  961. }
  962. }
  963. }
  964. */
  965. ev->sender_fpr = NULL;
  966. }
  967. // update own identities
  968. if (ev->own_identities && ev->own_identities->ident) {
  969. free_identity_list(session->«yml:lcase(@name)»_state.own.identities);
  970. session->«yml:lcase(@name)»_state.own.identities = ev->own_identities;
  971. ev->own_identities = NULL;
  972. }
  973. status = «@name»_driver(session, fsm, event);
  974. the_end:
  975. //free_«@name»_event(ev); // FIXME: We don't own this pointer. Are we sure it gets freed externally?
  976. return status;
  977. }
  978. ||
  979. }
  980. apply "fsm", 0, mode=gen;
  981. }
  982. template "fsm", mode=send2 {
  983. ||
  984. case Sync_PR_«yml:lcase(@name)»: {
  985. switch (message_type) {
  986. ||
  987. if "message[@type='broadcast']"
  988. ||
  989. // these messages are being broadcasted
  990. `` for "message[@type='broadcast']" |>> case «../@name»_PR_«yml:mixedCase(@name)»:
  991. status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(../@name)»);
  992. if (status)
  993. goto the_end;
  994. if (!(channels && channels->ident)) {
  995. // status = PEP_«yml:ucase(../@name)»_NO_CHANNEL;
  996. // we don't check for having a channel, because if
  997. // this is initial setup before having an own
  998. // identity we're fine
  999. goto the_end;
  1000. }
  1001. break;
  1002. ||
  1003. ||
  1004. // these go anycast; previously used address is sticky (unicast)
  1005. `` for "message[@type='anycast' and @security!='ignore']" |>> case «../@name»_PR_«yml:mixedCase(@name)»:
  1006. // if we have a comm_partner fixed send it there
  1007. if (session->«yml:lcase(../@name)»_state.comm_partner.identity) {
  1008. pEp_identity *channel = identity_dup(session->«yml:lcase(../@name)»_state.comm_partner.identity);
  1009. if (!channel) {
  1010. status = PEP_OUT_OF_MEMORY;
  1011. goto the_end;
  1012. }
  1013. channels = new_identity_list(channel);
  1014. if (!channels) {
  1015. status = PEP_OUT_OF_MEMORY;
  1016. goto the_end;
  1017. }
  1018. }
  1019. // if we can reply just do
  1020. else if (session->«yml:lcase(../@name)»_state.transport.from) {
  1021. pEp_identity *channel = identity_dup(session->«yml:lcase(../@name)»_state.transport.from);
  1022. if (!channel) {
  1023. status = PEP_OUT_OF_MEMORY;
  1024. goto the_end;
  1025. }
  1026. channels = new_identity_list(channel);
  1027. if (!channels) {
  1028. status = PEP_OUT_OF_MEMORY;
  1029. goto the_end;
  1030. }
  1031. }
  1032. // real anycast, send it to the first matching
  1033. else {
  1034. status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(../@name)»);
  1035. if (status)
  1036. goto the_end;
  1037. if (!channels)
  1038. goto the_end;
  1039. if (channels->next) {
  1040. free_identity_list(channels->next);
  1041. channels->next = NULL;
  1042. }
  1043. }
  1044. break;
  1045. default:
  1046. status = PEP_«yml:ucase(../@name)»_ILLEGAL_MESSAGE;
  1047. goto the_end;
  1048. }
  1049. }
  1050. break;
  1051. ||
  1052. }
  1053. template "fsm[message/@security='ignore' or message/@ratelimit>0]", mode=send {
  1054. ||
  1055. case Sync_PR_«yml:lcase(@name)»: {
  1056. ||
  1057. if "message[@security='ignore']" {
  1058. ||
  1059. // ignore
  1060. switch (message_type) {
  1061. ||
  1062. for "message[@security='ignore']" {
  1063. |>> case «../@name»_PR_«yml:mixedCase(@name)»:
  1064. |>>> return PEP_STATUS_OK;
  1065. }
  1066. ||
  1067. default:
  1068. break;
  1069. }
  1070. ||
  1071. }
  1072. if "message[@ratelimit>0]" {
  1073. ||
  1074. // test if a message with a rate limit was just sent; in case drop
  1075. switch (message_type) {
  1076. ||
  1077. for "message[@ratelimit>0]"
  1078. ||
  1079. case «../@name»_PR_«yml:mixedCase(@name)»:
  1080. if (now < session->«yml:lcase(../../@name)»_state.own.last_«../@name»_«@name» + «@ratelimit»)
  1081. return PEP_STATUS_OK;
  1082. break;
  1083. ||
  1084. if "message[@ratelimit>0]"
  1085. ||
  1086. default:
  1087. break;
  1088. }
  1089. break;
  1090. ||
  1091. ||
  1092. default:
  1093. break;
  1094. }
  1095. ||
  1096. }
  1097. }
  1098. template "fsm", mode=timeout
  1099. ||
  1100. /**
  1101. * <!-- _«@name»_timeout() -->
  1102. *
  1103. * @internal
  1104. *
  1105. * @brief Determine if «@name» state machine has timed out
  1106. * (by hanging in the same state too long,
  1107. * exceeding «yml:ucase(@name)»_THRESHOLD)
  1108. *
  1109. * @param[in] state current state
  1110. *
  1111. * @retval true if wait has exceeded «yml:ucase(@name)»_THRESHOLD
  1112. * false otherwise
  1113. */
  1114. static bool _«@name»_timeout(int state)
  1115. {
  1116. static int last_state = None;
  1117. static time_t switch_time = 0;
  1118. if (state > Init) {
  1119. if (state == last_state) {
  1120. if (time(NULL) - switch_time > «yml:ucase(@name)»_THRESHOLD) {
  1121. last_state = None;
  1122. switch_time = 0;
  1123. return true;
  1124. }
  1125. }
  1126. else {
  1127. last_state = state;
  1128. switch_time = time(NULL);
  1129. }
  1130. }
  1131. else {
  1132. last_state = None;
  1133. switch_time = 0;
  1134. }
  1135. return false;
  1136. }
  1137. ||
  1138. template "fsm", mode=reset_state_machine
  1139. ||
  1140. case «../@name»_PR_«yml:lcase(@name)»: {
  1141. int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
  1142. switch (state) {
  1143. `` for "state[@name!='InitState' and @timeout != 'off']" |>>> case «@name»:
  1144. if (_«@name»_timeout(state)) {
  1145. session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = Init;
  1146. event = Init;
  1147. `` if "@threshold > 0" |>>>>> «@name»TimeoutHandler(session);
  1148. }
  1149. break;
  1150. default:
  1151. _«@name»_timeout(None);
  1152. }
  1153. break;
  1154. }
  1155. ||
  1156. template "fsm", mode=signal_message
  1157. {
  1158. ||
  1159. case «../@name»_PR_«yml:lcase(@name)»:
  1160. switch (msg->choice.«yml:lcase(@name)».present) {
  1161. ||
  1162. for "message[@security='unencrypted']" {
  1163. if "position()=1" |>> // these messages require a detached signature
  1164. ||
  1165. case «../@name»_PR_«yml:mixedCase(@name)»:
  1166. if (!sender_fpr) {
  1167. status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1168. goto the_end;
  1169. }
  1170. event = «@name»;
  1171. break;
  1172. ||
  1173. }
  1174. for "message[@security='untrusted']" {
  1175. if "position()=1" |>> // these messages must arrive encrypted
  1176. ||
  1177. case «../@name»_PR_«yml:mixedCase(@name)»:
  1178. if (rating < PEP_rating_reliable) {
  1179. status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1180. goto the_end;
  1181. }
  1182. event = «@name»;
  1183. break;
  1184. ||
  1185. }
  1186. for "message[@security!='unencrypted' and @security!='untrusted' and @security!='ignore']" {
  1187. if "position()=1" |>> // these messages must come through a trusted channel
  1188. ||
  1189. case «../@name»_PR_«yml:mixedCase(@name)»:
  1190. if (rating < PEP_rating_trusted) {
  1191. status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1192. goto the_end;
  1193. }
  1194. status = own_key_is_listed(session, sender_fpr, &is_own_key);
  1195. if (status)
  1196. goto the_end;
  1197. if (!is_own_key) {
  1198. status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1199. goto the_end;
  1200. }
  1201. event = «@name»;
  1202. break;
  1203. ||
  1204. }
  1205. for "message[@security='ignore']"
  1206. ||
  1207. case «../@name»_PR_«yml:mixedCase(@name)»:
  1208. free_«../../@name»_message(msg);
  1209. return PEP_STATUS_OK;
  1210. ||
  1211. ||
  1212. default:
  1213. status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1214. goto the_end;
  1215. }
  1216. break;
  1217. ||
  1218. }
  1219. template "fsm", mode=event
  1220. {
  1221. ||
  1222. case «../@name»_PR_«yml:lcase(@name)»: {
  1223. switch (msg->choice.«yml:lcase(@name)».present) {
  1224. ||
  1225. for "message"
  1226. ||
  1227. case «../@name»_PR_«yml:mixedCase(@name)»:
  1228. ev->event = «@name»;
  1229. break;
  1230. ||
  1231. ||
  1232. default:
  1233. // unknown message type
  1234. free(ev);
  1235. return NULL;
  1236. }
  1237. break;
  1238. }
  1239. ||
  1240. }
  1241. template "fsm", mode=driver
  1242. ||
  1243. case «../@name»_PR_«yml:lcase(@name)»: {
  1244. int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
  1245. next_state = fsm_«@name»(session, state, event);
  1246. if (next_state > None) {
  1247. session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
  1248. event = Init;
  1249. }
  1250. else if (next_state < None) {
  1251. return PEP_STATEMACHINE_ERROR - next_state;
  1252. }
  1253. break;
  1254. }
  1255. ||
  1256. template "fsm", mode=gen {
  1257. if "count(state)>0" document "generated/{@name}_fsm.h", "text" {
  1258. ||
  1259. /**
  1260. * @file «@name»_fsm.h
  1261. * @brief Finite state machine states, definitions, and structs for the «@name» protocol.
  1262. * @generated from ../sync/gen_statemachine.ysl2
  1263. *
  1264. * @license GNU General Public License 3.0 - see LICENSE.txt
  1265. */
  1266. #ifndef «yml:ucase(@name)»_FSM_H
  1267. #define «yml:ucase(@name)»_FSM_H
  1268. #include "«../@name»_impl.h"
  1269. #ifdef __cplusplus
  1270. extern "C" {
  1271. #endif
  1272. ///////////////////////////////////////////////////////////////////////////////////////////
  1273. // state machine for «@name»
  1274. ///////////////////////////////////////////////////////////////////////////////////////////
  1275. // states
  1276. typedef enum _«@name»_state {
  1277. «@name»_state_None = None,
  1278. «@name»_state_Init = Init,
  1279. ||
  1280. for "func:distinctName(state[not(@name='InitState')])"
  1281. |> «@name»`if "position()!=last()" > , `
  1282. ||
  1283. } «@name»_state;
  1284. // events
  1285. typedef enum _«@name»_event {
  1286. «@name»_event_Timeout = None,
  1287. «@name»_event_Init = Init,
  1288. ||
  1289. for "message" {
  1290. const "name", "@name";
  1291. |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
  1292. }
  1293. |> «@name»_event_Extra = Extra,
  1294. for "external" {
  1295. if "@id < 128"
  1296. error > external «@name» must have ID >= 128 but it's «@id»
  1297. |> «@name» = «@id»,
  1298. }
  1299. for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
  1300. if "@name!='Init'"
  1301. |> «@name»`if "position()!=last()" > , `
  1302. }
  1303. ||
  1304. } «@name»_event;
  1305. // state machine
  1306. #ifndef NDEBUG
  1307. /**
  1308. * <!-- «@name»_state_name() -->
  1309. *
  1310. * @brief Convert «@name» state to string
  1311. *
  1312. * @param[in] state state to convert to string
  1313. *
  1314. * @retval string representation of state name
  1315. * NULL if invalid state
  1316. */
  1317. const char *«@name»_state_name(int state);
  1318. /**
  1319. * <!-- «@name»_event_name() -->
  1320. *
  1321. * @brief Convert «@name» event to string
  1322. *
  1323. * @param[in] event event to convert to string
  1324. *
  1325. * @retval string representation of event name
  1326. * NULL if invalid event
  1327. */
  1328. const char *«@name»_event_name(int event);
  1329. #endif
  1330. /**
  1331. * <!-- fsm_«@name» -->
  1332. *
  1333. * @brief Given the current state of a «@name» state machine, determine if
  1334. * a received «@name» event will cause a state transition
  1335. * and, if so, return the next state.
  1336. *
  1337. * @param[in] session session state machine is associated with
  1338. * @param[in] state current state
  1339. * @param[in] event incoming event to evaluate
  1340. *
  1341. * @retval next_state if event causes a state transition
  1342. * @retval None if state machine should remain in the same state
  1343. *
  1344. */
  1345. «@name»_state fsm_«@name»(
  1346. PEP_SESSION session,
  1347. «@name»_state state,
  1348. «@name»_event event
  1349. );
  1350. #ifdef __cplusplus
  1351. }
  1352. #endif
  1353. #endif
  1354. ||
  1355. }
  1356. if "count(state)>0" document "generated/{@name}_fsm.c", "text" {
  1357. ||
  1358. /**
  1359. * @file «@name»_fsm.c
  1360. * @brief Finite state machine implementation for the «@name» protocol.
  1361. * @generated from ../sync/gen_statemachine.ysl2
  1362. *
  1363. * @license GNU General Public License 3.0 - see LICENSE.txt
  1364. */
  1365. #include "«@name»_fsm.h"
  1366. #include <stdlib.h>
  1367. #ifdef NDEBUG
  1368. static
  1369. #endif
  1370. const char *«@name»_state_name(int state)
  1371. {
  1372. switch (state) {
  1373. case End:
  1374. return "End";
  1375. case None:
  1376. return "None";
  1377. case Init:
  1378. return "InitState";
  1379. ||
  1380. for "func:distinctName(state[not(@name='InitState')])" {
  1381. |>> case «@name»:
  1382. |>>> return "«@name»";
  1383. }
  1384. ||
  1385. default:
  1386. assert(0);
  1387. return "unknown state";
  1388. }
  1389. }
  1390. #ifdef NDEBUG
  1391. static
  1392. #endif
  1393. const char *«@name»_event_name(int event)
  1394. {
  1395. switch (event) {
  1396. case None:
  1397. return "Timeout";
  1398. case Init:
  1399. return "Init";
  1400. ||
  1401. for "func:distinctName(state/event[not(@name='Init')]|message)" {
  1402. |>> case «@name»:
  1403. |>>> return "«@name»";
  1404. }
  1405. ||
  1406. default:
  1407. assert(0);
  1408. return "unknown event";
  1409. }
  1410. }
  1411. /**
  1412. * <!-- _str() -->
  1413. *
  1414. * @internal
  1415. *
  1416. * @brief Convert an integer to a string representation
  1417. *
  1418. * @param[in] n integer to convert
  1419. * @param[in] hex true if it should be in hex representation,
  1420. * false for decimal
  1421. *
  1422. * @retval string representation of input
  1423. * NULL if out of memory or input invalid
  1424. */
  1425. static char *_str(int n, bool hex)
  1426. {
  1427. char *buf = calloc(1, 24);
  1428. assert(buf);
  1429. if (!buf)
  1430. return NULL;
  1431. if (hex)
  1432. snprintf(buf, 24, "%.4x", n);
  1433. else
  1434. snprintf(buf, 24, "%d", n);
  1435. return buf;
  1436. }
  1437. #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
  1438. /**
  1439. * <!-- _«@name»_ERR_LOG_int() -->
  1440. *
  1441. * @internal
  1442. *
  1443. * @brief Write a «@name» error to log with integer input as description
  1444. *
  1445. * @param[in] session relevant session
  1446. * @param[in] t C string event name
  1447. * @param[in] n integer to log (e.g. error status)
  1448. * @param[in] hex true if integer should be logged in hex format,
  1449. * false if decimal
  1450. *
  1451. * @retval PEP_OUT_OF_MEMORY if out of memory
  1452. * @retval PEP_ILLEGAL_VALUE if input error during logging
  1453. * @retval PEP_STATUS_OK otherwise
  1454. */
  1455. __attribute__((__unused__))
  1456. static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
  1457. {
  1458. char *_buf = _str(n, hex);
  1459. if (!_buf)
  1460. return PEP_OUT_OF_MEMORY;
  1461. PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
  1462. free(_buf);
  1463. return status;
  1464. }
  1465. #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
  1466. #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
  1467. #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
  1468. «@name»_state fsm_«@name»(
  1469. PEP_SESSION session,
  1470. «@name»_state state,
  1471. «@name»_event event
  1472. )
  1473. {
  1474. assert(session);
  1475. if (!session)
  1476. return invalid_state;
  1477. if ((int) state == None)
  1478. state = «@name»_state_Init;
  1479. switch (state) {
  1480. `` apply "state", 2, mode=fsm
  1481. default:
  1482. «@name»_ERR_LOG("invalid state", «@name»_state_name(state));
  1483. assert(0);
  1484. return invalid_state;
  1485. }
  1486. return None;
  1487. }
  1488. ||
  1489. }
  1490. }
  1491. template "state", mode=fsm {
  1492. choose {
  1493. when "@name='InitState'" | case «../@name»_state_Init:
  1494. otherwise | case «@name»:
  1495. }
  1496. ||
  1497. «../@name»_SERVICE_LOG("in state", "«@name»");
  1498. switch (event) {
  1499. case None:
  1500. // received Timeout event, ignoring
  1501. break;
  1502. ||
  1503. if "not(event[@name='Init'])"
  1504. ||
  1505. case Init:
  1506. «../@name»_SERVICE_LOG("received Init but nothing to do", "Init");
  1507. break;
  1508. ||
  1509. ||
  1510. `` apply "event", 2, mode=fsm
  1511. default:
  1512. // ignore events not handled here
  1513. «../@name»_SERVICE_LOG("ignoring event", «../@name»_event_name(event));
  1514. return invalid_event;
  1515. }
  1516. break;
  1517. ||
  1518. }
  1519. template "event", mode=fsm {
  1520. | case «@name»: {
  1521. if "condition|action|send" |> PEP_STATUS status;
  1522. if "condition" |> bool result = false;
  1523. if "condition|action|send" |
  1524. ||
  1525. «../../@name»_SERVICE_LOG("received event", "«@name»");
  1526. `` apply "transition|action|condition|else|send|debug";
  1527. ||
  1528. if "name(*[last()])!='transition'" {
  1529. |
  1530. |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
  1531. |> break;
  1532. }
  1533. ||
  1534. }
  1535. ||
  1536. }
  1537. template "transition" {
  1538. const "fsm", "ancestor::fsm";
  1539. ||
  1540. «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
  1541. return «@target»;
  1542. ||
  1543. }
  1544. template "send" {
  1545. const "fsm", "ancestor::fsm";
  1546. const "protocol", "ancestor::protocol";
  1547. ||
  1548. «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
  1549. status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»_PR_«yml:mixedCase(@name)»);
  1550. if (status == PEP_OUT_OF_MEMORY)
  1551. return out_of_memory;
  1552. if (status) {
  1553. «$fsm/@name»_ERR_LOG_HEX("sending «@name» failed", status);
  1554. return cannot_send;
  1555. }
  1556. ||
  1557. }
  1558. template "debug"
  1559. | KeySync_SERVICE_LOG("«.»", "«ancestor::protocol/@name»");
  1560. template "action" {
  1561. const "fsm", "ancestor::fsm";
  1562. ||
  1563. «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
  1564. status = «@name»(session);
  1565. if (status == PEP_OUT_OF_MEMORY)
  1566. return out_of_memory;
  1567. if (status) {
  1568. «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
  1569. assert(0);
  1570. return invalid_action;
  1571. }
  1572. ||
  1573. }
  1574. template "condition" {
  1575. const "fsm", "ancestor::fsm";
  1576. ||
  1577. status = «@name»(session, &result);
  1578. if (status == PEP_OUT_OF_MEMORY)
  1579. return out_of_memory;
  1580. if (status) {
  1581. «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
  1582. assert(0);
  1583. return invalid_condition;
  1584. }
  1585. if (result) {
  1586. «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
  1587. ||
  1588. apply "transition|action|condition|else|send|debug";
  1589. | }
  1590. }
  1591. template "else" {
  1592. if "not(name(preceding-sibling::*[1]) = 'condition')"
  1593. error "else without if";
  1594. | else {
  1595. |> «ancestor::fsm/@name»_SERVICE_LOG("condition does not apply", "«preceding-sibling::*[last()]/@name»");
  1596. apply "transition|action|condition|else|send|debug";
  1597. | }
  1598. }
  1599. }