sync/gen_statemachine.ysl2
author buff <andreas@pep-project.org>
Fri, 23 Oct 2020 11:08:11 +0200
branchRelease_2.1
changeset 5139 2d551164adeb
parent 5034 ad76ee3dce6b
permissions -rw-r--r--
Xcode: drops iOS <12 support
     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-2020, 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                     // FIXME: there should be a mapping between event and message type
   295                     //        with current implementation they've got an offset of 1
   296                     msg = new_«@name»_message(fsm, event - 1);
   297                     if (!msg) {
   298                         status = PEP_OUT_OF_MEMORY;
   299                         goto the_end;
   300                     }
   301 
   302                     status = update_«@name»_message(session, msg);
   303                     if (status)
   304                         goto the_end;
   305                 }
   306 
   307                 ev = new_«@name»_event(fsm, event, msg);
   308                 if (!ev) {
   309                     status = PEP_OUT_OF_MEMORY;
   310                     goto the_end;
   311                 }
   312 
   313                 status = set_all_userids_to_own(session, own_identities);
   314                 if (status != PEP_STATUS_OK)
   315                     goto the_end;
   316                     
   317                 ev->own_identities = own_identities;
   318 
   319                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   320                         session->«yml:lcase(@name)»_management);
   321                 if (result) {
   322                     status = PEP_STATEMACHINE_ERROR;
   323                     goto the_end;
   324                 }
   325                 return PEP_STATUS_OK;
   326 
   327             the_end:
   328                 free_«@name»_event(ev); // msg gets freed here
   329                 return status;
   330             }
   331 
   332             PEP_STATUS signal_«@name»_message(
   333                     PEP_SESSION session, 
   334                     PEP_rating rating,
   335                     const char *data,
   336                     size_t size,
   337                     const pEp_identity *from,
   338                     const char *sender_fpr
   339                 )
   340             {
   341                 assert(session && data && size);
   342                 if (!(session && data && size))
   343                     return PEP_ILLEGAL_VALUE;
   344 
   345                 if (!session->inject_«yml:lcase(@name)»_event)
   346                    return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
   347 
   348                 PEP_STATUS status = PEP_STATUS_OK;
   349                 «@name»_event_t *ev = NULL;
   350 
   351                 «@name»_t *msg = NULL;
   352                 status = decode_«@name»_message(data, size, &msg);
   353                 if (status)
   354                     return status;
   355 
   356                 «@name»_PR fsm = msg->present;
   357                 int event = 0;
   358                 bool is_own_key = false;
   359 
   360                 switch (fsm) {
   361                     `` apply "fsm", 2, mode=signal_message
   362                     default:
   363                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   364                         goto the_end;
   365                 }
   366 
   367                 ev = new_«@name»_event(fsm, event, msg);
   368                 if (!ev) {
   369                     status = PEP_OUT_OF_MEMORY;
   370                     goto the_end;
   371                 }
   372 
   373                 // add transport data
   374 
   375                 if (from) {
   376                     ev->from = identity_dup(from);
   377                     if (!ev->from) {
   378                         status = PEP_OUT_OF_MEMORY;
   379                         goto the_end;
   380                     }
   381                 }
   382 
   383                 if (sender_fpr) {
   384                     ev->sender_fpr = strdup(sender_fpr);
   385                     assert(ev->sender_fpr);
   386                     if (!ev->sender_fpr) {
   387                         status = PEP_OUT_OF_MEMORY;
   388                         goto the_end;
   389                     }
   390                 }
   391 
   392                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   393                         session->«yml:lcase(@name)»_management);
   394                 if (result) {
   395                     status = PEP_STATEMACHINE_ERROR;
   396                     goto the_end;
   397                 }
   398 
   399                 return PEP_STATUS_OK;
   400 
   401             the_end:
   402                 free_«@name»_event(ev); // msg gets freed here
   403                 return status;
   404             }
   405 
   406             PEP_STATUS send_«@name»_message(
   407                     PEP_SESSION session, 
   408                     «@name»_PR fsm,
   409                     int message_type
   410                 )
   411             {
   412                 PEP_STATUS status = PEP_STATUS_OK;
   413 
   414                 assert(session && fsm > None && message_type > None);
   415                 if (!(session && fsm > None && message_type > None))
   416                     return PEP_ILLEGAL_VALUE;
   417             ||
   418             if "fsm/message[@ratelimit>0]" {
   419             ||
   420 
   421                 // test if a message with a rate limit was just sent; in case drop
   422                 time_t now = time(NULL);
   423                 switch (fsm) {
   424             ||
   425             for "fsm[message/@ratelimit>0]" {
   426             ||
   427                     case Sync_PR_«yml:lcase(@name)»:
   428                         switch (message_type) {
   429             ||
   430             for "message[@ratelimit>0]"
   431             ||
   432                             case «../@name»_PR_«yml:mixedCase(@name)»:
   433                                 if (now < session->«yml:lcase(../../@name)»_state.own.last_«../@name»_«@name» + «@ratelimit»)
   434                                     return PEP_STATUS_OK;
   435                                 break;
   436             ||
   437             ||
   438                             default:
   439                                 break;
   440                         }
   441                         break;
   442             ||
   443             }
   444             ||
   445                     default:
   446                         break;
   447                 }
   448             ||
   449             }
   450             ||
   451 
   452                 «@name»_t *msg = new_«@name»_message(fsm, message_type);
   453                 if (!msg)
   454                     return PEP_OUT_OF_MEMORY;
   455 
   456                 char *data = NULL;
   457                 message *m = NULL;
   458                 identity_list *channels = NULL;
   459                 char *key_data = NULL;
   460                 size_t key_data_size = 0;
   461                 stringlist_t *extra = NULL;
   462                 bool transaction;
   463 
   464                 status = update_«@name»_message(session, msg);
   465                 if (status)
   466                     goto the_end;
   467 
   468                 size_t size = 0;
   469                 status = encode_«@name»_message(msg, &data, &size);
   470                 if (status)
   471                     goto the_end;
   472 
   473                 // we never use this
   474                 if (session->«yml:lcase(@name)»_state.comm_partner.identity
   475                         && session->«yml:lcase(@name)»_state.comm_partner.identity->fpr) {
   476                     free(session->«yml:lcase(@name)»_state.comm_partner.identity->fpr);
   477                     session->«yml:lcase(@name)»_state.comm_partner.identity->fpr = NULL;
   478                 }
   479 
   480                 // if we have this we always use this
   481                 if (session->«yml:lcase(@name)»_state.comm_partner.sender_fpr) {
   482                     free(session->«yml:lcase(@name)»_state.transport.sender_fpr);
   483                     session->«yml:lcase(@name)»_state.transport.sender_fpr =
   484                             strdup(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
   485                     assert(session->«yml:lcase(@name)»_state.transport.sender_fpr);
   486                     if (!session->«yml:lcase(@name)»_state.transport.sender_fpr) {
   487                         status = PEP_OUT_OF_MEMORY;
   488                         goto the_end;
   489                     }
   490                 }
   491 
   492                 switch (message_type) {
   493                     // these messages are being broadcasted
   494                     `` for "fsm/message[@type='broadcast']" |>> case «../@name»_PR_«yml:mixedCase(@name)»:
   495                         status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   496                         if (status)
   497                             goto the_end;
   498 
   499                         if (!(channels && channels->ident)) {
   500                             // status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   501                             // we don't check for having a channel, because if
   502                             // this is initial setup before having an own
   503                             // identity we're fine
   504                             goto the_end;
   505                         }
   506                         break;
   507 
   508                     // these go anycast; previously used address is sticky (unicast)
   509                     `` for "fsm/message[@type='anycast']" |>> case «../@name»_PR_«yml:mixedCase(@name)»:
   510                         // if we have a comm_partner fixed send it there
   511                         if (session->«yml:lcase(@name)»_state.comm_partner.identity) {
   512                             pEp_identity *channel = identity_dup(session->«yml:lcase(@name)»_state.comm_partner.identity);
   513                             if (!channel) {
   514                                 status = PEP_OUT_OF_MEMORY;
   515                                 goto the_end;
   516                             }
   517 
   518                             channels = new_identity_list(channel);
   519                             if (!channels) {
   520                                 status = PEP_OUT_OF_MEMORY;
   521                                 goto the_end;
   522                             }
   523                         }
   524                         // if we can reply just do
   525                         else if (session->«yml:lcase(@name)»_state.transport.from) {
   526                             pEp_identity *channel = identity_dup(session->«yml:lcase(@name)»_state.transport.from);
   527                             if (!channel) {
   528                                 status = PEP_OUT_OF_MEMORY;
   529                                 goto the_end;
   530                             }
   531 
   532                             channels = new_identity_list(channel);
   533                             if (!channels) {
   534                                 status = PEP_OUT_OF_MEMORY;
   535                                 goto the_end;
   536                             }
   537                         }
   538                         // real anycast, send it to the first matching
   539                         else {
   540                             status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   541                             if (status)
   542                                 goto the_end;
   543                             if (!channels)
   544                                 goto the_end;
   545 
   546                             if (channels->next) {
   547                                 free_identity_list(channels->next);
   548                                 channels->next = NULL;
   549                             }
   550                         }
   551                         break;
   552 
   553                     default:
   554                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   555                         goto the_end;
   556                 }
   557 
   558                 for (identity_list *li = channels; li && li->ident ; li = li->next) {
   559                     message *_m = NULL;
   560                     char *_data = NULL;
   561                     
   562                     _data = malloc(size);
   563                     assert(_data);
   564                     if (!_data) {
   565                         status = PEP_OUT_OF_MEMORY;
   566                         goto the_end;
   567                     }
   568                     memcpy(_data, data, size);
   569 
   570                     switch (message_type) {
   571                     `` for "fsm/message[@security='unencrypted' and ../@name!='KeySync']" | #error unencrypted only allowed with KeySync
   572                     `` for "fsm/message[@security='unencrypted' and ../@name='KeySync']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
   573                             status = try_base_prepare_message(
   574                                     session,
   575                                     li->ident,
   576                                     li->ident,
   577                                     BASE_SYNC,
   578                                     _data,
   579                                     size,
   580                                     li->ident->fpr,
   581                                     &_m
   582                                 );
   583                             if (status) {
   584                                 free(_data);
   585                                 goto the_end;
   586                             }
   587                             attach_own_key(session, _m);
   588                             decorate_message(session, _m, PEP_rating_undefined, NULL, true, true);
   589                             m = _m;
   590                             break;
   591 
   592                     `` for "fsm/message[@security='untrusted' and ../@name!='KeySync']" | #error untrusted only allowed with KeySync
   593                     `` for "fsm/message[@security='untrusted' and ../@name='KeySync']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
   594                             // add fpr of key of comm partner
   595 
   596                             assert(session->«yml:lcase(@name)»_state.transport.sender_fpr);
   597                             if (!session->«yml:lcase(@name)»_state.transport.sender_fpr) {
   598                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   599                                 goto the_end;
   600                             }
   601 
   602                             extra = new_stringlist(session->«yml:lcase(@name)»_state.transport.sender_fpr);
   603                             if (!extra) {
   604                                 status = PEP_OUT_OF_MEMORY;
   605                                 goto the_end;
   606                             }
   607 
   608                             status = base_prepare_message(
   609                                     session,
   610                                     li->ident,
   611                                     li->ident,
   612                                     BASE_SYNC,
   613                                     _data,
   614                                     size,
   615                                     NULL,
   616                                     &_m
   617                                 );
   618                             if (status) {
   619                                 free(_data);
   620                                 goto the_end;
   621                             }
   622 
   623                             status = try_encrypt_message(session, _m, extra, &m, PEP_enc_PEP, 0);
   624                             if (status) {
   625                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   626                                 goto the_end;
   627                             }
   628                             add_opt_field(m, "pEp-auto-consume", "yes");
   629                             m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
   630                             free_message(_m);
   631                             break;
   632 
   633                         // attach own keys for new member
   634                         `` for "fsm/message[@security='attach_own_keys_for_new_member']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
   635                             // check if we had a former negotiation
   636 
   637                             transaction = false;
   638                             for (int i=0; i < session->«yml:lcase(@name)»_state.own.negotiation.size; i++) {
   639                                 if (session->«yml:lcase(@name)»_state.own.negotiation.buf[i]) {
   640                                     transaction = true;
   641                                     break;
   642                                 }
   643                             }
   644     
   645                             // if it is a former negotiation check if the key
   646                             // is fully trusted and the sender key of this
   647                             // transaction; if so add the sender key to extra
   648                             // keys allowing this new partner to read the
   649                             // secret keys
   650 
   651                             if (transaction) {
   652                                 assert(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr &&
   653                                     session->«yml:lcase(@name)»_state.transport.from &&
   654                                     session->«yml:lcase(@name)»_state.transport.from->user_id);
   655                                 if (!(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr &&
   656                                         session->«yml:lcase(@name)»_state.transport.from &&
   657                                         session->«yml:lcase(@name)»_state.transport.from->user_id))
   658                                 {
   659                                     status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   660                                     goto the_end;
   661                                 }
   662                             
   663                                 // test if this is a green channel
   664 
   665                                 pEp_identity *ident = new_identity(NULL,
   666                                         session->«yml:lcase(@name)»_state.comm_partner.sender_fpr,
   667                                         session->«yml:lcase(@name)»_state.transport.from->user_id,
   668                                         NULL
   669                                     );
   670                                 if (!ident) {
   671                                     status = PEP_OUT_OF_MEMORY;
   672                                     goto the_end;
   673                                 }
   674                                 status = get_trust(session, ident);
   675                                 if (status) {
   676                                     free_identity(ident);
   677                                     goto the_end;
   678                                 }
   679                                 assert(ident->comm_type == PEP_ct_pEp); // we don't deliver otherwise
   680                                 if (ident->comm_type != PEP_ct_pEp) {
   681                                     free_identity(ident);
   682                                     status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   683                                     goto the_end;
   684                                 }
   685                                 free_identity(ident);
   686 
   687                                 // test if we accepted this as own key already
   688 
   689                                 bool is_own_key = false;
   690                                 status = own_key_is_listed(session,
   691                                         session->«yml:lcase(@name)»_state.comm_partner.sender_fpr,
   692                                         &is_own_key);
   693                                 assert(!status);
   694                                 if (status)
   695                                     goto the_end;
   696                                 assert(is_own_key);
   697                                 if (!is_own_key) {
   698                                     status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   699                                     goto the_end;
   700                                 }
   701 
   702                                 // if so add key of comm partner to extra keys
   703 
   704                                 extra = new_stringlist(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
   705                                 if (!extra) {
   706                                     status = PEP_OUT_OF_MEMORY;
   707                                     goto the_end;
   708                                 }
   709                             }
   710                             
   711                             status = base_prepare_message(
   712                                     session,
   713                                     li->ident,
   714                                     li->ident,
   715                                     BASE_SYNC,
   716                                     _data,
   717                                     size,
   718                                     NULL,
   719                                     &_m
   720                                 );
   721                             if (status) {
   722                                 free(_data);
   723                                 goto the_end;
   724                             }
   725 
   726                             // export secret keys into memory
   727 
   728                             key_data = strdup("");
   729                             assert(key_data);
   730                             if (!key_data) {
   731                                 free(_data);
   732                                 free_message(_m);
   733                                 status = PEP_OUT_OF_MEMORY;
   734                                 goto the_end;
   735                             }
   736                             key_data_size = 1; // N.B. If null termination makes us happy for debugging, fine, but 
   737                                                // if we include this in the size, libetpan will null terminate and 
   738                                                // go bananas. We can't have a NUL in the mime text.
   739 
   740                             for (stringlist_t *sl = session->«yml:lcase(@name)»_state.own.keys;
   741                                     sl && sl->value ; sl = sl->next)
   742                             {
   743                                 char *_key_data = NULL;
   744                                 size_t _size = 0;
   745                                 status = export_secret_key(session, sl->value, &_key_data, &_size);
   746                                 if (status && status != PEP_KEY_NOT_FOUND) {
   747                                     free(_data);
   748                                     free_message(_m);
   749                                     goto the_end;
   750                                 }
   751 
   752                                 if (status != PEP_KEY_NOT_FOUND) {
   753                                     assert(_key_data && _size);
   754                                     char *n = realloc(key_data, key_data_size + _size);
   755                                     if (!n) {
   756                                         free(_data);
   757                                         free_message(_m);
   758                                         status = PEP_OUT_OF_MEMORY;
   759                                         goto the_end;
   760                                     }
   761                                     key_data = n;
   762                                     key_data_size += _size;
   763                                     strlcat(key_data, _key_data, key_data_size);
   764                                     free(_key_data);
   765                                     _key_data = NULL;
   766                                 }
   767                                 status = export_key(session, sl->value, &_key_data, &_size);
   768                                 if (status && status != PEP_KEY_NOT_FOUND) {
   769                                     free(_data);
   770                                     free_message(_m);
   771                                     goto the_end;
   772                                 }
   773 
   774                                 if (status != PEP_KEY_NOT_FOUND) {
   775                                     assert(_key_data && _size);
   776                                     char *n = realloc(key_data, key_data_size + _size);
   777                                     if (!n) {
   778                                         free(_data);
   779                                         free_message(_m);
   780                                         status = PEP_OUT_OF_MEMORY;
   781                                         goto the_end;
   782                                     }
   783                                     key_data = n;
   784                                     key_data_size += _size;
   785                                     strlcat(key_data, _key_data, key_data_size);
   786                                     free(_key_data);
   787                                     _key_data = NULL;
   788                                 }
   789                             }
   790 
   791                             // add secret key data as attachment
   792 
   793                             // N.B. The -1 makes sure we do NOT add a NUL into the mime stream!
   794                             bloblist_t *bl = bloblist_add(_m->attachments, key_data, key_data_size - 1,
   795                                     "application/octet-stream", "file://own.key");
   796                             if (!bl) {
   797                                 free(_data);
   798                                 free_message(_m);
   799                                 status = PEP_OUT_OF_MEMORY;
   800                                 goto the_end;
   801                             }
   802                             key_data = NULL;
   803 
   804                             status = try_encrypt_message(session, _m, extra, &m, PEP_enc_PEP, 0);
   805                             if (status) {
   806                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   807                                 goto the_end;
   808                             }
   809                             add_opt_field(m, "pEp-auto-consume", "yes");
   810                             m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
   811                             free_message(_m);
   812                             break;
   813 
   814                         // attach own keys for group
   815                         `` for "fsm/message[@security='attach_own_keys_for_group']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
   816                             status = base_prepare_message(
   817                                     session,
   818                                     li->ident,
   819                                     li->ident,
   820                                     BASE_SYNC,
   821                                     _data,
   822                                     size,
   823                                     NULL,
   824                                     &_m
   825                                 );
   826                             if (status) {
   827                                 free(_data);
   828                                 goto the_end;
   829                             }
   830 
   831                             // export secret keys into memory
   832 
   833                             key_data = strdup("");
   834                             assert(key_data);
   835                             if (!key_data) {
   836                                 free(_data);
   837                                 free_message(_m);
   838                                 status = PEP_OUT_OF_MEMORY;
   839                                 goto the_end;
   840                             }
   841                             key_data_size = 1; // N.B. If null termination makes us happy for debugging, fine, but 
   842                                                // if we include this in the size, libetpan will null terminate and 
   843                                                // go bananas. We can't have a NUL in the mime text.
   844 
   845                             for (stringlist_t *sl = session->«yml:lcase(@name)»_state.own.keys;
   846                                     sl && sl->value ; sl = sl->next)
   847                             {
   848                                 char *_key_data = NULL;
   849                                 size_t _size = 0;
   850                                 status = export_secret_key(session, sl->value, &_key_data, &_size);
   851                                 if (status && status != PEP_KEY_NOT_FOUND) {
   852                                     free(_data);
   853                                     free_message(_m);
   854                                     goto the_end;
   855                                 }
   856 
   857                                 if (status != PEP_KEY_NOT_FOUND) {
   858                                     assert(_key_data && _size);
   859                                     char *n = realloc(key_data, key_data_size + _size);
   860                                     if (!n) {
   861                                         free(_data);
   862                                         free_message(_m);
   863                                         status = PEP_OUT_OF_MEMORY;
   864                                         goto the_end;
   865                                     }
   866                                     key_data = n;
   867                                     key_data_size += _size;
   868                                     strlcat(key_data, _key_data, key_data_size);
   869                                     free(_key_data);
   870                                     _key_data = NULL;
   871                                 }
   872                                 status = export_key(session, sl->value, &_key_data, &_size);
   873                                 if (status && status != PEP_KEY_NOT_FOUND) {
   874                                     free(_data);
   875                                     free_message(_m);
   876                                     goto the_end;
   877                                 }
   878 
   879                                 if (status != PEP_KEY_NOT_FOUND) {
   880                                     assert(_key_data && _size);
   881                                     char *n = realloc(key_data, key_data_size + _size);
   882                                     if (!n) {
   883                                         free(_data);
   884                                         free_message(_m);
   885                                         status = PEP_OUT_OF_MEMORY;
   886                                         goto the_end;
   887                                     }
   888                                     key_data = n;
   889                                     key_data_size += _size;
   890                                     strlcat(key_data, _key_data, key_data_size);
   891                                     free(_key_data);
   892                                     _key_data = NULL;
   893                                 }
   894                             }
   895 
   896                             // add secret key data as attachment
   897 
   898                             // N.B. The -1 makes sure we do NOT add a NUL into the mime stream!
   899                             bl = bloblist_add(_m->attachments, key_data, key_data_size - 1,
   900                                     "application/octet-stream", "file://own.key");
   901                             if (!bl) {
   902                                 free(_data);
   903                                 free_message(_m);
   904                                 status = PEP_OUT_OF_MEMORY;
   905                                 goto the_end;
   906                             }
   907                             key_data = NULL;
   908 
   909                             // we do not support extra keys here and will only encrypt to ourselves
   910                             status = try_encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
   911                             if (status) {
   912                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   913                                 goto the_end;
   914                             }
   915                             add_opt_field(m, "pEp-auto-consume", "yes");
   916                             m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
   917                             free_message(_m);
   918                             break;
   919 
   920                         default: // security=trusted only
   921                             status = base_prepare_message(
   922                                     session,
   923                                     li->ident,
   924                                     li->ident,
   925                                     BASE_SYNC,
   926                                     _data,
   927                                     size,
   928                                     NULL,
   929                                     &_m
   930                                 );
   931                             if (status) {
   932                                 free(_data);
   933                                 goto the_end;
   934                             }
   935 
   936                             status = try_encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
   937                             if (status) {
   938                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   939                                 goto the_end;
   940                             }
   941                             add_opt_field(m, "pEp-auto-consume", "yes");
   942                             m->in_reply_to = stringlist_add(m->in_reply_to, "pEp-auto-consume@pEp.foundation");
   943                             free_message(_m);
   944                     }
   945 
   946                     status = session->messageToSend(m);
   947                     m = NULL;
   948                 }
   949             ||
   950             if "fsm/message[@ratelimit>0]" {
   951             ||
   952 
   953                 switch (fsm) {
   954             ||
   955             for "fsm[message/@ratelimit>0]" {
   956             ||
   957                     case Sync_PR_«yml:lcase(@name)»:
   958                         switch (message_type) {
   959             ||
   960             for "message[@ratelimit>0]"
   961             ||
   962                             case «../@name»_PR_«yml:mixedCase(@name)»:
   963                                 session->«yml:lcase(../../@name)»_state.own.last_«../@name»_«@name» = now;
   964                                 break;
   965             ||
   966             ||
   967                             default:
   968                                 break;
   969                         }
   970                         break;
   971             ||
   972             }
   973             ||
   974                     default:
   975                         break;
   976                 }
   977             ||
   978             }
   979             ||
   980 
   981             the_end:
   982                 free_stringlist(extra);
   983                 free_identity_list(channels);
   984                 free_message(m);
   985                 free(data);
   986                 free(key_data);
   987                 free_«@name»_message(msg);
   988                 if (status)
   989                     SERVICE_ERROR_LOG(session, "send_«@name»_message()", status);
   990                 return status;
   991             }
   992 
   993             PEP_STATUS recv_«@name»_event(
   994                     PEP_SESSION session,
   995                     «@name»_event_t *ev
   996                 )
   997             {
   998                 assert(session && ev);
   999                 if (!(session && ev))
  1000                     return PEP_ILLEGAL_VALUE;
  1001 
  1002                 PEP_STATUS status = PEP_STATUS_OK;
  1003                 «@name»_PR fsm = (int) None;
  1004                 int event = None;
  1005 
  1006                 if (ev->event > None && ev->event < Extra) {
  1007                     status = update_«@name»_state(session, ev->msg, &fsm, &event);
  1008                     if (status)
  1009                         goto the_end;
  1010 
  1011                     if (ev->fsm) {
  1012                         if (ev->fsm != fsm |`> |` ev->event != event) {
  1013                             status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
  1014                             goto the_end;
  1015                         }
  1016                     }
  1017                     else if (ev->event) {
  1018                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
  1019                         goto the_end;
  1020                     }
  1021                 }
  1022                 else {
  1023                     fsm = ev->fsm;
  1024                     event = ev->event;
  1025                 }
  1026 
  1027                 // update transport data
  1028 
  1029                 if (ev->from) {
  1030                     free_identity(session->«yml:lcase(@name)»_state.transport.from);
  1031                     session->«yml:lcase(@name)»_state.transport.from = ev->from;
  1032                     ev->from = NULL;
  1033                 }
  1034 
  1035                 if (ev->sender_fpr) {
  1036                     free(session->«yml:lcase(@name)»_state.transport.sender_fpr);
  1037                     session->«yml:lcase(@name)»_state.transport.sender_fpr = ev->sender_fpr;
  1038                     
  1039                     /* Removed for temp ENGINE-647 fix. Will be reenabled once better sync debugging is in.
  1040                     // Check against saved comm_partner sender_fpr state, if there is one yet
  1041                     if (session->«yml:lcase(@name)»_state.comm_partner.sender_fpr) {
  1042                         // 1. Does it match sender_fpr?
  1043                         if (strcasecmp(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr, ev->sender_fpr) != 0) {
  1044                             // 2. If not, is it a group key?
  1045                             bool is_own_key = false;
  1046                             status = own_key_is_listed(session, ev->sender_fpr, &is_own_key);
  1047                             if (status)
  1048                                 goto the_end;
  1049                             if (!is_own_key) {
  1050                                 status = PEP_ILLEGAL_VALUE;
  1051                                 goto the_end;
  1052                             }    
  1053                         }
  1054                     }
  1055                     */
  1056                                                             
  1057                     ev->sender_fpr = NULL;
  1058                 }
  1059 
  1060                 // update own identities
  1061 
  1062                 if (ev->own_identities && ev->own_identities->ident) {
  1063                     free_identity_list(session->«yml:lcase(@name)»_state.own.identities);
  1064                     session->«yml:lcase(@name)»_state.own.identities = ev->own_identities;
  1065                     ev->own_identities = NULL;
  1066                 }
  1067 
  1068                 
  1069                 status = «@name»_driver(session, fsm, event);
  1070 
  1071             the_end:
  1072                 //free_«@name»_event(ev); // FIXME: We don't own this pointer. Are we sure it gets freed externally?
  1073                 return status;
  1074             }
  1075 
  1076             ||
  1077         }
  1078 
  1079         apply "fsm", 0, mode=gen;
  1080     }
  1081 
  1082     template "fsm", mode=timeout
  1083     ||
  1084     static bool _«@name»_timeout(int state)
  1085     {
  1086         static int last_state = None;
  1087         static time_t switch_time = 0;
  1088 
  1089         if (state > Init) {
  1090             if (state == last_state) {
  1091                 if (time(NULL) - switch_time > «yml:ucase(@name)»_THRESHOLD) {
  1092                     last_state = None;
  1093                     switch_time = 0;
  1094                     return true;
  1095                 }
  1096             }
  1097             else {
  1098                 last_state = state;
  1099                 switch_time = time(NULL);
  1100             }
  1101         }
  1102         else {
  1103             last_state = None;
  1104             switch_time = 0;
  1105         }
  1106 
  1107         return false;
  1108     }
  1109 
  1110     ||
  1111 
  1112     template "fsm", mode=reset_state_machine
  1113     ||
  1114         case «../@name»_PR_«yml:lcase(@name)»: {
  1115             int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
  1116             switch (state) {
  1117                 `` for "state[@name!='InitState' and @timeout != 'off']" |>>> case «@name»:
  1118                     if (_«@name»_timeout(state)) {
  1119                         session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = Init;
  1120                         event = Init;
  1121                         `` if "@threshold > 0" |>>>>> «@name»TimeoutHandler(session);
  1122                     }
  1123                     break;
  1124                 
  1125                 default:
  1126                     _«@name»_timeout(None);
  1127             }
  1128             break;
  1129         }
  1130 
  1131     ||
  1132 
  1133     template "fsm", mode=signal_message
  1134     {
  1135         ||
  1136         case «../@name»_PR_«yml:lcase(@name)»:
  1137             switch (msg->choice.«yml:lcase(@name)».present) {
  1138         ||
  1139         for "message[@security='unencrypted']" {
  1140         if "position()=1" |>> // these messages require a detached signature
  1141         ||
  1142                 case «../@name»_PR_«yml:mixedCase(@name)»:
  1143                     if (!sender_fpr) {
  1144                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1145                         goto the_end;
  1146                     }
  1147                     event = «@name»;
  1148                     break;
  1149 
  1150         ||
  1151         }
  1152         for "message[@security='untrusted']" {
  1153         if "position()=1" |>> // these messages must arrive encrypted
  1154         ||
  1155                 case «../@name»_PR_«yml:mixedCase(@name)»:
  1156                     if (rating < PEP_rating_reliable) {
  1157                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1158                         goto the_end;
  1159                     }
  1160                     event = «@name»;
  1161                     break;
  1162 
  1163         ||
  1164         }
  1165         for "message[@security!='unencrypted' and @security!='untrusted']" {
  1166         if "position()=1" |>> // these messages must come through a trusted channel
  1167         ||
  1168                 case «../@name»_PR_«yml:mixedCase(@name)»:
  1169                     if (rating < PEP_rating_trusted) {
  1170                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1171                         goto the_end;
  1172                     }
  1173                     status = own_key_is_listed(session, sender_fpr, &is_own_key);
  1174                     if (status)
  1175                         goto the_end;
  1176                     if (!is_own_key) {
  1177                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1178                         goto the_end;
  1179                     }
  1180                     event = «@name»;
  1181                     break;
  1182 
  1183         ||
  1184         }
  1185         ||
  1186                 default:
  1187                     status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
  1188                     goto the_end;
  1189             }
  1190             break;
  1191 
  1192         ||
  1193     }
  1194 
  1195     template "fsm", mode=event
  1196     {
  1197     ||
  1198     case «../@name»_PR_«yml:lcase(@name)»: {
  1199         switch (msg->choice.«yml:lcase(@name)».present) {
  1200     ||
  1201     for "message"
  1202     ||
  1203             case «../@name»_PR_«yml:mixedCase(@name)»:
  1204                 ev->event = «@name»;
  1205                 break;
  1206     ||
  1207     ||
  1208             default:
  1209                 // unknown message type
  1210                 free(ev);
  1211                 return NULL;
  1212         }
  1213         break;
  1214     }
  1215 
  1216     ||
  1217     }
  1218 
  1219     template "fsm", mode=driver
  1220     ||
  1221     case «../@name»_PR_«yml:lcase(@name)»: {
  1222         int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
  1223         next_state = fsm_«@name»(session, state, event);
  1224         if (next_state > None) {
  1225             session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
  1226             event = Init;
  1227         }
  1228         else if (next_state < None) {
  1229             return PEP_STATEMACHINE_ERROR - next_state;
  1230         }
  1231         break;
  1232     }
  1233 
  1234     ||
  1235 
  1236     template "fsm", mode=gen {
  1237         document "generated/{@name}_fsm.h", "text" {
  1238         ||
  1239         // This file is under GNU General Public License 3.0
  1240         // see LICENSE.txt
  1241 
  1242         #pragma once
  1243 
  1244         #include "«../@name»_impl.h"
  1245 
  1246         #ifdef __cplusplus
  1247         extern "C" {
  1248         #endif
  1249 
  1250         // state machine for «@name»
  1251 
  1252         // states
  1253 
  1254         typedef enum _«@name»_state {
  1255             «@name»_state_None = None,
  1256             «@name»_state_Init = Init,
  1257         ||
  1258         for "func:distinctName(state[not(@name='InitState')])"
  1259             |> «@name»`if "position()!=last()" > , `
  1260         ||
  1261         } «@name»_state;
  1262 
  1263         // events
  1264 
  1265         typedef enum _«@name»_event {
  1266             «@name»_event_Timeout = None,
  1267             «@name»_event_Init = Init,
  1268         ||
  1269         for "message" {
  1270             const "name", "@name";
  1271             |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
  1272         }
  1273         |> «@name»_event_Extra = Extra,
  1274         for "external" {
  1275             if "@id < 128"
  1276                 error > external «@name» must have ID >= 128 but it's «@id»
  1277             |> «@name» = «@id»,
  1278         }
  1279         for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
  1280             if "@name!='Init'"
  1281                 |> «@name»`if "position()!=last()" > , `
  1282         }
  1283         ||
  1284         } «@name»_event;
  1285 
  1286         // state machine
  1287 
  1288         #ifndef NDEBUG
  1289         const char *«@name»_state_name(int state);
  1290         const char *«@name»_event_name(int event);
  1291         #endif
  1292 
  1293         // the state machine function is returning the next state in case of a
  1294         // transition or None for staying
  1295 
  1296         «@name»_state fsm_«@name»(
  1297                 PEP_SESSION session,
  1298                 «@name»_state state,
  1299                 «@name»_event event
  1300             );
  1301 
  1302         #ifdef __cplusplus
  1303         }
  1304         #endif
  1305 
  1306         ||
  1307         }
  1308 
  1309         document "generated/{@name}_fsm.c", "text" {
  1310         ||
  1311         // This file is under GNU General Public License 3.0
  1312         // see LICENSE.txt
  1313 
  1314         #include "«@name»_fsm.h"
  1315         #include <stdlib.h>
  1316 
  1317         #ifdef NDEBUG
  1318         static
  1319         #endif
  1320         const char *«@name»_state_name(int state)
  1321         {
  1322             switch (state) {
  1323                 case End:
  1324                     return "End";
  1325                 case None:
  1326                     return "None";
  1327                 case Init:
  1328                     return "InitState";
  1329         ||
  1330         for "func:distinctName(state[not(@name='InitState')])" {
  1331             |>> case «@name»:
  1332             |>>> return "«@name»";
  1333         }
  1334         ||
  1335                 default:
  1336                     assert(0);
  1337                     return "unknown state";
  1338             }
  1339         }
  1340 
  1341         #ifdef NDEBUG
  1342         static
  1343         #endif
  1344         const char *«@name»_event_name(int event)
  1345         {
  1346             switch (event) {
  1347                 case None:
  1348                     return "Timeout";
  1349                 case Init:
  1350                     return "Init";
  1351         ||
  1352         for "func:distinctName(state/event[not(@name='Init')]|message)" {
  1353             |>> case «@name»:
  1354             |>>> return "«@name»";
  1355         }
  1356         ||
  1357                 default:
  1358                     assert(0);
  1359                     return "unknown event";
  1360             }
  1361         }
  1362 
  1363 
  1364         static char *_str(int n, bool hex)
  1365         {
  1366             char *buf = calloc(1, 24);
  1367             assert(buf);
  1368             if (!buf)
  1369                 return NULL;
  1370 
  1371             if (hex)
  1372                 snprintf(buf, 24, "%.4x", n);
  1373             else
  1374                 snprintf(buf, 24, "%d", n);
  1375             return buf;
  1376         }
  1377 
  1378         #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
  1379 
  1380         static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
  1381         {
  1382             char *_buf = _str(n, hex);
  1383             if (!_buf)
  1384                 return PEP_OUT_OF_MEMORY;
  1385             PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
  1386             free(_buf);
  1387             return status;
  1388         }
  1389 
  1390         #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
  1391         #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
  1392         #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
  1393 
  1394         «@name»_state fsm_«@name»(
  1395                 PEP_SESSION session,
  1396                 «@name»_state state,
  1397                 «@name»_event event
  1398             )
  1399         {
  1400             assert(session);
  1401             if (!session)
  1402                 return invalid_state;
  1403 
  1404             if (state == None)
  1405                 state = «@name»_state_Init;
  1406 
  1407             switch (state) {
  1408                 `` apply "state", 2, mode=fsm
  1409                 default:
  1410                     «@name»_ERR_LOG("invalid state", «@name»_state_name(state));
  1411                     assert(0);
  1412                     return invalid_state;
  1413             }
  1414             
  1415             return None;
  1416         }
  1417 
  1418         ||
  1419         }
  1420     }
  1421     
  1422     template "state", mode=fsm {
  1423         choose {
  1424             when "@name='InitState'" | case «../@name»_state_Init:
  1425             otherwise | case «@name»:
  1426         }
  1427         ||
  1428             «../@name»_SERVICE_LOG("in state", "«@name»");
  1429 
  1430             switch (event) {
  1431                 case None:
  1432                     «../@name»_SERVICE_LOG("received Timeout event", "ignoring");
  1433                     break;
  1434      
  1435         ||
  1436         if "not(event[@name='Init'])"
  1437         ||
  1438                 case Init:
  1439                     «../@name»_SERVICE_LOG("received Init but nothing to do", "Init");
  1440                     break;
  1441 
  1442         ||
  1443         ||
  1444                 `` apply "event", 2, mode=fsm
  1445                 default:
  1446                     // ignore events not handled here
  1447                     «../@name»_SERVICE_LOG("ignoring event", «../@name»_event_name(event));
  1448                     return invalid_event;
  1449             }
  1450             break;
  1451 
  1452         ||
  1453     }
  1454 
  1455     template "event", mode=fsm {
  1456         | case «@name»: {
  1457         if "condition|action|send" |> PEP_STATUS status;
  1458         if "condition" |> bool result = false;
  1459         if "condition|action|send" |
  1460         ||
  1461             «../../@name»_SERVICE_LOG("received event", "«@name»");
  1462             `` apply "transition|action|condition|else|send|debug";
  1463         ||
  1464         if "name(*[last()])!='transition'" {
  1465             |
  1466             |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
  1467             |> break;
  1468         }
  1469         ||
  1470         }
  1471         
  1472         ||
  1473     }
  1474 
  1475     template "transition" {
  1476         const "fsm", "ancestor::fsm";
  1477         ||
  1478 
  1479         «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
  1480         return «@target»;
  1481         ||
  1482     }
  1483 
  1484     template "send" {
  1485         const "fsm", "ancestor::fsm";
  1486         const "protocol", "ancestor::protocol";
  1487         ||
  1488 
  1489         «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
  1490         status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»_PR_«yml:mixedCase(@name)»);
  1491         if (status == PEP_OUT_OF_MEMORY)
  1492             return out_of_memory;
  1493         if (status) {
  1494             «$fsm/@name»_ERR_LOG_HEX("sending «@name» failed", status);
  1495             return cannot_send;
  1496         }
  1497         ||
  1498     }
  1499 
  1500     template "debug"
  1501         | KeySync_SERVICE_LOG("«.»", "«ancestor::protocol/@name»");
  1502 
  1503     template "action" {
  1504         const "fsm", "ancestor::fsm";
  1505         ||
  1506 
  1507         «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
  1508         status = «@name»(session);
  1509         if (status == PEP_OUT_OF_MEMORY)
  1510             return out_of_memory;
  1511         if (status) {
  1512             «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
  1513             assert(0);
  1514             return invalid_action;
  1515         }
  1516         ||
  1517     }
  1518 
  1519     template "condition" {
  1520         const "fsm", "ancestor::fsm";
  1521         ||
  1522 
  1523         status = «@name»(session, &result);
  1524         if (status == PEP_OUT_OF_MEMORY)
  1525             return out_of_memory;
  1526         if (status) {
  1527             «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
  1528             assert(0);
  1529             return invalid_condition;
  1530         }
  1531         if (result) {
  1532             «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
  1533         ||
  1534         apply "transition|action|condition|else|send|debug";
  1535         | }
  1536     }
  1537 
  1538     template "else" {
  1539         if "not(name(preceding-sibling::*[1]) = 'condition')"
  1540             error "else without if";
  1541 
  1542         | else {
  1543         |> «ancestor::fsm/@name»_SERVICE_LOG("condition does not apply", "«preceding-sibling::*[last()]/@name»");
  1544         apply "transition|action|condition|else|send|debug";
  1545         | }
  1546     }
  1547 }