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.
 
 
 
 

542 lines
17 KiB

// 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
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 <stdbool.h>
#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
};
/**
* <!-- free_«@name»_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»
/**
* <!-- new_«@name»_message() -->
*
* @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);
/**
* <!-- free_«@name»_message() -->
*
* @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);
/**
* <!-- update_«@name»_state() -->
*
* @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);
/**
* <!-- update_«@name»_message() -->
*
* @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
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 <assert.h>
#include <stdlib.h>
#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;
||
}