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