// This file is under GNU General Public License 3.0 // see LICENSE.txt // generate message functions // Copyleft (c) 2017-2019, p≡p foundation // Written by Volker Birk include yslt.yml2 tstylesheet { include standardlib.ysl2 include ./functions.ysl2 template "/" { apply "protocol", 0, mode=header; apply "protocol", 0, mode=impl; } template "protocol", mode=header if "fsm[count(state)]" document "generated/{@name}_func.h", "text" || /** * @file «@name»_func.h * @brief State storage and retrieval, and associated «@name» message generation and processing, for the «@name» protocol. * @generated from ../sync/gen_message_func.ysl2 * * @license GNU General Public License 3.0 - see LICENSE.txt */ #ifndef «yml:ucase(@name)»_FUNC_H #define «yml:ucase(@name)»_FUNC_H #ifdef __cplusplus extern "C" { #endif #include #include "../asn.1/«@name».h" `` for "func:distinctType(fsm/message/field[not(func:basicType())])" | #include "../asn.1/«@type».h" /** * State information and associated stored data for all «@name» state machines */ struct «@name»_state_s { /** * @struct «@name»_state_s::own_«@name»_state_s * @brief Own state storage. */ struct own_«@name»_state_s { stringlist_t *keys; //!< own keys stringlist_t *backup; //!< ???? identity_list *identities; //!< own identities `` for "fsm/message[@ratelimit>0]" |>> time_t last_«../@name»_«@name»; //!< Timestamp of last «../@name» «@name» message accepted // TIDs we're using ourselves `` for "func:distinctName(fsm/message/field[@type='TID'])" |>> «func:ctype()» «@name»; //!< own «@name» TID } own; /*!< Own state storage */ /** * @struct «@name»_state_s::comm_partner_state_s * @brief State acquired from our communications partner */ struct comm_partner_state_s { // transport data we expect char *sender_fpr; //!< sender fpr for comm partner that we expect all sender messages to be signed by pEp_identity *identity; //!< comm partner identity // TIDs our comm partner wants to have `` for "func:distinctName(fsm/message/field[@type='TID'])" |>> «func:ctype()» «@name»; //!< sender's «@name» TID } comm_partner; /*!< Received comm partner state storage */ /** * @struct «@name»_state_s::transport_data_s * @brief Input buffer for actual transport data coming in */ struct transport_data_s { // transport data we got pEp_identity *from; //!< identity of the sender of incoming transport data char *sender_fpr; //!< sender fpr for sender of incoming transport data } transport; /*!< transport input buffer */ `` apply "fsm", mode=state }; /** * * * @brief free and reset all «@name» state associated with this session * * @param[in] session the session * */ void free_«@name»_state(PEP_SESSION session); // functions for protocol «@name» /** * * * @brief Generate new «@name»_t message structure of the given message type * for the input finite state machine type * * @param[in] fsm current state machine type (??) * @param[in] message_type the type of «@name» message struct to be created (with empty data) * * @return message the message struct desired * @return NULL if the message_type is unknown * * @TODO This description comes entirely from code inspection, but is probably * better optimised by the author. Caveat lector - I may have gotten it wrong. */ «@name»_t *new_«@name»_message(«@name»_PR fsm, int message_type); /** * * * @brief free a/an «@name»_t asn.1 message struct * * @param[in] msg the «@name»_t message struct to free * * @TODO This description comes entirely from code inspection, but is probably * better optimised by the author. Caveat lector - I may have gotten it wrong. */ void free_«@name»_message(«@name»_t *msg); /** * * * @brief Given a/an «@name» message and its corresponding finite state machine, * update the session's state for its «@name» state machine given the information * decoded from the message. * * This function takes a «@name» message and, depending on the type of «@name» message it is, * (determined during parsing), copies the relevant data from the message struct into the * corresponding session state structures as a utf8 string. * * @param[in] session session associated with the «@name» finite state machine * @param[in] msg the message struct containing «@name» data (asn.1 - XER?) * @param[out] fsm present finite state machine type indicated by the message * @param[out] message_type the type of the message that was sent in * * @retval status * * @TODO This description comes entirely from code inspection, but is probably * better optimised by the author. Caveat lector - I may have gotten it wrong. * */ PEP_STATUS update_«@name»_state(PEP_SESSION session, «@name»_t *msg, «@name»_PR *fsm, int *message_type); /** * * * @brief Given a «@name»_t message struct, fill in the relevant data for that message type and * the state machine type indicated in the message from the current information contained * in the session according to the message type indicated on the message struct * * @param[in] session the session from which to take the «@name» data * @param[in] msg the «@name»_t message structure * * @retval status * * @TODO This description comes entirely from code inspection, but is probably * better optimised by the author. Caveat lector - I may have gotten it wrong. */ PEP_STATUS update_«@name»_message(PEP_SESSION session, «@name»_t *msg); #ifdef __cplusplus } #endif #endif || template "fsm", mode=state || /** * @struct «../@name»::_«@name»_state_s * @brief Input/output buffer for «@name» messages * * @note Can't find a good way to generate documentation for the fields here. */ struct _«@name»_state_s { int state; //!< current «@name» state `` for "func:distinctName(message/field)" |> «func:ctype()» «@name»; } «yml:lcase(@name)»; /*!< «@name» message Input/output buffer */ || template "protocol", mode=impl if "fsm[count(state)]" document "generated/{@name}_func.c", "text" { || /** * @file «@name»_func.c * @brief Implementation of tate storage and retrieval, and associated «@name» message * generation and processing, for the «@name» protocol. * @generated from ../sync/gen_message_func.ysl2 * * @license GNU General Public License 3.0 - see LICENSE.txt */ #include #include #include "pEp_internal.h" #include "map_asn1.h" #include "«@name»_func.h" `` for "fsm" | #include "«@name»_fsm.h" void free_«@name»_state(PEP_SESSION session) { if (!session) return; // own state free_stringlist(session->«yml:lcase(@name)»_state.own.keys); session->«yml:lcase(@name)»_state.own.keys = NULL; free_identity_list(session->«yml:lcase(@name)»_state.own.identities); session->«yml:lcase(@name)»_state.own.identities = NULL; // TIDs we're using ourselves || for "func:distinctName(fsm/message/field[@type='TID'])" |> ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_«@type», &session->«yml:lcase(../../../@name)»_state.own.«@name»); || // state we learned about our communication partner free(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr); session->«yml:lcase(@name)»_state.comm_partner.sender_fpr = NULL; free_identity(session->«yml:lcase(@name)»_state.comm_partner.identity); session->«yml:lcase(@name)»_state.comm_partner.identity = NULL; // TIDs our comm partner wants to have || for "func:distinctName(fsm/message/field[@type='TID'])" |> ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_«@type», &session->«yml:lcase(../../../@name)»_state.comm_partner.«@name»); || // buffer for transport data free_identity(session->«yml:lcase(@name)»_state.transport.from); session->«yml:lcase(@name)»_state.transport.from = NULL; free(session->«yml:lcase(@name)»_state.transport.sender_fpr); session->«yml:lcase(@name)»_state.transport.sender_fpr = NULL; // message buffers || for "fsm" { for "func:distinctName(message/field[not(substring(@type,1,1)=yml:lcase(substring(@type,1,1)))])" |> ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_«@type», &session->«yml:lcase(../../../@name)»_state.«yml:lcase(../../@name)».«@name»); | } || memset(&session->«yml:lcase(@name)»_state, 0, sizeof(session->«yml:lcase(@name)»_state)); } «@name»_t *new_«@name»_message(«@name»_PR fsm, int message_type) { «@name»_t *msg = calloc(sizeof(«@name»_t), 1); assert(msg); if (!msg) return NULL; if (fsm) { msg->present = fsm; if (message_type) { switch (fsm) { `` apply "fsm", 4, mode=impl default: free(msg); return NULL; } } } return msg; } void free_«@name»_message(«@name»_t *msg) { ASN_STRUCT_FREE(asn_DEF_«@name», msg); } PEP_STATUS update_«@name»_state(PEP_SESSION session, «@name»_t *msg, «@name»_PR *fsm, int *message_type) { int result = 0; assert(session && msg && fsm && message_type); if (!(session && msg && fsm && message_type)) return PEP_ILLEGAL_VALUE; *fsm = 0; *message_type = None; switch (msg->present) { case «@name»_PR_NOTHING: return PEP_ILLEGAL_VALUE; `` apply "fsm", 2, mode=update_state default: return PEP_ILLEGAL_VALUE; } *fsm = msg->present; return PEP_STATUS_OK; } PEP_STATUS update_«@name»_message(PEP_SESSION session, «@name»_t *msg) { assert(session && msg); if (!(session && msg)) return PEP_ILLEGAL_VALUE; int fsm = msg->present; switch (fsm) { case «@name»_PR_NOTHING: return PEP_ILLEGAL_VALUE; `` apply "fsm", 2, mode=update_message default: return PEP_ILLEGAL_VALUE; } return PEP_STATUS_OK; } || } template "fsm", mode=update_message || case «../@name»_PR_«yml:lcase(@name)»: { int message_type = msg->choice.«yml:lcase(@name)».present; switch (message_type) { case «@name»_PR_NOTHING: return PEP_ILLEGAL_VALUE; `` apply "message", 2, mode=update_message default: return PEP_ILLEGAL_VALUE; } break; } || template "message", mode=update_message { || case «../@name»_PR_«yml:mixedCase(@name)»: `` apply "auto" `` apply "field", mode=update_message break; || } template "auto" choose { when "@type = 'Version'" { const "fsm", "ancestor::fsm"; || { long *major = (long *) malloc(sizeof(long)); long *minor = (long *) malloc(sizeof(long)); assert(major && minor); if (!(major && minor)) return PEP_OUT_OF_MEMORY; *major = «$fsm/version/@major»; *minor = «$fsm/version/@minor»; msg->choice.«yml:lcase($fsm/@name)».choice.«yml:mixedCase(../@name)».«@name».major = major; msg->choice.«yml:lcase($fsm/@name)».choice.«yml:mixedCase(../@name)».«@name».minor = minor; } || } otherwise error "unkown type for auto in message: {@type}; allowed types: Version"; } template "field", mode=update_message { const "message_name", "yml:mixedCase(../@name)"; const "state" > «yml:lcase(ancestor::protocol/@name)»_state.«yml:lcase(ancestor::fsm/@name)» choose { when "func:basicType() or @type='Rating'" // copyable || msg->choice.«yml:lcase(../../@name)».choice.«$message_name».«@name» = session->«$state».«@name»; || when "@type='Identity'" || { pEp_identity *ident = Identity_to_Struct(&session->«$state».«@name», NULL); if (!ident) return PEP_OUT_OF_MEMORY; Identity_t *_ident = Identity_from_Struct(ident, &msg->choice.«yml:lcase(../../@name)».choice.«$message_name».«@name»); free_identity(ident); if (!_ident) return PEP_OUT_OF_MEMORY; } || when "@type='IdentityList'" || { identity_list *il = IdentityList_to_identity_list( &session->«$state».«@name», NULL); if (!il) return PEP_OUT_OF_MEMORY; PEP_STATUS own_idents_status = set_all_userids_to_own(session, il); if (own_idents_status != PEP_STATUS_OK) return own_idents_status; IdentityList_t *_il = IdentityList_from_identity_list(il, &msg->choice.«yml:lcase(../../@name)».choice.«$message_name».«@name»); free_identity_list(il); if (!_il) return PEP_OUT_OF_MEMORY; } || otherwise // string based || { int result = OCTET_STRING_fromBuf( &msg->choice.«yml:lcase(../../@name)».choice.«$message_name».«@name», (char *) session->«$state».«@name».buf, session->«$state».«@name».size ); if (result) return PEP_OUT_OF_MEMORY; } || } } template "fsm", mode=update_state || case «../@name»_PR_«yml:lcase(@name)»: switch (msg->choice.«yml:lcase(@name)».present) { case «@name»_PR_NOTHING: return PEP_ILLEGAL_VALUE; `` apply "message", 2, mode=update_state default: return PEP_ILLEGAL_VALUE; } break; || template "message", mode=update_state { const "message_name", "concat(yml:lcase(substring(@name,1,1)), substring(@name,2))"; || case «../@name»_PR_«$message_name»: `` apply "field", mode=update_state with "message_name", "$message_name" *message_type = «yml:capit($message_name)»; break; || } template "field", mode=update_state { param "message_name"; choose { when "func:basicType() or @type='Rating'" // copyable || session->«yml:lcase(../../../@name)»_state.«yml:lcase(../../@name)».«@name» = msg->choice.«yml:lcase(../../@name)» .choice.«$message_name».«@name»; || when "@type='Identity'" || { pEp_identity *ident = Identity_to_Struct( &msg->choice.«yml:lcase(../../@name)».choice.«$message_name».«@name», NULL); if (!ident) return PEP_OUT_OF_MEMORY; Identity_t *_ident = Identity_from_Struct(ident, &session->«yml:lcase(../../../@name)»_state.«yml:lcase(../../@name)».«@name»); free_identity(ident); if (!_ident) return PEP_OUT_OF_MEMORY; } || when "@type='IdentityList'" || { identity_list *il = IdentityList_to_identity_list( &msg->choice.«yml:lcase(../../@name)».choice.«$message_name».«@name», NULL); if (!il) return PEP_OUT_OF_MEMORY; PEP_STATUS own_idents_status = set_all_userids_to_own(session, il); if (own_idents_status != PEP_STATUS_OK) return own_idents_status; IdentityList_t *_il = IdentityList_from_identity_list(il, &session->«yml:lcase(../../../@name)»_state.«yml:lcase(../../@name)».«@name»); free_identity_list(il); if (!_il) return PEP_OUT_OF_MEMORY; } || otherwise // string based || result = OCTET_STRING_fromBuf(&session->«yml:lcase(../../../@name)»_state.«yml:lcase(../../@name)».«@name», (char *) msg->choice.«yml:lcase(../../@name)».choice.«$message_name».«@name».buf, msg->choice.«yml:lcase(../../@name)».choice.«$message_name».«@name».size); if (result) return PEP_OUT_OF_MEMORY; || } } template "fsm", mode=impl || case «../@name»_PR_«yml:lcase(@name)»: msg->choice.«yml:lcase(@name)».present = message_type; break; || }