|
|
- // This file is under GNU General Public License 3.0
- // see LICENSE.txt
-
- // generate state machine code
-
- // Copyleft (c) 2016-2020, p≡p foundation
-
- // Written by Volker Birk
-
- include yslt.yml2
-
- tstylesheet {
- include standardlib.ysl2
- include ./functions.ysl2
-
- template "/protocol" {
- if "count(fsm/state)>0" document "generated/{@name}_event.h", "text"
- ||
- /**
- * @file «@name»_event.h
- * @brief Structures and functions for «@name» events.
- * @generated from ../sync/gen_statemachine.ysl2
- *
- * @license GNU General Public License 3.0 - see LICENSE.txt
- */
-
- #ifndef «yml:ucase(@name)»_EVENT_H
- #define «yml:ucase(@name)»_EVENT_H
-
- #include "pEpEngine.h"
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- /**
- * «@name» is also defined as «yml:ucase(@name)» for ASN.1 reasons and
- * forward declaration to avoid the need for header inclusion everywhere.
- * @see struct «@name» in «@name».h
- */
- typedef struct «@name» «yml:ucase(@name)»;
- /** forward declaration - @see enum «@name»_PR in «@name».h */
- typedef int «yml:ucase(@name)»_PR;
-
- /**
- * Data for an event related to a message
- */
- typedef struct «@name»_event {
- // state machine data
- «yml:ucase(@name)»_PR fsm; /*!< state machine type associated with this event */
- int event; /*!< type of event */
- «yml:ucase(@name)» *msg; /*!< the «@name» messaged caused by (or causing???) this event */
-
- // transport data
- pEp_identity *from; /*!< identity of the message sender */
- char *sender_fpr; /*!< fpr of key used by the message sender */
-
- identity_list *own_identities; /*!< List of our own identities */
- } «@name»_event_t;
-
- /**
- * <!-- new_«@name»_event() -->
- *
- * @brief allocate a new «@name»_event
- *
- * @param[in] fsm finite state machine the event is for
- * @param[in] event event or None
- * @param[in] msg message to compute event from
- *
- * @retval pointer to new event
- * @retval NULL in case of failure
- *
- */
- DYNAMIC_API «@name»_event_t *new_«@name»_event(«yml:ucase(@name)»_PR fsm, int event, «yml:ucase(@name)» *msg);
-
- #define «yml:ucase(@name)»_TIMEOUT_EVENT new_«@name»_event(«@name»_PR_NOTHING, 0, NULL);
-
- /**
- * <!-- free_«@name»_event() -->
- *
- * @brief free memory occupied by event
- *
- * @param[in] ev event to free
- *
- */
- DYNAMIC_API void free_«@name»_event(«@name»_event_t *ev);
-
-
- #ifdef __cplusplus
- }
- #endif
- #endif
-
- ||
-
- if "count(fsm/state)>0" document "generated/{@name}_event.c", "text"
- ||
-
- /**
- * @file «@name»_event.c
- * @brief Allocation, fsm drivers, and handling for «@name» events.
- * @generated from ../sync/gen_statemachine.ysl2
- *
- * @license GNU General Public License 3.0 - see LICENSE.txt
- */
-
- #include "platform.h"
-
- #include "pEp_internal.h"
- #include "«@name»_event.h"
- #include "«@name»_func.h"
- `` for "fsm" | #include "«@name»_fsm.h"
-
- DYNAMIC_API «@name»_event_t *new_«@name»_event(«yml:ucase(@name)»_PR fsm, int event, «@name»_t *msg)
- {
- «@name»_event_t *ev = («@name»_event_t *) calloc(1, sizeof(«@name»_event_t));
- assert(ev);
- if (!ev)
- return NULL;
-
- ev->fsm = fsm;
- ev->event = event;
- ev->msg = msg;
-
- if (msg) {
- switch (fsm) {
- `` apply "fsm", 3, mode=event
- default:
- // unknown protocol
- free(ev);
- return NULL;
- }
- }
-
- return ev;
- }
-
- DYNAMIC_API void free_«@name»_event(«@name»_event_t *ev)
- {
- if (ev) {
- free_identity_list(ev->own_identities);
- free_«@name»_message(ev->msg);
- free_identity(ev->from);
- free(ev->sender_fpr);
- free(ev);
- }
- }
-
- ||
-
- if "count(fsm/state)>0" document "generated/{@name}_impl.h", "text" {
- ||
- /**
- * @file «@name»_impl.h
- * @brief «@name» protocol implementation declarations
- * @generated from ../sync/gen_statemachine.ysl2
- *
- * @license GNU General Public License 3.0 - see LICENSE.txt
- */
-
- #ifndef «yml:ucase(@name)»_IMPL_H
- #define «yml:ucase(@name)»_IMPL_H
-
- #include "fsm_common.h"
- #include "«@name»_event.h"
- #include "message_api.h"
- #include "../asn.1/«@name».h"
-
- #define «yml:ucase(@name)»_THRESHOLD «@threshold»
- `` for "fsm[count(state)>0]" | #define «yml:ucase(@name)»_THRESHOLD «@threshold»
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- /////////////////////////////////////////////////////////////////////
- // conditions
- /////////////////////////////////////////////////////////////////////
-
- ||
- for "func:distinctName(*//condition)" {
- | /**
- | * @brief Evaluate condition: Is «@name» true?
- | * @param[in] session the session
- | * @param[out] result true if «@name», else false
- | * @retval status
- | */
- | PEP_STATUS «@name»(PEP_SESSION session, bool * result);
- }
- ||
-
- /////////////////////////////////////////////////////////////////////
- // actions
- /////////////////////////////////////////////////////////////////////
-
- ||
- for "func:distinctName(*//action)" {
- | /**
- | * @brief Action: Do «@name»
- | * @param[in] session the session
- | * @retval status
- | */
- | PEP_STATUS «@name»(PEP_SESSION session);
- }
- ||
-
- /////////////////////////////////////////////////////////////////////
- // timeout handlers
- /////////////////////////////////////////////////////////////////////
-
- ||
- for "fsm[@threshold > 0]" {
- | /**
- | * <!-- «@name»TimeoutHandler() -->
- | *
- | * @brief Handle timeouts for «@name» state machine
- | *
- | * @param[in] session the session
- | * @retval status
- | */
- | PEP_STATUS «@name»TimeoutHandler(PEP_SESSION session);
- }
- ||
-
-
- /////////////////////////////////////////////////////////////////////
- // «@name» state machine driver, message sending, and event receipt
- /////////////////////////////////////////////////////////////////////
-
- /**
- * <!-- send_«@name»_message() -->
- *
- * @brief send message about «@name» event to communication partners using state
- *
- * @param[in] session the session
- * @param[in] fsm the finite state machine from which to get state
- * @param[in] message_type type of message to send
- *
- * @retval status of message send
- *
- */
- PEP_STATUS send_«@name»_message(
- PEP_SESSION session,
- «@name»_PR fsm,
- int message_type
- );
-
- /**
- * <!-- recv_«@name»_event() -->
- *
- * @brief receive «@name» message and store it in state
- *
- * @param[in] session the session
- * @param[in] ev the event to process
- *
- * @retval status of event processing/storage
- *
- */
- PEP_STATUS recv_«@name»_event(
- PEP_SESSION session,
- «@name»_event_t *ev
- );
-
- /**
- * <!-- «@name»_driver() -->
- *
- * @brief «@name» state machine driver
- *
- * @param[in] session the session
- * @param[in] fsm finite state machine to drive
- * @param[in] ev the event to process???
- *
- * @retval ???
- *
- * @note if fsm or event set to 0 use fields in src if present
- *
- */
- PEP_STATUS «@name»_driver(
- PEP_SESSION session,
- «@name»_PR fsm,
- int event
- );
-
- /////////////////////////////////////////////////////////////////////////////
- // API used by the engine internally //
- /////////////////////////////////////////////////////////////////////////////
-
- /**
- * <!-- signal_«@name»_event() -->
- *
- * @brief Internal engine API: call this if you need to signal an external «@name» event
- *
- * @param[in] session the session
- * @param[in] fsm finite state machine
- * @param[in] event event type
- * @param[out] own_identities list of own identities
- *
- * @retval status
- *
- * @ownership the ownership of own_identities goes to the callee
- *
- */
- PEP_STATUS signal_«@name»_event(
- PEP_SESSION session,
- «@name»_PR fsm,
- int event,
- identity_list *own_identities
- );
-
- /**
- * <!-- signal_«@name»_message() -->
- *
- * @brief Internal engine API: to be called by transports receiving a «@name» message
- *
- * @param[in] session the session
- * @param[in] rating rating of the decrypted «@name» message
- * @param[out] data payload of «@name» message
- * @param[out] from identity of the message sender
- * @param[out] fpr fingerprint of the sender key used to sign the message
- *
- * @retval status
- *
- */
- PEP_STATUS signal_«@name»_message(
- PEP_SESSION session,
- PEP_rating rating,
- const char *data,
- size_t size,
- const pEp_identity *from,
- const char *sender_fpr
- );
-
- #ifdef __cplusplus
- }
- #endif
- #endif
-
- ||
- }
-
- if "count(fsm/state)>0" document "generated/{@name}_impl.c", "text" {
- ||
- /**
- * @file «@name»_impl.c
- * @brief «@name» protocol implementation: driver, event handling, message signalling/sending/receipt, etc.
- * @generated from ../sync/gen_statemachine.ysl2
- *
- * @license GNU General Public License 3.0 - see LICENSE.txt
- */
-
- #include "«@name»_impl.h"
- #include "pEp_internal.h"
- #include "«@name»_event.h"
- #include "«yml:lcase(@name)»_codec.h"
- #include "baseprotocol.h"
- #include "security_checks.h"
- `` for "fsm[count(state)>0]" | #include "«@name»_fsm.h"
-
- `` apply "fsm[count(state)>0]", 0, mode=timeout
- PEP_STATUS «@name»_driver(
- PEP_SESSION session,
- «@name»_PR fsm,
- int event
- )
- {
- assert(session);
- if (!session)
- return PEP_ILLEGAL_VALUE;
-
- switch (fsm) {
- case None:
- if (!event) {
- // timeout occured
- `` for "fsm[count(state)>0]" |>>>> «../@name»_driver(session, «../@name»_PR_«yml:lcase(@name)», None);
- return PEP_STATUS_OK;
- }
- return PEP_ILLEGAL_VALUE;
-
- `` apply "fsm[count(state)>0]", mode=reset_state_machine;
- default:
- return PEP_ILLEGAL_VALUE;
- }
-
- int next_state = None;
- do {
- switch (fsm) {
- `` apply "fsm[count(state)>0]", 3, mode=driver
- default:
- return PEP_ILLEGAL_VALUE;
- }
- } while (next_state);
-
- return PEP_STATUS_OK;
- }
-
- PEP_STATUS signal_«@name»_event(
- PEP_SESSION session,
- «@name»_PR fsm,
- int event,
- identity_list *own_identities
- )
- {
- «@name»_t *msg = NULL;
- «@name»_event_t *ev = NULL;
-
- assert(session && fsm > 0 && event > None);
- if (!(session && fsm > 0 && event > None))
- return PEP_ILLEGAL_VALUE;
-
- PEP_STATUS status = PEP_STATUS_OK;
-
- if (!session->inject_«yml:lcase(@name)»_event)
- return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
-
- if (event < Extra) {
- // FIXME: there should be a mapping between event and message type
- // with current implementation they've got an offset of 1
- msg = new_«@name»_message(fsm, event - 1);
- if (!msg) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
-
- status = update_«@name»_message(session, msg);
- if (status)
- goto the_end;
- }
-
- ev = new_«@name»_event(fsm, event, msg);
- if (!ev) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
-
- status = set_all_userids_to_own(session, own_identities);
- if (status != PEP_STATUS_OK)
- goto the_end;
-
- ev->own_identities = own_identities;
-
- int result = session->inject_«yml:lcase(@name)»_event(ev,
- session->«yml:lcase(@name)»_management);
- if (result) {
- status = PEP_STATEMACHINE_ERROR;
- goto the_end;
- }
- return PEP_STATUS_OK;
-
- the_end:
- free_«@name»_event(ev); // msg gets freed here
- return status;
- }
-
- PEP_STATUS signal_«@name»_message(
- PEP_SESSION session,
- PEP_rating rating,
- const char *data,
- size_t size,
- const pEp_identity *from,
- const char *sender_fpr
- )
- {
- assert(session && data && size);
- if (!(session && data && size))
- return PEP_ILLEGAL_VALUE;
-
- if (!session->inject_«yml:lcase(@name)»_event)
- return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
-
- PEP_STATUS status = PEP_STATUS_OK;
- «@name»_event_t *ev = NULL;
-
- «@name»_t *msg = NULL;
- status = decode_«@name»_message(data, size, &msg);
- if (status)
- return status;
-
- «@name»_PR fsm = msg->present;
- int event = 0;
- bool is_own_key = false;
-
- switch (fsm) {
- `` apply "fsm[count(state)>0]", 2, mode=signal_message
- default:
- status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
-
- ev = new_«@name»_event(fsm, event, msg);
- if (!ev) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
-
- // add transport data
-
- if (from) {
- ev->from = identity_dup(from);
- if (!ev->from) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- }
-
- if (sender_fpr) {
- ev->sender_fpr = strdup(sender_fpr);
- assert(ev->sender_fpr);
- if (!ev->sender_fpr) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- }
-
- int result = session->inject_«yml:lcase(@name)»_event(ev,
- session->«yml:lcase(@name)»_management);
- if (result) {
- status = PEP_STATEMACHINE_ERROR;
- goto the_end;
- }
-
- return PEP_STATUS_OK;
-
- the_end:
- free_«@name»_event(ev); // msg gets freed here
- return status;
- }
-
- PEP_STATUS send_«@name»_message(
- PEP_SESSION session,
- «@name»_PR fsm,
- int message_type
- )
- {
- PEP_STATUS status = PEP_STATUS_OK;
-
- assert(session && (int) fsm > None && message_type > None);
- if (!(session && (int) fsm > None && message_type > None))
- return PEP_ILLEGAL_VALUE;
-
- time_t now = time(NULL);
- switch (fsm) {
- ||
- apply "fsm[count(state)>0]", 2, mode=send;
- ||
- }
-
- «@name»_t *msg = new_«@name»_message(fsm, message_type);
- if (!msg)
- return PEP_OUT_OF_MEMORY;
-
- char *data = NULL;
- message *m = NULL;
- identity_list *channels = NULL;
- char *key_data = NULL;
- size_t key_data_size = 0;
- stringlist_t *extra = NULL;
- bool transaction;
-
- status = update_«@name»_message(session, msg);
- if (status)
- goto the_end;
-
- size_t size = 0;
- status = encode_«@name»_message(msg, &data, &size);
- if (status)
- goto the_end;
-
- // we never use this
- if (session->«yml:lcase(@name)»_state.comm_partner.identity
- && session->«yml:lcase(@name)»_state.comm_partner.identity->fpr) {
- free(session->«yml:lcase(@name)»_state.comm_partner.identity->fpr);
- session->«yml:lcase(@name)»_state.comm_partner.identity->fpr = NULL;
- }
-
- // if we have this we always use this
- if (session->«yml:lcase(@name)»_state.comm_partner.sender_fpr) {
- free(session->«yml:lcase(@name)»_state.transport.sender_fpr);
- session->«yml:lcase(@name)»_state.transport.sender_fpr =
- strdup(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
- assert(session->«yml:lcase(@name)»_state.transport.sender_fpr);
- if (!session->«yml:lcase(@name)»_state.transport.sender_fpr) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- }
-
- switch (fsm) {
- ||
- apply "fsm[count(state)>0]", 2, mode=send2;
- ||
- default:
- break;
- }
-
- for (identity_list *li = channels; li && li->ident ; li = li->next) {
- message *_m = NULL;
- char *_data = NULL;
-
- _data = malloc(size);
- assert(_data);
- if (!_data) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- memcpy(_data, data, size);
-
- switch (message_type) {
- `` for "fsm/message[@security='unencrypted' and ../@name!='KeySync']" | #error unencrypted only allowed with KeySync
- `` for "fsm/message[@security='unencrypted' and ../@name='KeySync']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
- status = try_base_prepare_message(
- session,
- li->ident,
- li->ident,
- BASE_SYNC,
- _data,
- size,
- li->ident->fpr,
- &_m
- );
- if (status) {
- free(_data);
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- continue;
- }
- attach_own_key(session, _m);
- decorate_message(session, _m, PEP_rating_undefined, NULL, true, true);
- m = _m;
- break;
-
- `` for "fsm/message[@security='untrusted']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
- // add fpr of key of comm partner
-
- if (!session->«yml:lcase(@name)»_state.transport.sender_fpr) {
- status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
- continue;
- }
-
- extra = new_stringlist(session->«yml:lcase(@name)»_state.transport.sender_fpr);
- if (!extra) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
-
- status = base_prepare_message(
- session,
- li->ident,
- li->ident,
- BASE_SYNC,
- _data,
- size,
- NULL,
- &_m
- );
- if (status) {
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- free(_data);
- continue;
- }
-
- status = try_encrypt_message(session, _m, extra, &m, PEP_enc_PEP, 0);
- if (status) {
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
- continue;
- }
- add_opt_field(m, "pEp-auto-consume", "yes");
- m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
- free_message(_m);
- break;
-
- // attach own keys for new member
- `` for "fsm/message[@security='attach_own_keys_for_new_member']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
- // check if we had a former negotiation
-
- transaction = false;
- for (int i=0; i < session->«yml:lcase(@name)»_state.own.negotiation.size; i++) {
- if (session->«yml:lcase(@name)»_state.own.negotiation.buf[i]) {
- transaction = true;
- break;
- }
- }
-
- // if it is a former negotiation check if the key
- // is fully trusted and the sender key of this
- // transaction; if so add the sender key to extra
- // keys allowing this new partner to read the
- // secret keys
-
- if (transaction) {
- if (!(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr &&
- session->«yml:lcase(@name)»_state.transport.from &&
- session->«yml:lcase(@name)»_state.transport.from->user_id))
- {
- status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
- continue;
- }
-
- // test if this is a green channel
-
- pEp_identity *ident = new_identity(NULL,
- session->«yml:lcase(@name)»_state.comm_partner.sender_fpr,
- session->«yml:lcase(@name)»_state.transport.from->user_id,
- NULL
- );
- if (!ident) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- status = get_trust(session, ident);
- if (status) {
- free_identity(ident);
- goto the_end;
- }
- assert(ident->comm_type == PEP_ct_pEp); // we don't deliver otherwise
- if (ident->comm_type != PEP_ct_pEp) {
- free_identity(ident);
- status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
- continue;
- }
- free_identity(ident);
-
- // test if we accepted this as own key already
-
- bool is_own_key = false;
- status = own_key_is_listed(session,
- session->«yml:lcase(@name)»_state.comm_partner.sender_fpr,
- &is_own_key);
- assert(!status);
- if (status)
- goto the_end;
- if (!is_own_key) {
- status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
- continue;
- }
-
- // if so add key of comm partner to extra keys
-
- extra = new_stringlist(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
- if (!extra) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- }
-
- status = base_prepare_message(
- session,
- li->ident,
- li->ident,
- BASE_SYNC,
- _data,
- size,
- NULL,
- &_m
- );
- if (status) {
- free(_data);
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- continue;
- }
-
- // export secret keys into memory
-
- key_data = strdup("");
- assert(key_data);
- if (!key_data) {
- free(_data);
- free_message(_m);
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- key_data_size = 1; // N.B. If null termination makes us happy for debugging, fine, but
- // if we include this in the size, libetpan will null terminate and
- // go bananas. We can't have a NUL in the mime text.
-
- for (stringlist_t *sl = session->«yml:lcase(@name)»_state.own.keys;
- sl && sl->value ; sl = sl->next)
- {
- char *_key_data = NULL;
- size_t _size = 0;
- status = export_secret_key(session, sl->value, &_key_data, &_size);
- if (status && status != PEP_KEY_NOT_FOUND) {
- free(_data);
- free_message(_m);
- goto the_end;
- }
-
- if (status != PEP_KEY_NOT_FOUND) {
- assert(_key_data && _size);
- char *n = realloc(key_data, key_data_size + _size);
- if (!n) {
- free(_data);
- free_message(_m);
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- key_data = n;
- key_data_size += _size;
- strlcat(key_data, _key_data, key_data_size);
- free(_key_data);
- _key_data = NULL;
- }
- status = export_key(session, sl->value, &_key_data, &_size);
- if (status && status != PEP_KEY_NOT_FOUND) {
- free(_data);
- free_message(_m);
- goto the_end;
- }
-
- if (status != PEP_KEY_NOT_FOUND) {
- assert(_key_data && _size);
- char *n = realloc(key_data, key_data_size + _size);
- if (!n) {
- free(_data);
- free_message(_m);
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- key_data = n;
- key_data_size += _size;
- strlcat(key_data, _key_data, key_data_size);
- free(_key_data);
- _key_data = NULL;
- }
- }
-
- // add secret key data as attachment
-
- // N.B. The -1 makes sure we do NOT add a NUL into the mime stream!
- bloblist_t *bl = bloblist_add(_m->attachments, key_data, key_data_size - 1,
- "application/octet-stream", "file://own.key");
- if (!bl) {
- free(_data);
- free_message(_m);
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- key_data = NULL;
-
- status = try_encrypt_message(session, _m, extra, &m, PEP_enc_PEP, 0);
- if (status) {
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
- continue;
- }
- add_opt_field(m, "pEp-auto-consume", "yes");
- m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
- free_message(_m);
- break;
-
- // attach own keys for group
- `` for "fsm/message[@security='attach_own_keys_for_group']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
- status = base_prepare_message(
- session,
- li->ident,
- li->ident,
- BASE_SYNC,
- _data,
- size,
- NULL,
- &_m
- );
- if (status) {
- free(_data);
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- continue;
- }
-
- // export secret keys into memory
-
- key_data = strdup("");
- assert(key_data);
- if (!key_data) {
- free(_data);
- free_message(_m);
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- key_data_size = 1; // N.B. If null termination makes us happy for debugging, fine, but
- // if we include this in the size, libetpan will null terminate and
- // go bananas. We can't have a NUL in the mime text.
-
- for (stringlist_t *sl = session->«yml:lcase(@name)»_state.own.keys;
- sl && sl->value ; sl = sl->next)
- {
- char *_key_data = NULL;
- size_t _size = 0;
- status = export_secret_key(session, sl->value, &_key_data, &_size);
- if (status && status != PEP_KEY_NOT_FOUND) {
- free(_data);
- free_message(_m);
- goto the_end;
- }
-
- if (status != PEP_KEY_NOT_FOUND) {
- assert(_key_data && _size);
- char *n = realloc(key_data, key_data_size + _size);
- if (!n) {
- free(_data);
- free_message(_m);
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- key_data = n;
- key_data_size += _size;
- strlcat(key_data, _key_data, key_data_size);
- free(_key_data);
- _key_data = NULL;
- }
- status = export_key(session, sl->value, &_key_data, &_size);
- if (status && status != PEP_KEY_NOT_FOUND) {
- free(_data);
- free_message(_m);
- goto the_end;
- }
-
- if (status != PEP_KEY_NOT_FOUND) {
- assert(_key_data && _size);
- char *n = realloc(key_data, key_data_size + _size);
- if (!n) {
- free(_data);
- free_message(_m);
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- key_data = n;
- key_data_size += _size;
- strlcat(key_data, _key_data, key_data_size);
- free(_key_data);
- _key_data = NULL;
- }
- }
-
- // add secret key data as attachment
-
- // N.B. The -1 makes sure we do NOT add a NUL into the mime stream!
- bl = bloblist_add(_m->attachments, key_data, key_data_size - 1,
- "application/octet-stream", "file://own.key");
- if (!bl) {
- free(_data);
- free_message(_m);
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- key_data = NULL;
-
- // we do not support extra keys here and will only encrypt to ourselves
- status = try_encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
- if (status) {
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
- continue;
- }
- add_opt_field(m, "pEp-auto-consume", "yes");
- m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
- free_message(_m);
- break;
-
- default: // security=trusted only
- status = base_prepare_message(
- session,
- li->ident,
- li->ident,
- BASE_SYNC,
- _data,
- size,
- NULL,
- &_m
- );
- if (status) {
- free(_data);
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- continue;
- }
-
- status = try_encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
- if (status) {
- status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
- if (status == PEP_OUT_OF_MEMORY)
- goto the_end;
- continue;
- }
- add_opt_field(m, "pEp-auto-consume", "yes");
- m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
- free_message(_m);
- }
-
- status = session->messageToSend(m);
- m = NULL;
- }
- ||
- if "fsm/message[@ratelimit>0]" {
- ||
-
- switch (fsm) {
- ||
- for "fsm[message/@ratelimit>0]" {
- ||
- case Sync_PR_«yml:lcase(@name)»:
- switch (message_type) {
- ||
- for "message[@ratelimit>0]"
- ||
- case «../@name»_PR_«yml:mixedCase(@name)»:
- session->«yml:lcase(../../@name)»_state.own.last_«../@name»_«@name» = now;
- break;
- ||
- ||
- default:
- break;
- }
- break;
-
- ||
- }
- ||
- default:
- break;
- }
- ||
- }
- ||
-
- the_end:
- free_stringlist(extra);
- free_identity_list(channels);
- free_message(m);
- free(data);
- free(key_data);
- free_«@name»_message(msg);
- if (status)
- SERVICE_ERROR_LOG(session, "send_«@name»_message()", status);
- return status;
- }
-
- PEP_STATUS recv_«@name»_event(
- PEP_SESSION session,
- «@name»_event_t *ev
- )
- {
- assert(session && ev);
- if (!(session && ev))
- return PEP_ILLEGAL_VALUE;
-
- PEP_STATUS status = PEP_STATUS_OK;
- «@name»_PR fsm = (int) None;
- int event = None;
-
- if (ev->event > None && ev->event < Extra) {
- status = update_«@name»_state(session, ev->msg, &fsm, &event);
- if (status)
- goto the_end;
-
- if (ev->fsm) {
- if (ev->fsm != fsm |`> |` ev->event != event) {
- status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
- }
- else if (ev->event) {
- status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
- }
- else {
- fsm = ev->fsm;
- event = ev->event;
- }
-
- // update transport data
-
- if (ev->from) {
- free_identity(session->«yml:lcase(@name)»_state.transport.from);
- session->«yml:lcase(@name)»_state.transport.from = ev->from;
- ev->from = NULL;
- }
-
- if (ev->sender_fpr) {
- free(session->«yml:lcase(@name)»_state.transport.sender_fpr);
- session->«yml:lcase(@name)»_state.transport.sender_fpr = ev->sender_fpr;
-
- /* Removed for temp ENGINE-647 fix. Will be reenabled once better sync debugging is in.
- // Check against saved comm_partner sender_fpr state, if there is one yet
- if (session->«yml:lcase(@name)»_state.comm_partner.sender_fpr) {
- // 1. Does it match sender_fpr?
- if (strcasecmp(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr, ev->sender_fpr) != 0) {
- // 2. If not, is it a group key?
- bool is_own_key = false;
- status = own_key_is_listed(session, ev->sender_fpr, &is_own_key);
- if (status)
- goto the_end;
- if (!is_own_key) {
- status = PEP_ILLEGAL_VALUE;
- goto the_end;
- }
- }
- }
- */
-
- ev->sender_fpr = NULL;
- }
-
- // update own identities
-
- if (ev->own_identities && ev->own_identities->ident) {
- free_identity_list(session->«yml:lcase(@name)»_state.own.identities);
- session->«yml:lcase(@name)»_state.own.identities = ev->own_identities;
- ev->own_identities = NULL;
- }
-
-
- status = «@name»_driver(session, fsm, event);
-
- the_end:
- //free_«@name»_event(ev); // FIXME: We don't own this pointer. Are we sure it gets freed externally?
- return status;
- }
-
- ||
- }
-
- apply "fsm", 0, mode=gen;
- }
-
- template "fsm", mode=send2 {
- ||
- case Sync_PR_«yml:lcase(@name)»: {
- switch (message_type) {
- ||
- if "message[@type='broadcast']"
- ||
- // these messages are being broadcasted
- `` for "message[@type='broadcast']" |>> case «../@name»_PR_«yml:mixedCase(@name)»:
- status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(../@name)»);
- if (status)
- goto the_end;
-
- if (!(channels && channels->ident)) {
- // status = PEP_«yml:ucase(../@name)»_NO_CHANNEL;
- // we don't check for having a channel, because if
- // this is initial setup before having an own
- // identity we're fine
- goto the_end;
- }
- break;
-
- ||
- ||
- // these go anycast; previously used address is sticky (unicast)
- `` for "message[@type='anycast' and @security!='ignore']" |>> case «../@name»_PR_«yml:mixedCase(@name)»:
- // if we have a comm_partner fixed send it there
- if (session->«yml:lcase(../@name)»_state.comm_partner.identity) {
- pEp_identity *channel = identity_dup(session->«yml:lcase(../@name)»_state.comm_partner.identity);
- if (!channel) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
-
- channels = new_identity_list(channel);
- if (!channels) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- }
- // if we can reply just do
- else if (session->«yml:lcase(../@name)»_state.transport.from) {
- pEp_identity *channel = identity_dup(session->«yml:lcase(../@name)»_state.transport.from);
- if (!channel) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
-
- channels = new_identity_list(channel);
- if (!channels) {
- status = PEP_OUT_OF_MEMORY;
- goto the_end;
- }
- }
- // real anycast, send it to the first matching
- else {
- status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(../@name)»);
- if (status)
- goto the_end;
- if (!channels)
- goto the_end;
-
- if (channels->next) {
- free_identity_list(channels->next);
- channels->next = NULL;
- }
- }
- break;
-
- default:
- status = PEP_«yml:ucase(../@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
- }
- break;
-
- ||
- }
-
- template "fsm[message/@security='ignore' or message/@ratelimit>0]", mode=send {
- ||
- case Sync_PR_«yml:lcase(@name)»: {
- ||
- if "message[@security='ignore']" {
- ||
- // ignore
- switch (message_type) {
- ||
- for "message[@security='ignore']" {
- |>> case «../@name»_PR_«yml:mixedCase(@name)»:
- |>>> return PEP_STATUS_OK;
- }
- ||
- default:
- break;
- }
- ||
- }
- if "message[@ratelimit>0]" {
- ||
-
- // test if a message with a rate limit was just sent; in case drop
- switch (message_type) {
- ||
- for "message[@ratelimit>0]"
- ||
- case «../@name»_PR_«yml:mixedCase(@name)»:
- if (now < session->«yml:lcase(../../@name)»_state.own.last_«../@name»_«@name» + «@ratelimit»)
- return PEP_STATUS_OK;
- break;
-
- ||
- if "message[@ratelimit>0]"
- ||
- default:
- break;
- }
- break;
-
- ||
- ||
- default:
- break;
- }
- ||
- }
- }
-
- template "fsm", mode=timeout
- ||
- /**
- * <!-- _«@name»_timeout() -->
- *
- * @internal
- *
- * @brief Determine if «@name» state machine has timed out
- * (by hanging in the same state too long,
- * exceeding «yml:ucase(@name)»_THRESHOLD)
- *
- * @param[in] state current state
- *
- * @retval true if wait has exceeded «yml:ucase(@name)»_THRESHOLD
- * false otherwise
- */
- static bool _«@name»_timeout(int state)
- {
- static int last_state = None;
- static time_t switch_time = 0;
-
- if (state > Init) {
- if (state == last_state) {
- if (time(NULL) - switch_time > «yml:ucase(@name)»_THRESHOLD) {
- last_state = None;
- switch_time = 0;
- return true;
- }
- }
- else {
- last_state = state;
- switch_time = time(NULL);
- }
- }
- else {
- last_state = None;
- switch_time = 0;
- }
-
- return false;
- }
-
- ||
-
- template "fsm", mode=reset_state_machine
- ||
- case «../@name»_PR_«yml:lcase(@name)»: {
- int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
- switch (state) {
- `` for "state[@name!='InitState' and @timeout != 'off']" |>>> case «@name»:
- if (_«@name»_timeout(state)) {
- session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = Init;
- event = Init;
- `` if "@threshold > 0" |>>>>> «@name»TimeoutHandler(session);
- }
- break;
-
- default:
- _«@name»_timeout(None);
- }
- break;
- }
-
- ||
-
- template "fsm", mode=signal_message
- {
- ||
- case «../@name»_PR_«yml:lcase(@name)»:
- switch (msg->choice.«yml:lcase(@name)».present) {
- ||
- for "message[@security='unencrypted']" {
- if "position()=1" |>> // these messages require a detached signature
- ||
- case «../@name»_PR_«yml:mixedCase(@name)»:
- if (!sender_fpr) {
- status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
- event = «@name»;
- break;
-
- ||
- }
- for "message[@security='untrusted']" {
- if "position()=1" |>> // these messages must arrive encrypted
- ||
- case «../@name»_PR_«yml:mixedCase(@name)»:
- if (rating < PEP_rating_reliable) {
- status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
- event = «@name»;
- break;
-
- ||
- }
- for "message[@security!='unencrypted' and @security!='untrusted' and @security!='ignore']" {
- if "position()=1" |>> // these messages must come through a trusted channel
- ||
- case «../@name»_PR_«yml:mixedCase(@name)»:
- if (rating < PEP_rating_trusted) {
- status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
- status = own_key_is_listed(session, sender_fpr, &is_own_key);
- if (status)
- goto the_end;
- if (!is_own_key) {
- status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
- event = «@name»;
- break;
-
- ||
- }
- for "message[@security='ignore']"
- ||
- case «../@name»_PR_«yml:mixedCase(@name)»:
- free_«../../@name»_message(msg);
- return PEP_STATUS_OK;
-
- ||
- ||
- default:
- status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
- goto the_end;
- }
- break;
-
- ||
- }
-
- template "fsm", mode=event
- {
- ||
- case «../@name»_PR_«yml:lcase(@name)»: {
- switch (msg->choice.«yml:lcase(@name)».present) {
- ||
- for "message"
- ||
- case «../@name»_PR_«yml:mixedCase(@name)»:
- ev->event = «@name»;
- break;
- ||
- ||
- default:
- // unknown message type
- free(ev);
- return NULL;
- }
- break;
- }
-
- ||
- }
-
- template "fsm", mode=driver
- ||
- case «../@name»_PR_«yml:lcase(@name)»: {
- int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
- next_state = fsm_«@name»(session, state, event);
- if (next_state > None) {
- session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
- event = Init;
- }
- else if (next_state < None) {
- return PEP_STATEMACHINE_ERROR - next_state;
- }
- break;
- }
-
- ||
-
- template "fsm", mode=gen {
- if "count(state)>0" document "generated/{@name}_fsm.h", "text" {
- ||
- /**
- * @file «@name»_fsm.h
- * @brief Finite state machine states, definitions, and structs for the «@name» protocol.
- * @generated from ../sync/gen_statemachine.ysl2
- *
- * @license GNU General Public License 3.0 - see LICENSE.txt
- */
-
- #ifndef «yml:ucase(@name)»_FSM_H
- #define «yml:ucase(@name)»_FSM_H
-
- #include "«../@name»_impl.h"
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // state machine for «@name»
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- // states
-
- typedef enum _«@name»_state {
- «@name»_state_None = None,
- «@name»_state_Init = Init,
- ||
- for "func:distinctName(state[not(@name='InitState')])"
- |> «@name»`if "position()!=last()" > , `
- ||
- } «@name»_state;
-
- // events
-
- typedef enum _«@name»_event {
- «@name»_event_Timeout = None,
- «@name»_event_Init = Init,
- ||
- for "message" {
- const "name", "@name";
- |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
- }
- |> «@name»_event_Extra = Extra,
- for "external" {
- if "@id < 128"
- error > external «@name» must have ID >= 128 but it's «@id»
- |> «@name» = «@id»,
- }
- for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
- if "@name!='Init'"
- |> «@name»`if "position()!=last()" > , `
- }
- ||
- } «@name»_event;
-
-
- // state machine
-
- #ifndef NDEBUG
- /**
- * <!-- «@name»_state_name() -->
- *
- * @brief Convert «@name» state to string
- *
- * @param[in] state state to convert to string
- *
- * @retval string representation of state name
- * NULL if invalid state
- */
- const char *«@name»_state_name(int state);
- /**
- * <!-- «@name»_event_name() -->
- *
- * @brief Convert «@name» event to string
- *
- * @param[in] event event to convert to string
- *
- * @retval string representation of event name
- * NULL if invalid event
- */
- const char *«@name»_event_name(int event);
- #endif
-
- /**
- * <!-- fsm_«@name» -->
- *
- * @brief Given the current state of a «@name» state machine, determine if
- * a received «@name» event will cause a state transition
- * and, if so, return the next state.
- *
- * @param[in] session session state machine is associated with
- * @param[in] state current state
- * @param[in] event incoming event to evaluate
- *
- * @retval next_state if event causes a state transition
- * @retval None if state machine should remain in the same state
- *
- */
- «@name»_state fsm_«@name»(
- PEP_SESSION session,
- «@name»_state state,
- «@name»_event event
- );
-
- #ifdef __cplusplus
- }
- #endif
- #endif
-
- ||
- }
-
- if "count(state)>0" document "generated/{@name}_fsm.c", "text" {
- ||
- /**
- * @file «@name»_fsm.c
- * @brief Finite state machine implementation for the «@name» protocol.
- * @generated from ../sync/gen_statemachine.ysl2
- *
- * @license GNU General Public License 3.0 - see LICENSE.txt
- */
-
- #include "«@name»_fsm.h"
- #include <stdlib.h>
-
- #ifdef NDEBUG
- static
- #endif
- const char *«@name»_state_name(int state)
- {
- switch (state) {
- case End:
- return "End";
- case None:
- return "None";
- case Init:
- return "InitState";
- ||
- for "func:distinctName(state[not(@name='InitState')])" {
- |>> case «@name»:
- |>>> return "«@name»";
- }
- ||
- default:
- assert(0);
- return "unknown state";
- }
- }
-
- #ifdef NDEBUG
- static
- #endif
- const char *«@name»_event_name(int event)
- {
- switch (event) {
- case None:
- return "Timeout";
- case Init:
- return "Init";
- ||
- for "func:distinctName(state/event[not(@name='Init')]|message)" {
- |>> case «@name»:
- |>>> return "«@name»";
- }
- ||
- default:
- assert(0);
- return "unknown event";
- }
- }
-
- /**
- * <!-- _str() -->
- *
- * @internal
- *
- * @brief Convert an integer to a string representation
- *
- * @param[in] n integer to convert
- * @param[in] hex true if it should be in hex representation,
- * false for decimal
- *
- * @retval string representation of input
- * NULL if out of memory or input invalid
- */
- static char *_str(int n, bool hex)
- {
- char *buf = calloc(1, 24);
- assert(buf);
- if (!buf)
- return NULL;
-
- if (hex)
- snprintf(buf, 24, "%.4x", n);
- else
- snprintf(buf, 24, "%d", n);
- return buf;
- }
-
- #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
-
- /**
- * <!-- _«@name»_ERR_LOG_int() -->
- *
- * @internal
- *
- * @brief Write a «@name» error to log with integer input as description
- *
- * @param[in] session relevant session
- * @param[in] t C string event name
- * @param[in] n integer to log (e.g. error status)
- * @param[in] hex true if integer should be logged in hex format,
- * false if decimal
- *
- * @retval PEP_OUT_OF_MEMORY if out of memory
- * @retval PEP_ILLEGAL_VALUE if input error during logging
- * @retval PEP_STATUS_OK otherwise
- */
- __attribute__((__unused__))
- static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
- {
- char *_buf = _str(n, hex);
- if (!_buf)
- return PEP_OUT_OF_MEMORY;
- PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
- free(_buf);
- return status;
- }
-
- #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
- #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
- #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
-
- «@name»_state fsm_«@name»(
- PEP_SESSION session,
- «@name»_state state,
- «@name»_event event
- )
- {
- assert(session);
- if (!session)
- return invalid_state;
-
- if ((int) state == None)
- state = «@name»_state_Init;
-
- switch (state) {
- `` apply "state", 2, mode=fsm
- default:
- «@name»_ERR_LOG("invalid state", «@name»_state_name(state));
- assert(0);
- return invalid_state;
- }
-
- return None;
- }
-
- ||
- }
- }
-
- template "state", mode=fsm {
- choose {
- when "@name='InitState'" | case «../@name»_state_Init:
- otherwise | case «@name»:
- }
- ||
- «../@name»_SERVICE_LOG("in state", "«@name»");
-
- switch (event) {
- case None:
- // received Timeout event, ignoring
- break;
-
- ||
- if "not(event[@name='Init'])"
- ||
- case Init:
- «../@name»_SERVICE_LOG("received Init but nothing to do", "Init");
- break;
-
- ||
- ||
- `` apply "event", 2, mode=fsm
- default:
- // ignore events not handled here
- «../@name»_SERVICE_LOG("ignoring event", «../@name»_event_name(event));
- return invalid_event;
- }
- break;
-
- ||
- }
-
- template "event", mode=fsm {
- | case «@name»: {
- if "condition|action|send" |> PEP_STATUS status;
- if "condition" |> bool result = false;
- if "condition|action|send" |
- ||
- «../../@name»_SERVICE_LOG("received event", "«@name»");
- `` apply "transition|action|condition|else|send|debug";
- ||
- if "name(*[last()])!='transition'" {
- |
- |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
- |> break;
- }
- ||
- }
-
- ||
- }
-
- template "transition" {
- const "fsm", "ancestor::fsm";
- ||
-
- «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
- return «@target»;
- ||
- }
-
- template "send" {
- const "fsm", "ancestor::fsm";
- const "protocol", "ancestor::protocol";
- ||
-
- «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
- status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»_PR_«yml:mixedCase(@name)»);
- if (status == PEP_OUT_OF_MEMORY)
- return out_of_memory;
- if (status) {
- «$fsm/@name»_ERR_LOG_HEX("sending «@name» failed", status);
- return cannot_send;
- }
- ||
- }
-
- template "debug"
- | KeySync_SERVICE_LOG("«.»", "«ancestor::protocol/@name»");
-
- template "action" {
- const "fsm", "ancestor::fsm";
- ||
-
- «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
- status = «@name»(session);
- if (status == PEP_OUT_OF_MEMORY)
- return out_of_memory;
- if (status) {
- «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
- assert(0);
- return invalid_action;
- }
- ||
- }
-
- template "condition" {
- const "fsm", "ancestor::fsm";
- ||
-
- status = «@name»(session, &result);
- if (status == PEP_OUT_OF_MEMORY)
- return out_of_memory;
- if (status) {
- «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
- assert(0);
- return invalid_condition;
- }
- if (result) {
- «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
- ||
- apply "transition|action|condition|else|send|debug";
- | }
- }
-
- template "else" {
- if "not(name(preceding-sibling::*[1]) = 'condition')"
- error "else without if";
-
- | else {
- |> «ancestor::fsm/@name»_SERVICE_LOG("condition does not apply", "«preceding-sibling::*[last()]/@name»");
- apply "transition|action|condition|else|send|debug";
- | }
- }
- }
-
|