sync/gen_statemachine.ysl2
author Thomas
Fri, 07 Jun 2019 07:41:36 +0200
branchsequoia_windows
changeset 3819 0c1bcfa6f9f3
parent 3779 c6898c703a27
child 3912 506818fe8cbd
permissions -rw-r--r--
Merge with sync
     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         «@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         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         «@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         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             `` for "fsm" | #include "«@name»_fsm.h"
   235 
   236             `` apply "fsm", 0, mode=timeout
   237             PEP_STATUS «@name»_driver(
   238                     PEP_SESSION session,
   239                     «@name»_PR fsm,
   240                     int event
   241                 )
   242             {
   243                 assert(session);
   244                 if (!session)
   245                     return PEP_ILLEGAL_VALUE;
   246 
   247                 switch (fsm) {
   248                     case None:
   249                         if (!event) {
   250                             // timeout occured
   251                         `` for "fsm" |>>>> «../@name»_driver(session, «../@name»_PR_«yml:lcase(@name)», None);
   252                             return PEP_STATUS_OK;
   253                         }
   254                         return PEP_ILLEGAL_VALUE;
   255 
   256                     `` apply "fsm", mode=reset_state_machine;
   257                     default:
   258                         return PEP_ILLEGAL_VALUE;
   259                 }
   260 
   261                 int next_state = None;
   262                 do {
   263                     switch (fsm) {
   264                         `` apply "fsm", 3, mode=driver               
   265                         default:
   266                             return PEP_ILLEGAL_VALUE;
   267                     }
   268                 }  while (next_state);
   269 
   270                 return PEP_STATUS_OK;
   271             }
   272 
   273             PEP_STATUS signal_«@name»_event(
   274                     PEP_SESSION session, 
   275                     «@name»_PR fsm,
   276                     int event,
   277                     identity_list *own_identities
   278                 )
   279             {
   280                 «@name»_t *msg = NULL;
   281                 «@name»_event_t *ev = NULL;
   282 
   283                 assert(session && fsm > 0 && event > None);
   284                 if (!(session && fsm > 0 && event > None))
   285                     return PEP_ILLEGAL_VALUE;
   286 
   287                 PEP_STATUS status = PEP_STATUS_OK;
   288 
   289                 if (!session->inject_«yml:lcase(@name)»_event)
   290                    return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
   291 
   292                 if (event < Extra) {
   293                     msg = new_«@name»_message(fsm, event);
   294                     if (!msg) {
   295                         status = PEP_OUT_OF_MEMORY;
   296                         goto the_end;
   297                     }
   298 
   299                     status = update_«@name»_message(session, msg);
   300                     if (status)
   301                         goto the_end;
   302                 }
   303 
   304                 ev = new_«@name»_event(fsm, event, msg);
   305                 if (!ev) {
   306                     status = PEP_OUT_OF_MEMORY;
   307                     goto the_end;
   308                 }
   309 
   310                 ev->own_identities = own_identities;
   311 
   312                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   313                         session->«yml:lcase(@name)»_management);
   314                 if (result) {
   315                     status = PEP_STATEMACHINE_ERROR;
   316                     goto the_end;
   317                 }
   318                 return PEP_STATUS_OK;
   319 
   320             the_end:
   321                 free_«@name»_event(ev); // msg gets freed here
   322                 return status;
   323             }
   324 
   325             PEP_STATUS signal_«@name»_message(
   326                     PEP_SESSION session, 
   327                     PEP_rating rating,
   328                     const char *data,
   329                     size_t size,
   330                     const pEp_identity *from,
   331                     const char *sender_fpr
   332                 )
   333             {
   334                 assert(session && data && size);
   335                 if (!(session && data && size))
   336                     return PEP_ILLEGAL_VALUE;
   337 
   338                 if (!session->inject_«yml:lcase(@name)»_event)
   339                    return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
   340 
   341                 PEP_STATUS status = PEP_STATUS_OK;
   342                 «@name»_event_t *ev = NULL;
   343 
   344                 «@name»_t *msg = NULL;
   345                 status = decode_«@name»_message(data, size, &msg);
   346                 if (status)
   347                     return status;
   348 
   349                 «@name»_PR fsm = msg->present;
   350                 int event = 0;
   351                 bool is_own_key = false;
   352 
   353                 switch (fsm) {
   354                     `` apply "fsm", 2, mode=signal_message
   355                     default:
   356                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   357                         goto the_end;
   358                 }
   359 
   360                 ev = new_«@name»_event(fsm, event, msg);
   361                 if (!ev) {
   362                     status = PEP_OUT_OF_MEMORY;
   363                     goto the_end;
   364                 }
   365 
   366                 // add transport data
   367 
   368                 if (from) {
   369                     ev->from = identity_dup(from);
   370                     if (!ev->from) {
   371                         status = PEP_OUT_OF_MEMORY;
   372                         goto the_end;
   373                     }
   374                 }
   375 
   376                 if (sender_fpr) {
   377                     ev->sender_fpr = strdup(sender_fpr);
   378                     assert(ev->sender_fpr);
   379                     if (!ev->sender_fpr) {
   380                         status = PEP_OUT_OF_MEMORY;
   381                         goto the_end;
   382                     }
   383                 }
   384 
   385                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   386                         session->«yml:lcase(@name)»_management);
   387                 if (result) {
   388                     status = PEP_STATEMACHINE_ERROR;
   389                     goto the_end;
   390                 }
   391 
   392                 return PEP_STATUS_OK;
   393 
   394             the_end:
   395                 free_«@name»_event(ev); // msg gets freed here
   396                 return status;
   397             }
   398 
   399             PEP_STATUS send_«@name»_message(
   400                     PEP_SESSION session, 
   401                     «@name»_PR fsm,
   402                     int message_type
   403                 )
   404             {
   405                 PEP_STATUS status = PEP_STATUS_OK;
   406 
   407                 assert(session && fsm > None && message_type > None);
   408                 if (!(session && fsm > None && message_type > None))
   409                     return PEP_ILLEGAL_VALUE;
   410                 
   411                 «@name»_t *msg = new_«@name»_message(fsm, message_type);
   412                 if (!msg)
   413                     return PEP_OUT_OF_MEMORY;
   414 
   415                 char *data = NULL;
   416                 message *m = NULL;
   417                 identity_list *channels = NULL;
   418                 char *key_data = NULL;
   419                 size_t key_data_size = 0;
   420                 stringlist_t *extra = NULL;
   421                 bool transaction;
   422 
   423                 status = update_«@name»_message(session, msg);
   424                 if (status)
   425                     goto the_end;
   426 
   427                 size_t size = 0;
   428                 status = encode_«@name»_message(msg, &data, &size);
   429                 if (status)
   430                     goto the_end;
   431 
   432                 switch (message_type) {
   433                     // these messages are being broadcasted
   434                     `` for "fsm/message[@type='broadcast']" |>> case «../@name»_PR_«yml:mixedCase(@name)»:
   435                         status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   436                         if (status)
   437                             goto the_end;
   438 
   439                         if (!(channels && channels->ident)) {
   440                             // status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   441                             // we don't check for having a channel, because if
   442                             // this is initial setup before having an own
   443                             // identity we're fine
   444                             goto the_end;
   445                         }
   446                         break;
   447 
   448                     // these go anycast; previously used address is sticky (unicast)
   449                     `` for "fsm/message[@type='anycast']" |>> case «../@name»_PR_«yml:mixedCase(@name)»:
   450                         if (!session->«yml:lcase(@name)»_state.transport.from `> |`|
   451                             (session->«yml:lcase(@name)»_state.transport.from->flags &
   452                             PEP_idf_not_for_«yml:lcase(@name)»)) {
   453 
   454                             // no address available yet, try to find one
   455                             status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   456                             if (status)
   457                                 goto the_end;
   458                             break;
   459 
   460                             if (channels && channels->ident) {
   461                                 // only need the first one
   462                                 free_identity_list(channels->next);
   463                                 channels->next = NULL;
   464                             }
   465                             else {
   466                                 status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   467                                 goto the_end;
   468                             }
   469                         }
   470                         else {
   471                             pEp_identity *channel = identity_dup(session->«yml:lcase(@name)»_state.transport.from);
   472                             if (!channel) {
   473                                 status = PEP_OUT_OF_MEMORY;
   474                                 goto the_end;
   475                             }
   476 
   477                             channels = new_identity_list(channel);
   478                             if (!channels) {
   479                                 status = PEP_OUT_OF_MEMORY;
   480                                 goto the_end;
   481                             }
   482                         }
   483                         break;
   484 
   485                     default:
   486                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   487                         goto the_end;
   488                 }
   489 
   490                 for (identity_list *li = channels; li && li->ident ; li = li->next) {
   491                     message *_m = NULL;
   492                     char *_data = NULL;
   493                     
   494                     _data = malloc(size);
   495                     assert(_data);
   496                     if (!_data) {
   497                         status = PEP_OUT_OF_MEMORY;
   498                         goto the_end;
   499                     }
   500                     memcpy(_data, data, size);
   501 
   502                     switch (message_type) {
   503                     `` for "fsm/message[@security='unencrypted']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
   504                             status = base_prepare_message(
   505                                     session,
   506                                     li->ident,
   507                                     li->ident,
   508                                     _data,
   509                                     size,
   510                                     li->ident->fpr,
   511                                     &_m
   512                                 );
   513                             if (status) {
   514                                 free(_data);
   515                                 goto the_end;
   516                             }
   517                             attach_own_key(session, _m);
   518                             m = _m;
   519                             break;
   520 
   521                     `` for "fsm/message[@security='untrusted']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
   522                             // add fpr of key of comm partner
   523 
   524                             assert(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
   525                             if (!session->«yml:lcase(@name)»_state.comm_partner.sender_fpr) {
   526                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   527                                 goto the_end;
   528                             }
   529 
   530                             extra = new_stringlist(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
   531                             if (!extra) {
   532                                 status = PEP_OUT_OF_MEMORY;
   533                                 goto the_end;
   534                             }
   535 
   536                             status = base_prepare_message(
   537                                     session,
   538                                     li->ident,
   539                                     li->ident,
   540                                     _data,
   541                                     size,
   542                                     NULL,
   543                                     &_m
   544                                 );
   545                             if (status) {
   546                                 free(_data);
   547                                 goto the_end;
   548                             }
   549 
   550                             status = encrypt_message(session, _m, extra, &m, PEP_enc_PEP, 0);
   551                             if (status) {
   552                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   553                                 goto the_end;
   554                             }
   555                             free_message(_m);
   556                             break;
   557 
   558                     `` for "fsm/message[@security='attach_own_keys']" |>>> case «../@name»_PR_«yml:mixedCase(@name)»:
   559                             // check if we had a former negotiation
   560 
   561                             transaction = false;
   562                             for (int i=0; i < session->«yml:lcase(@name)»_state.own.negotiation.size; i++) {
   563                                 if (session->«yml:lcase(@name)»_state.own.negotiation.buf[i]) {
   564                                     transaction = true;
   565                                     break;
   566                                 }
   567                             }
   568     
   569                             // if it is a former negotiation check if the key
   570                             // is fully trusted and the sender key of this
   571                             // transaction; if so add the sender key to extra
   572                             // keys allowing this new partner to read the
   573                             // secret keys
   574 
   575                             if (transaction) {
   576                                 assert(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr &&
   577                                     session->«yml:lcase(@name)»_state.transport.from &&
   578                                     session->«yml:lcase(@name)»_state.transport.from->user_id);
   579                                 if (!(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr &&
   580                                         session->«yml:lcase(@name)»_state.transport.from &&
   581                                         session->«yml:lcase(@name)»_state.transport.from->user_id))
   582                                 {
   583                                     status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   584                                     goto the_end;
   585                                 }
   586                             
   587                                 // test if this is a green channel
   588 
   589                                 pEp_identity *ident = new_identity(NULL,
   590                                         session->«yml:lcase(@name)»_state.comm_partner.sender_fpr,
   591                                         session->«yml:lcase(@name)»_state.transport.from->user_id,
   592                                         NULL
   593                                     );
   594                                 if (!ident) {
   595                                     status = PEP_OUT_OF_MEMORY;
   596                                     goto the_end;
   597                                 }
   598                                 status = get_trust(session, ident);
   599                                 if (status) {
   600                                     free_identity(ident);
   601                                     goto the_end;
   602                                 }
   603                                 assert(ident->comm_type == PEP_ct_pEp); // we don't deliver otherwise
   604                                 if (ident->comm_type != PEP_ct_pEp) {
   605                                     free_identity(ident);
   606                                     status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   607                                     goto the_end;
   608                                 }
   609                                 free_identity(ident);
   610 
   611                                 // test if we accepted this as own key already
   612 
   613                                 bool is_own_key = false;
   614                                 status = own_key_is_listed(session,
   615                                         session->«yml:lcase(@name)»_state.comm_partner.sender_fpr,
   616                                         &is_own_key);
   617                                 assert(!status);
   618                                 if (status)
   619                                     goto the_end;
   620                                 assert(is_own_key);
   621                                 if (!is_own_key) {
   622                                     status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   623                                     goto the_end;
   624                                 }
   625 
   626                                 // if so add key of comm partner to extra keys
   627 
   628                                 extra = new_stringlist(session->«yml:lcase(@name)»_state.comm_partner.sender_fpr);
   629                                 if (!extra) {
   630                                     status = PEP_OUT_OF_MEMORY;
   631                                     goto the_end;
   632                                 }
   633                             }
   634                             
   635                             status = base_prepare_message(
   636                                     session,
   637                                     li->ident,
   638                                     li->ident,
   639                                     _data,
   640                                     size,
   641                                     NULL,
   642                                     &_m
   643                                 );
   644                             if (status) {
   645                                 free(_data);
   646                                 goto the_end;
   647                             }
   648 
   649                             // export secret keys into memory
   650 
   651                             key_data = strdup("");
   652                             assert(key_data);
   653                             if (!key_data) {
   654                                 free(_data);
   655                                 free_message(_m);
   656                                 status = PEP_OUT_OF_MEMORY;
   657                                 goto the_end;
   658                             }
   659                             key_data_size = 1; // N.B. If null termination makes us happy for debugging, fine, but 
   660                                                // if we include this in the size, libetpan will null terminate and 
   661                                                // go bananas. We can't have a NUL in the mime text.
   662 
   663                             for (stringlist_t *sl = session->«yml:lcase(@name)»_state.own.keys;
   664                                     sl && sl->value ; sl = sl->next)
   665                             {
   666                                 char *_key_data = NULL;
   667                                 size_t _size = 0;
   668                                 status = export_secret_key(session, sl->value, &_key_data, &_size);
   669                                 if (status && status != PEP_KEY_NOT_FOUND) {
   670                                     free(_data);
   671                                     free_message(_m);
   672                                     goto the_end;
   673                                 }
   674 
   675                                 if (status != PEP_KEY_NOT_FOUND) {
   676                                     assert(_key_data && _size);
   677                                     char *n = realloc(key_data, key_data_size + _size);
   678                                     if (!n) {
   679                                         free(_data);
   680                                         free_message(_m);
   681                                         status = PEP_OUT_OF_MEMORY;
   682                                         goto the_end;
   683                                     }
   684                                     key_data = n;
   685                                     key_data_size += _size;
   686                                     strlcat(key_data, _key_data, key_data_size);
   687                                     free(_key_data);
   688                                     _key_data = NULL;
   689                                 }
   690                                 status = export_key(session, sl->value, &_key_data, &_size);
   691                                 if (status && status != PEP_KEY_NOT_FOUND) {
   692                                     free(_data);
   693                                     free_message(_m);
   694                                     goto the_end;
   695                                 }
   696 
   697                                 if (status != PEP_KEY_NOT_FOUND) {
   698                                     assert(_key_data && _size);
   699                                     char *n = realloc(key_data, key_data_size + _size);
   700                                     if (!n) {
   701                                         free(_data);
   702                                         free_message(_m);
   703                                         status = PEP_OUT_OF_MEMORY;
   704                                         goto the_end;
   705                                     }
   706                                     key_data = n;
   707                                     key_data_size += _size;
   708                                     strlcat(key_data, _key_data, key_data_size);
   709                                     free(_key_data);
   710                                     _key_data = NULL;
   711                                 }
   712                             }
   713 
   714                             // add secret key data as attachment
   715 
   716                             // N.B. The -1 makes sure we do NOT add a NUL into the mime stream!
   717                             bloblist_t *bl = bloblist_add(_m->attachments, key_data, key_data_size - 1,
   718                                     "application/octet-stream", "file://own.key");
   719                             if (!bl) {
   720                                 free(_data);
   721                                 free_message(_m);
   722                                 status = PEP_OUT_OF_MEMORY;
   723                                 goto the_end;
   724                             }
   725                             key_data = NULL;
   726 
   727                             status = encrypt_message(session, _m, extra, &m, PEP_enc_PEP, 0);
   728                             if (status) {
   729                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   730                                 goto the_end;
   731                             }
   732                             free_message(_m);
   733                             break;
   734 
   735                         default: // security=trusted only
   736                             status = base_prepare_message(
   737                                     session,
   738                                     li->ident,
   739                                     li->ident,
   740                                     _data,
   741                                     size,
   742                                     NULL,
   743                                     &_m
   744                                 );
   745                             if (status) {
   746                                 free(_data);
   747                                 goto the_end;
   748                             }
   749 
   750                             status = encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
   751                             if (status) {
   752                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   753                                 goto the_end;
   754                             }
   755                             free_message(_m);
   756                     }
   757 
   758                     status = session->messageToSend(m);
   759                     m = NULL;
   760                 }
   761 
   762             the_end:
   763                 free_stringlist(extra);
   764                 free_identity_list(channels);
   765                 free_message(m);
   766                 free(data);
   767                 free(key_data);
   768                 free_«@name»_message(msg);
   769                 if (status)
   770                     SERVICE_ERROR_LOG(session, "send_«@name»_message()", status);
   771                 return status;
   772             }
   773 
   774             PEP_STATUS recv_«@name»_event(
   775                     PEP_SESSION session,
   776                     «@name»_event_t *ev
   777                 )
   778             {
   779                 assert(session && ev);
   780                 if (!(session && ev))
   781                     return PEP_ILLEGAL_VALUE;
   782 
   783                 PEP_STATUS status = PEP_STATUS_OK;
   784                 «@name»_PR fsm = (int) None;
   785                 int event = None;
   786 
   787                 if (ev->event > None && ev->event < Extra) {
   788                     status = update_«@name»_state(session, ev->msg, &fsm, &event);
   789                     if (status)
   790                         goto the_end;
   791 
   792                     if (ev->fsm) {
   793                         if (ev->fsm != fsm |`> |` ev->event != event) {
   794                             status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   795                             goto the_end;
   796                         }
   797                     }
   798                     else if (ev->event) {
   799                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   800                         goto the_end;
   801                     }
   802                 }
   803                 else {
   804                     fsm = ev->fsm;
   805                     event = ev->event;
   806                 }
   807 
   808                 // update transport data
   809 
   810                 if (ev->from) {
   811                     free_identity(session->«yml:lcase(@name)»_state.transport.from);
   812                     session->«yml:lcase(@name)»_state.transport.from = ev->from;
   813                     ev->from = NULL;
   814                 }
   815 
   816                 if (ev->sender_fpr) {
   817                     free(session->«yml:lcase(@name)»_state.transport.sender_fpr);
   818                     session->«yml:lcase(@name)»_state.transport.sender_fpr = ev->sender_fpr;
   819                     ev->sender_fpr = NULL;
   820                 }
   821 
   822                 // update own identities
   823 
   824                 if (ev->own_identities && ev->own_identities->ident) {
   825                     free_identity_list(session->«yml:lcase(@name)»_state.own.identities);
   826                     session->«yml:lcase(@name)»_state.own.identities = ev->own_identities;
   827                     ev->own_identities = NULL;
   828                 }
   829 
   830                 status = «@name»_driver(session, fsm, event);
   831 
   832             the_end:
   833                 //free_«@name»_event(ev); // FIXME: We don't own this pointer. Are we sure it gets freed externally?
   834                 return status;
   835             }
   836 
   837             ||
   838         }
   839 
   840         apply "fsm", 0, mode=gen;
   841     }
   842 
   843     template "fsm", mode=timeout
   844     ||
   845     static bool _«@name»_timeout(int state)
   846     {
   847         static int last_state = None;
   848         static time_t switch_time = 0;
   849 
   850         if (state > Init) {
   851             if (state == last_state) {
   852                 if (time(NULL) - switch_time > «yml:ucase(@name)»_THRESHOLD) {
   853                     last_state = None;
   854                     switch_time = 0;
   855                     return true;
   856                 }
   857             }
   858             else {
   859                 last_state = state;
   860                 switch_time = time(NULL);
   861             }
   862         }
   863         else {
   864             last_state = None;
   865             switch_time = 0;
   866         }
   867 
   868         return false;
   869     }
   870 
   871     ||
   872 
   873     template "fsm", mode=reset_state_machine
   874     ||
   875         case «../@name»_PR_«yml:lcase(@name)»: {
   876             int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
   877             switch (state) {
   878                 `` for "state[@name!='InitState' and @timeout != 'off']" |>>> case «@name»:
   879                     if (_«@name»_timeout(state)) {
   880                         session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = Init;
   881                         event = Init;
   882                         `` if "@threshold > 0" |>>>>> «@name»TimeoutHandler(session);
   883                     }
   884                     break;
   885                 
   886                 default:
   887                     _«@name»_timeout(None);
   888             }
   889             break;
   890         }
   891 
   892     ||
   893 
   894     template "fsm", mode=signal_message
   895     {
   896         ||
   897         case «../@name»_PR_«yml:lcase(@name)»:
   898             switch (msg->choice.«yml:lcase(@name)».present) {
   899         ||
   900         for "message[@security='unencrypted']" {
   901         if "position()=1" |>> // these messages require a detached signature
   902         ||
   903                 case «../@name»_PR_«yml:mixedCase(@name)»:
   904                     if (!sender_fpr) {
   905                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   906                         goto the_end;
   907                     }
   908                     event = «@name»;
   909                     break;
   910 
   911         ||
   912         }
   913         for "message[@security='untrusted']" {
   914         if "position()=1" |>> // these messages must arrive encrypted
   915         ||
   916                 case «../@name»_PR_«yml:mixedCase(@name)»:
   917                     if (rating < PEP_rating_reliable) {
   918                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   919                         goto the_end;
   920                     }
   921                     event = «@name»;
   922                     break;
   923 
   924         ||
   925         }
   926         for "message[@security!='unencrypted' and @security!='untrusted']" {
   927         if "position()=1" |>> // these messages must come through a trusted channel
   928         ||
   929                 case «../@name»_PR_«yml:mixedCase(@name)»:
   930                     if (rating < PEP_rating_trusted) {
   931                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   932                         goto the_end;
   933                     }
   934                     status = own_key_is_listed(session, sender_fpr, &is_own_key);
   935                     if (status)
   936                         goto the_end;
   937                     if (!is_own_key) {
   938                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   939                         goto the_end;
   940                     }
   941                     event = «@name»;
   942                     break;
   943 
   944         ||
   945         }
   946         ||
   947                 default:
   948                     status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   949                     goto the_end;
   950             }
   951             break;
   952 
   953         ||
   954     }
   955 
   956     template "fsm", mode=event
   957     {
   958     ||
   959     case «../@name»_PR_«yml:lcase(@name)»: {
   960         switch (msg->choice.«yml:lcase(@name)».present) {
   961     ||
   962     for "message"
   963     ||
   964             case «../@name»_PR_«yml:mixedCase(@name)»:
   965                 ev->event = «@name»;
   966                 break;
   967     ||
   968     ||
   969             default:
   970                 // unknown message type
   971                 free(ev);
   972                 return NULL;
   973         }
   974         break;
   975     }
   976 
   977     ||
   978     }
   979 
   980     template "fsm", mode=driver
   981     ||
   982     case «../@name»_PR_«yml:lcase(@name)»: {
   983         int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
   984         next_state = fsm_«@name»(session, state, event);
   985         if (next_state > None) {
   986             session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
   987             event = Init;
   988         }
   989         else if (next_state < None) {
   990             return PEP_STATEMACHINE_ERROR - next_state;
   991         }
   992         break;
   993     }
   994 
   995     ||
   996 
   997     template "fsm", mode=gen {
   998         document "generated/{@name}_fsm.h", "text" {
   999         ||
  1000         // This file is under GNU General Public License 3.0
  1001         // see LICENSE.txt
  1002 
  1003         #pragma once
  1004 
  1005         #include "«../@name»_impl.h"
  1006 
  1007         #ifdef __cplusplus
  1008         extern "C" {
  1009         #endif
  1010 
  1011         // state machine for «@name»
  1012 
  1013         // states
  1014 
  1015         typedef enum _«@name»_state {
  1016             «@name»_state_None = None,
  1017             «@name»_state_Init = Init,
  1018         ||
  1019         for "func:distinctName(state[not(@name='InitState')])"
  1020             |> «@name»`if "position()!=last()" > , `
  1021         ||
  1022         } «@name»_state;
  1023 
  1024         // events
  1025 
  1026         typedef enum _«@name»_event {
  1027             «@name»_event_None = None,
  1028             «@name»_event_Init = Init,
  1029         ||
  1030         for "message" {
  1031             const "name", "@name";
  1032             |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
  1033         }
  1034         |> «@name»_event_Extra = Extra,
  1035         for "external" {
  1036             if "@id < 128"
  1037                 error > external «@name» must have ID >= 128 but it's «@id»
  1038             |> «@name» = «@id»,
  1039         }
  1040         for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
  1041             if "@name!='Init'"
  1042                 |> «@name»`if "position()!=last()" > , `
  1043         }
  1044         ||
  1045         } «@name»_event;
  1046 
  1047         // state machine
  1048 
  1049         const char *«@name»_state_name(int state);
  1050         const char *«@name»_event_name(int event);
  1051 
  1052         // the state machine function is returning the next state in case of a
  1053         // transition or None for staying
  1054 
  1055         «@name»_state fsm_«@name»(
  1056                 PEP_SESSION session,
  1057                 «@name»_state state,
  1058                 «@name»_event event
  1059             );
  1060 
  1061         #ifdef __cplusplus
  1062         }
  1063         #endif
  1064 
  1065         ||
  1066         }
  1067 
  1068         document "generated/{@name}_fsm.c", "text" {
  1069         ||
  1070         // This file is under GNU General Public License 3.0
  1071         // see LICENSE.txt
  1072 
  1073         #include "«@name»_fsm.h"
  1074         #include <stdlib.h>
  1075 
  1076         const char *«@name»_state_name(int state)
  1077         {
  1078             switch (state) {
  1079                 case End:
  1080                     return "End";
  1081                 case None:
  1082                     return "None";
  1083                 case Init:
  1084                     return "InitState";
  1085         ||
  1086         for "func:distinctName(state[not(@name='InitState')])" {
  1087             |>> case «@name»:
  1088             |>>> return "«@name»";
  1089         }
  1090         ||
  1091                 default:
  1092                     return "unknown state";
  1093             }
  1094         }
  1095 
  1096         const char *«@name»_event_name(int event)
  1097         {
  1098             switch (event) {
  1099                 case None:
  1100                     return "None";
  1101                 case Init:
  1102                     return "Init";
  1103         ||
  1104         for "func:distinctName(state/event[not(@name='Init')])" {
  1105             |>> case «@name»:
  1106             |>>> return "«@name»";
  1107         }
  1108         ||
  1109                 default:
  1110                     return "unknown event";
  1111             }
  1112         }
  1113 
  1114 
  1115         static char *_str(int n, bool hex)
  1116         {
  1117             char *buf = calloc(1, 24);
  1118             assert(buf);
  1119             if (!buf)
  1120                 return NULL;
  1121 
  1122             if (hex)
  1123                 snprintf(buf, 24, "%.4x", n);
  1124             else
  1125                 snprintf(buf, 24, "%d", n);
  1126             return buf;
  1127         }
  1128 
  1129         #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
  1130 
  1131         static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
  1132         {
  1133             char *_buf = _str(n, hex);
  1134             if (!_buf)
  1135                 return PEP_OUT_OF_MEMORY;
  1136             PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
  1137             free(_buf);
  1138             return status;
  1139         }
  1140 
  1141         #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
  1142         #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
  1143         #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
  1144 
  1145         «@name»_state fsm_«@name»(
  1146                 PEP_SESSION session,
  1147                 «@name»_state state,
  1148                 «@name»_event event
  1149             )
  1150         {
  1151             assert(session);
  1152             if (!session)
  1153                 return invalid_state;
  1154 
  1155             if (state == None)
  1156                 state = «@name»_state_Init;
  1157 
  1158             switch (state) {
  1159                 `` apply "state", 2, mode=fsm
  1160                 default:
  1161                     «@name»_ERR_LOG_INT("invalid state", state);
  1162                     return invalid_state;
  1163             }
  1164             
  1165             return None;
  1166         }
  1167 
  1168         ||
  1169         }
  1170     }
  1171     
  1172     template "state", mode=fsm {
  1173         choose {
  1174             when "@name='InitState'" | case «../@name»_state_Init:
  1175             otherwise | case «@name»:
  1176         }
  1177         ||
  1178             «../@name»_SERVICE_LOG("in state", "«@name»");
  1179 
  1180             switch (event) {
  1181                 case None:
  1182                     «../@name»_SERVICE_LOG("received None event", "ignoring");
  1183                     break;
  1184      
  1185         ||
  1186         if "not(event[@name='Init'])"
  1187         ||
  1188                 case Init:
  1189                     «../@name»_SERVICE_LOG("received Init but nothing to do", "Init");
  1190                     break;
  1191 
  1192         ||
  1193         ||
  1194                 `` apply "event", 2, mode=fsm
  1195                 default:
  1196                     // ignore events not handled here
  1197                     «../@name»_SERVICE_LOG("ignoring event", «../@name»_event_name(event));
  1198                     return invalid_event;
  1199             }
  1200             break;
  1201 
  1202         ||
  1203     }
  1204 
  1205     template "event", mode=fsm {
  1206         | case «@name»: {
  1207         if "condition|action|send" |> PEP_STATUS status;
  1208         if "condition" |> bool result = false;
  1209         if "condition|action|send" |
  1210         ||
  1211             «../../@name»_SERVICE_LOG("received event", "«@name»");
  1212             `` apply "transition|action|condition|else|send";
  1213         ||
  1214         if "name(*[last()])!='transition'" {
  1215             |
  1216             |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
  1217             |> break;
  1218         }
  1219         ||
  1220         }
  1221         
  1222         ||
  1223     }
  1224 
  1225     template "transition" {
  1226         const "fsm", "ancestor::fsm";
  1227         ||
  1228 
  1229         «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
  1230         return «@target»;
  1231         ||
  1232     }
  1233 
  1234     template "send" {
  1235         const "fsm", "ancestor::fsm";
  1236         const "protocol", "ancestor::protocol";
  1237         ||
  1238 
  1239         «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
  1240         status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»_PR_«yml:mixedCase(@name)»);
  1241         if (status == PEP_OUT_OF_MEMORY)
  1242             return out_of_memory;
  1243         if (status) {
  1244             «$fsm/@name»_ERR_LOG_HEX("sending «@name» failed", status);
  1245             return cannot_send;
  1246         }
  1247         ||
  1248     }
  1249 
  1250     template "action" {
  1251         const "fsm", "ancestor::fsm";
  1252         ||
  1253 
  1254         «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
  1255         status = «@name»(session);
  1256         if (status == PEP_OUT_OF_MEMORY)
  1257             return out_of_memory;
  1258         if (status) {
  1259             «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
  1260             return invalid_action;
  1261         }
  1262         ||
  1263     }
  1264 
  1265     template "condition" {
  1266         const "fsm", "ancestor::fsm";
  1267         ||
  1268 
  1269         status = «@name»(session, &result);
  1270         if (status == PEP_OUT_OF_MEMORY)
  1271             return out_of_memory;
  1272         if (status) {
  1273             «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
  1274             return invalid_condition;
  1275         }
  1276         if (result) {
  1277             «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
  1278         ||
  1279         apply "transition|action|condition|else|send";
  1280         | }
  1281     }
  1282 
  1283     template "else" {
  1284         if "not(name(preceding-sibling::*[1]) = 'condition')"
  1285             error "else without if";
  1286 
  1287         | else {
  1288         |> «ancestor::fsm/@name»_SERVICE_LOG("condition does not apply", "«preceding-sibling::*[last()]/@name»");
  1289         apply "transition|action|condition|else|send";
  1290         | }
  1291     }
  1292 }