LCOV - code coverage report
Current view: top level - eventdispatcher - message.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 185 290 63.8 %
Date: 2021-09-19 09:06:58 Functions: 30 30 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2021  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/eventdispatcher
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program; if not, write to the Free Software
      18             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : /** \file
      21             :  * \brief Implementation of the Snap Communicator class.
      22             :  *
      23             :  * This class wraps the C poll() interface in a C++ object with many types
      24             :  * of objects:
      25             :  *
      26             :  * \li Server Connections; for software that want to offer a port to
      27             :  *     which clients can connect to; the server will call accept()
      28             :  *     once a new client connection is ready; this results in a
      29             :  *     Server/Client connection object
      30             :  * \li Client Connections; for software that want to connect to
      31             :  *     a server; these expect the IP address and port to connect to
      32             :  * \li Server/Client Connections; for the server when it accepts a new
      33             :  *     connection; in this case the server gets a socket from accept()
      34             :  *     and creates one of these objects to handle the connection
      35             :  *
      36             :  * Using the poll() function is the easiest and allows us to listen
      37             :  * on pretty much any number of sockets (on my server it is limited
      38             :  * at 16,768 and frankly over 1,000 we probably will start to have
      39             :  * real slowness issues on small VPN servers.)
      40             :  */
      41             : 
      42             : 
      43             : // self
      44             : //
      45             : #include    "eventdispatcher/message.h"
      46             : 
      47             : #include    "eventdispatcher/exception.h"
      48             : 
      49             : 
      50             : // advgetopt lib
      51             : //
      52             : #include    <advgetopt/validator.h>
      53             : 
      54             : 
      55             : // snaplogger lib
      56             : //
      57             : #include    <snaplogger/message.h>
      58             : 
      59             : 
      60             : // snapdev lib
      61             : //
      62             : #include    <snapdev/string_replace_many.h>
      63             : 
      64             : 
      65             : // boost lib
      66             : //
      67             : #include    <boost/algorithm/string.hpp>
      68             : 
      69             : 
      70             : // last include
      71             : //
      72             : #include    <snapdev/poison.h>
      73             : 
      74             : 
      75             : 
      76             : namespace ed
      77             : {
      78             : 
      79             : 
      80             : 
      81             : /** \brief Parse a message from the specified parameter.
      82             :  *
      83             :  * This function transformed the input string in a set of message
      84             :  * fields.
      85             :  *
      86             :  * The message format supported is:
      87             :  *
      88             :  * \code
      89             :  *      ( '<' sent-from-server ':' sent-from-service ' ')? ( ( server ':' )? service '/' )? command ' ' ( parameter_name '=' value ';' )*
      90             :  * \endcode
      91             :  *
      92             :  * The sender "\<sent-from-server:sent-from-service" names are added by
      93             :  * snapcommunicator when it receives a message which is destined for
      94             :  * another service (i.e. not itself). This can be used by the receiver
      95             :  * to reply back to the exact same process if it is a requirement for that
      96             :  * message (i.e. a process that sends a LOCK message, for example,
      97             :  * expects to receive the LOCKED message back as an answer.) Note that
      98             :  * it is assumed that there cannot be more than one service named
      99             :  * 'service' per server. This is enforced by the snapcommunicator
     100             :  * REGISTER function.
     101             :  *
     102             :  * \code
     103             :  *      // to reply to the exact message sender, one can use the
     104             :  *      // following two lines of code:
     105             :  *      //
     106             :  *      reply.set_server(message.get_sent_from_server());
     107             :  *      reply.set_service(message.get_sent_from_service());
     108             :  *
     109             :  *      // or use the reply_to() helper function
     110             :  *      //
     111             :  *      reply.reply_to(message);
     112             :  * \endcode
     113             :  *
     114             :  * The space after the command cannot be there unless parameters follow.
     115             :  * Parameters must be separated by semi-colons.
     116             :  *
     117             :  * The value of a parameter gets quoted when it includes a ';'. Within
     118             :  * the quotes, a Double Quote can be escaped inside by adding a backslash
     119             :  * in front of it (\"). Newline characters (as well as return carriage)
     120             :  * are also escaped using \n and \r respectively. Finally, we have to
     121             :  * escape backslashes themselves by doubling them, so \ becomes \\.
     122             :  *
     123             :  * Note that only parameter values support absolutely any character.
     124             :  * All the other parameters are limited to the Latin alphabet, digits,
     125             :  * and underscores ([A-Za-z0-9_]+). Also all commands are limited
     126             :  * to uppercase letters only.
     127             :  *
     128             :  * \note
     129             :  * The input message is not saved as a cached version of the message
     130             :  * because we assume it may not be 100% optimized (canonicalized.)
     131             :  *
     132             :  * \param[in] message  The string to convert into fields in this
     133             :  *                     message object.
     134             :  *
     135             :  * \return true if the message was successfully parsed; false when an
     136             :  *         error occurs and in that case no fields get modified.
     137             :  *
     138             :  * \sa to_message()
     139             :  * \sa get_sent_from_server()
     140             :  * \sa get_sent_from_service()
     141             :  * \sa reply_to()
     142             :  */
     143           8 : bool message::from_message(std::string const & original_message)
     144             : {
     145          16 :     std::string sent_from_server;
     146          16 :     std::string sent_from_service;
     147          16 :     std::string server;
     148          16 :     std::string service;
     149          16 :     std::string command;
     150          16 :     parameters_t parameters;
     151             : 
     152             :     // someone using telnet to test sending messages will include a '\r'
     153             :     // so run a trim on the message in case it is there
     154             :     //
     155          16 :     std::string msg(original_message);
     156           8 :     boost::trim(msg);
     157           8 :     char const * m(msg.c_str());
     158             : 
     159             :     // sent-from indicated?
     160             :     //
     161           8 :     if(*m != '\0' && *m == '<')
     162             :     {
     163             :         // the name of the server and server sending this message
     164             :         //
     165             :         // First ++m to skip the '<'
     166             :         //
     167           0 :         for(++m; *m != '\0' && *m != ':'; ++m)
     168             :         {
     169           0 :             if(*m == ' ')
     170             :             {
     171             :                 // invalid syntax from input message
     172             :                 //
     173           0 :                 SNAP_LOG_ERROR
     174           0 :                     << "a message with sent_from_server must not include a space in the server name ("
     175             :                     << original_message
     176             :                     << ")."
     177             :                     << SNAP_LOG_SEND;
     178           0 :                 return false;
     179             :             }
     180             : 
     181           0 :             sent_from_server += *m;
     182             :         }
     183           0 :         if(*m != '\0')
     184             :         {
     185             :             // First ++m to skip the ':'
     186           0 :             for(++m; *m != '\0' && *m != ' '; ++m)
     187             :             {
     188           0 :                 sent_from_service += *m;
     189             :             }
     190             :         }
     191           0 :         if(*m == '\0')
     192             :         {
     193             :             // invalid syntax from input message
     194             :             //
     195           0 :             SNAP_LOG_ERROR
     196           0 :                 << "a message cannot only include a 'sent from service' definition."
     197             :                 << SNAP_LOG_SEND;
     198           0 :             return false;
     199             :         }
     200             :         // Skip the ' '
     201           0 :         ++m;
     202             :     }
     203             : 
     204           8 :     bool has_server(false);
     205           8 :     bool has_service(false);
     206          68 :     for(; *m != '\0' && *m != ' '; ++m)
     207             :     {
     208          30 :         if(*m == ':')
     209             :         {
     210           0 :             if(has_server
     211           0 :             || has_service
     212           0 :             || command.empty())
     213             :             {
     214             :                 // we cannot have more than one ':'
     215             :                 // and the name cannot be empty if ':' is used
     216             :                 // we also cannot have a ':' after the '/'
     217             :                 //
     218           0 :                 SNAP_LOG_ERROR
     219           0 :                     << "a server name cannot be empty when specified, also it cannot include two server names and a server name after a service name was specified."
     220             :                     << SNAP_LOG_SEND;
     221           0 :                 return false;
     222             :             }
     223           0 :             has_server = true;
     224           0 :             server = command;
     225           0 :             command.clear();
     226             :         }
     227          30 :         else if(*m == '/')
     228             :         {
     229           0 :             if(has_service
     230           0 :             || command.empty())
     231             :             {
     232             :                 // we cannot have more than one '/'
     233             :                 // and the name cannot be empty if '/' is used
     234             :                 //
     235           0 :                 SNAP_LOG_ERROR
     236           0 :                     << "a service name is mandatory when the message includes a slash (/), also it cannot include two service names."
     237             :                     << SNAP_LOG_SEND;
     238           0 :                 return false;
     239             :             }
     240           0 :             has_service = true;
     241           0 :             service = command;
     242           0 :             command.clear();
     243             :         }
     244             :         else
     245             :         {
     246          30 :             command += *m;
     247             :         }
     248             :     }
     249             : 
     250           8 :     if(command.empty())
     251             :     {
     252             :         // command is mandatory
     253             :         //
     254           0 :         SNAP_LOG_ERROR
     255           0 :             << "a command is mandatory in a message."
     256             :             << SNAP_LOG_SEND;
     257           0 :         return false;
     258             :     }
     259             : 
     260             :     // if we have a space, we expect one or more parameters
     261             :     //
     262           8 :     if(*m == ' ')
     263             :     {
     264           6 :         for(++m; *m != '\0';)
     265             :         {
     266             :             // first we have to read the parameter name (up to the '=')
     267             :             //
     268           4 :             char const *s(m);
     269          24 :             for(; *m != '\0' && *m != '='; ++m);
     270           8 :             std::string const param_name(s, m - s);
     271           4 :             if(param_name.empty())
     272             :             {
     273             :                 // parameters must have a name
     274             :                 //
     275           0 :                 SNAP_LOG_ERROR
     276           0 :                     << "could not accept message because an empty parameter name is not valid."
     277             :                     << SNAP_LOG_SEND;
     278           0 :                 return false;
     279             :             }
     280             :             try
     281             :             {
     282           4 :                 verify_message_name(param_name);
     283             :             }
     284           0 :             catch(event_dispatcher_invalid_message const & e)
     285             :             {
     286             :                 // name is not empty, but it has invalid characters in it
     287             :                 //
     288           0 :                 SNAP_LOG_ERROR
     289           0 :                     << "could not accept message because parameter name \""
     290             :                     << param_name
     291             :                     << "\" is not considered valid: "
     292           0 :                     << e.what()
     293             :                     << SNAP_LOG_SEND;
     294           0 :                 return false;
     295             :             }
     296             : 
     297           4 :             if(*m == '\0'
     298           4 :             || *m != '=')
     299             :             {
     300             :                 // ?!?
     301             :                 //
     302           0 :                 SNAP_LOG_ERROR
     303           0 :                     << "message parameters must be followed by an equal (=) character."
     304             :                     << SNAP_LOG_SEND;
     305           0 :                 return false;
     306             :             }
     307           4 :             ++m;    // skip '='
     308             : 
     309             :             // retrieve the parameter name at first
     310             :             //
     311           8 :             std::string param_value;
     312           4 :             if(*m == '"')
     313             :             {
     314             :                 // quoted parameter
     315             :                 //
     316           0 :                 for(++m; *m != '"'; ++m)
     317             :                 {
     318           0 :                     if(*m == '\0')
     319             :                     {
     320             :                         // closing quote (") is missing
     321             :                         //
     322           0 :                         SNAP_LOG_ERROR
     323           0 :                             << "a quoted message parameter must end with a quote (\")."
     324             :                             << SNAP_LOG_SEND;
     325           0 :                         return false;
     326             :                     }
     327             : 
     328             :                     // restored escaped double quotes
     329             :                     // (note that we do not yet restore other backslashed
     330             :                     // characters, that's done below)
     331             :                     //
     332           0 :                     if(*m == '\\' && m[1] != '\0' && m[1] == '"')
     333             :                     {
     334           0 :                         ++m;
     335             :                     }
     336             :                     // here the character may be ';'
     337             :                     //
     338           0 :                     param_value += *m;
     339             :                 }
     340             : 
     341             :                 // skip the quote
     342             :                 //
     343           0 :                 ++m;
     344             :             }
     345             :             else
     346             :             {
     347             :                 // parameter value is found as is
     348             :                 //
     349          44 :                 for(; *m != '\0' && *m != ';'; ++m)
     350             :                 {
     351          20 :                     param_value += *m;
     352             :                 }
     353             :             }
     354             : 
     355           4 :             if(*m != '\0')
     356             :             {
     357           2 :                 if(*m != ';')
     358             :                 {
     359             :                     // this should never happen
     360             :                     //
     361           0 :                     SNAP_LOG_ERROR
     362           0 :                         << "two parameters must be separated by a semicolon (;)."
     363             :                         << SNAP_LOG_SEND;
     364           0 :                     return false;
     365             :                 }
     366             : 
     367             :                 // skip the ';'
     368             :                 //
     369           2 :                 ++m;
     370             :             }
     371             : 
     372             :             // also restore new lines and backslashes if any
     373             :             //
     374           4 :             std::string const unsafe_value(snap::string_replace_many(
     375             :                     param_value,
     376             :                     {
     377             :                         { "\\\\", "\\" },
     378             :                         { "\\n", "\n" },
     379             :                         { "\\r", "\r" }
     380           8 :                     }));
     381             : 
     382             :             // we got a valid parameter, add it
     383             :             //
     384           4 :             parameters[param_name] = unsafe_value;
     385             :         }
     386             :     }
     387             : 
     388           8 :     f_sent_from_server = sent_from_server;
     389           8 :     f_sent_from_service = sent_from_service;
     390           8 :     f_server = server;
     391           8 :     f_service = service;
     392           8 :     f_command = command;
     393           8 :     f_parameters.swap(parameters);
     394           8 :     f_cached_message.clear();
     395             : 
     396           8 :     return true;
     397             : }
     398             : 
     399             : 
     400             : /** \brief Transform all the message parameters in a string.
     401             :  *
     402             :  * This function transforms all the message parameters in a string
     403             :  * and returns the result. The string is a message we can send over
     404             :  * TCP/IP (if you make sure to add a "\n", note that the
     405             :  * send_message() does that automatically) or over UDP/IP.
     406             :  *
     407             :  * \note
     408             :  * The function caches the result so calling the function many times
     409             :  * will return the same string and thus the function is very fast
     410             :  * after the first call (assuming you do not modify the message on
     411             :  * each call to to_message().)
     412             :  *
     413             :  * \note
     414             :  * The sent-from information gets saved in the message only if both,
     415             :  * the server name and service name it was sent from are defined.
     416             :  *
     417             :  * \exception event_dispatcher_invalid_message
     418             :  * This function raises an exception if the message command was not
     419             :  * defined since a command is always mandatory.
     420             :  *
     421             :  * \return The converted message as a string.
     422             :  *
     423             :  * \sa get_sent_from_server()
     424             :  * \sa get_sent_from_service()
     425             :  * \sa set_reply_to_server()
     426             :  * \sa set_reply_to_service()
     427             :  */
     428          14 : std::string message::to_message() const
     429             : {
     430          14 :     if(f_cached_message.empty())
     431             :     {
     432          14 :         if(f_command.empty())
     433             :         {
     434           0 :             throw event_dispatcher_invalid_message("message::to_message(): cannot build a valid message without at least a command.");
     435             :         }
     436             : 
     437             :         // add info about the sender
     438             :         //
     439             :         // ['<' <sent-from-server> ':' <sent-from-service> ' ']
     440             :         //
     441          28 :         if(!f_sent_from_server.empty()
     442          14 :         || !f_sent_from_service.empty())
     443             :         {
     444           0 :             f_cached_message += '<';
     445           0 :             f_cached_message += f_sent_from_server;
     446           0 :             f_cached_message += ':';
     447           0 :             f_cached_message += f_sent_from_service;
     448           0 :             f_cached_message += ' ';
     449             :         }
     450             : 
     451             :         // add server and optionally the destination server name if both are defined
     452             :         //
     453             :         // ['<' <sent-from-server> ':' <sent-from-service> ' ']
     454             :         //      [[<server> ':'] <name> '/']
     455             :         //
     456          14 :         if(!f_service.empty())
     457             :         {
     458           0 :             if(!f_server.empty())
     459             :             {
     460           0 :                 f_cached_message += f_server;
     461           0 :                 f_cached_message += ':';
     462             :             }
     463           0 :             f_cached_message += f_service;
     464           0 :             f_cached_message += '/';
     465             :         }
     466             : 
     467             :         // ['<' <sent-from-server> ':' <sent-from-service> ' ']
     468             :         //      [[<server> ':'] <name> '/'] <command>
     469             :         //
     470          14 :         f_cached_message += f_command;
     471             : 
     472             :         // add parameters if any
     473             :         //
     474             :         // ['<' <sent-from-server> ':' <sent-from-service> ' ']
     475             :         //      [[<server> ':'] <name> '/'] <command>
     476             :         //      [' ' <param1> '=' <value1>][';' <param2> '=' <value2>]...
     477             :         //
     478          14 :         char sep(' ');
     479          18 :         for(auto p : f_parameters)
     480             :         {
     481           4 :             f_cached_message += sep;
     482           4 :             f_cached_message += p.first;
     483           4 :             f_cached_message += '=';
     484             : 
     485           4 :             std::string safe_value(snap::string_replace_many(
     486             :                     p.second,
     487             :                     {
     488             :                         { "\\", "\\\\" },
     489             :                         { "\n", "\\n" },
     490             :                         { "\r", "\\r" }
     491           8 :                     }));
     492             : 
     493           8 :             if(safe_value.find(';') != std::string::npos
     494           4 :             || (!safe_value.empty() && safe_value[0] == '\"'))
     495             :             {
     496             :                 // escape the double quotes
     497             :                 //
     498           0 :                 boost::algorithm::replace_all(safe_value, "\"", "\\\"");
     499             : 
     500             :                 // quote the resulting parameter and save in f_cached_message
     501             :                 //
     502           0 :                 f_cached_message += '"';
     503           0 :                 f_cached_message += safe_value;
     504           0 :                 f_cached_message += '"';
     505             :             }
     506             :             else
     507             :             {
     508             :                 // no special handling necessary
     509             :                 //
     510           4 :                 f_cached_message += safe_value;
     511             :             }
     512             : 
     513           4 :             sep = ';';
     514             :         }
     515             :     }
     516             : 
     517          14 :     return f_cached_message;
     518             : }
     519             : 
     520             : 
     521             : /** \brief Where this message came from.
     522             :  *
     523             :  * Some services send a message expecting an answer directly sent back
     524             :  * to them. Yet, those services may have multiple instances in your cluster
     525             :  * (i.e. snapcommunicator runs on all computers, snapwatchdog, snapfirewall,
     526             :  * snaplock, snapdbproxy are likely to run on most computers, etc.)
     527             :  * This parameter defines which computer the message came from. Thus,
     528             :  * you can use that information to send the message back to that
     529             :  * specific computer. The snapcommunicator on that computer will
     530             :  * then forward the message to the specified service instance.
     531             :  *
     532             :  * If empty (the default,) then the normal snapcommunicator behavior is
     533             :  * used (i.e. send to any instance of the service that is available.)
     534             :  *
     535             :  * \return The address and port of the specific service this message has to
     536             :  *         be sent to.
     537             :  *
     538             :  * \sa set_sent_from_server()
     539             :  * \sa set_sent_from_service()
     540             :  * \sa get_sent_from_service()
     541             :  */
     542           6 : std::string const & message::get_sent_from_server() const
     543             : {
     544           6 :     return f_sent_from_server;
     545             : }
     546             : 
     547             : 
     548             : /** \brief Set the name of the server that sent this message.
     549             :  *
     550             :  * This function saves the name of the server that was used to
     551             :  * generate the message. This can be used later to send a reply
     552             :  * to the service that sent this message.
     553             :  *
     554             :  * The snapcommunicator tool is actually in charge of setting this
     555             :  * parameter and you should never have to do so from your tool.
     556             :  * The set happens whenever the snapcommunicator receives a
     557             :  * message from a client. If you are not using the snapcommunicator
     558             :  * then you are welcome to use this function for your own needs.
     559             :  *
     560             :  * \param[in] sent_from_server  The name of the source server.
     561             :  *
     562             :  * \sa get_sent_from_server()
     563             :  * \sa get_sent_from_service()
     564             :  * \sa set_sent_from_service()
     565             :  */
     566           1 : void message::set_sent_from_server(std::string const & sent_from_server)
     567             : {
     568           1 :     if(f_sent_from_server != sent_from_server)
     569             :     {
     570             :         // this name can be empty and it supports lowercase
     571             :         //
     572           1 :         verify_message_name(sent_from_server, true);
     573             : 
     574           1 :         f_sent_from_server = sent_from_server;
     575           1 :         f_cached_message.clear();
     576             :     }
     577           1 : }
     578             : 
     579             : 
     580             : /** \brief Who sent this message.
     581             :  *
     582             :  * Some services send messages expecting an answer sent right back to
     583             :  * them. For example, the snaplock tool sends the message LOCKENTERING
     584             :  * and expects the LOCKENTERED as a reply. The reply has to be sent
     585             :  * to the exact same instance t hat sent the LOCKENTERING message.
     586             :  *
     587             :  * In order to do so, the system makes use of the server and service
     588             :  * name the data was sent from. Since the name of each service
     589             :  * registering with snapcommunicator must be unique, it 100% defines
     590             :  * the sender of the that message.
     591             :  *
     592             :  * If empty (the default,) then the normal snapcommunicator behavior is
     593             :  * used (i.e. send to any instance of the service that is available locally,
     594             :  * if not available locally, try to send it to another snapcommunicator
     595             :  * that knows about it.)
     596             :  *
     597             :  * \return The address and port of the specific service this message has to
     598             :  *         be sent to.
     599             :  *
     600             :  * \sa get_sent_from_server()
     601             :  * \sa set_sent_from_server()
     602             :  * \sa set_sent_from_service()
     603             :  */
     604           6 : std::string const & message::get_sent_from_service() const
     605             : {
     606           6 :     return f_sent_from_service;
     607             : }
     608             : 
     609             : 
     610             : /** \brief Set the name of the server that sent this message.
     611             :  *
     612             :  * This function saves the name of the service that sent this message
     613             :  * to snapcommuncator. It is set by snapcommunicator whenever it receives
     614             :  * a message from a service it manages so you do not have to specify this
     615             :  * parameter yourselves.
     616             :  *
     617             :  * This can be used to provide the name of the service to reply to. This
     618             :  * is useful when the receiver does not already know exactly who sends it
     619             :  * certain messages.
     620             :  *
     621             :  * \param[in] sent_from_service  The name of the service that sent this message.
     622             :  *
     623             :  * \sa get_sent_from_server()
     624             :  * \sa set_sent_from_server()
     625             :  * \sa get_sent_from_service()
     626             :  */
     627           1 : void message::set_sent_from_service(std::string const & sent_from_service)
     628             : {
     629           1 :     if(f_sent_from_service != sent_from_service)
     630             :     {
     631             :         // this name can be empty and it supports lowercase
     632             :         //
     633           1 :         verify_message_name(sent_from_service, true);
     634             : 
     635           1 :         f_sent_from_service = sent_from_service;
     636           1 :         f_cached_message.clear();
     637             :     }
     638           1 : }
     639             : 
     640             : 
     641             : /** \brief The server where this message has to be delivered.
     642             :  *
     643             :  * Some services need their messages to be delivered to a service
     644             :  * running on a specific computer. This function returns the name
     645             :  * of that server.
     646             :  *
     647             :  * If the function returns an empty string, then snapcommunicator is
     648             :  * free to send the message to any server.
     649             :  *
     650             :  * \return The name of the server to send this message to or an empty string.
     651             :  *
     652             :  * \sa set_server()
     653             :  * \sa get_service()
     654             :  * \sa set_service()
     655             :  */
     656           5 : std::string const & message::get_server() const
     657             : {
     658           5 :     return f_server;
     659             : }
     660             : 
     661             : 
     662             : /** \brief Set the name of a specific server where to send this message.
     663             :  *
     664             :  * In some cases you may want to send a message to a service running
     665             :  * on a specific server. This function can be used to specify the exact
     666             :  * server where the message has to be delivered.
     667             :  *
     668             :  * This is particularly useful when you need to send a reply to a
     669             :  * specific daemon that sent you a message.
     670             :  *
     671             :  * The name can be set to ".", which means send to a local service
     672             :  * only, whether it is available or not. This option can be used
     673             :  * to avoid/prevent sending a message to other computers.
     674             :  *
     675             :  * The name can be set to "*", which is useful to broadcast the message
     676             :  * to all servers even if the destination service name is
     677             :  * "snapcommunicator".
     678             :  *
     679             :  * \note
     680             :  * This function works hand in hand with the "snapcommunicator". That is,
     681             :  * the snapcommunicator is the one tool that makes use of the server name.
     682             :  * It won't otherwise work with just the library (i.e. you have to know
     683             :  * which client sent you a message to send it back to that client
     684             :  * if you manage an array of clients).
     685             :  *
     686             :  * \param[in] server  The name of the server to send this message to.
     687             :  *
     688             :  * \sa get_server()
     689             :  * \sa get_service()
     690             :  * \sa set_service()
     691             :  */
     692           2 : void message::set_server(std::string const & server)
     693             : {
     694           2 :     if(f_server != server)
     695             :     {
     696             :         // this name can be empty and it supports lowercase
     697             :         //
     698           4 :         if(server != "."
     699           2 :         && server != "*")
     700             :         {
     701           2 :             verify_message_name(server, true);
     702             :         }
     703             : 
     704           2 :         f_server = server;
     705           2 :         f_cached_message.clear();
     706             :     }
     707           2 : }
     708             : 
     709             : 
     710             : /** \brief Retrieve the name of the service the message is for.
     711             :  *
     712             :  * This function returns the name of the service this message is being
     713             :  * sent to.
     714             :  *
     715             :  * \return Destination service.
     716             :  *
     717             :  * \sa get_server()
     718             :  * \sa set_server()
     719             :  * \sa set_service()
     720             :  */
     721           5 : std::string const & message::get_service() const
     722             : {
     723           5 :     return f_service;
     724             : }
     725             : 
     726             : 
     727             : /** \brief Set the name of the service this message is being sent to.
     728             :  *
     729             :  * This function specifies the name of the server this message is expected
     730             :  * to be sent to.
     731             :  *
     732             :  * When a service wants to send a message to snapcommunicator, no service
     733             :  * name is required.
     734             :  *
     735             :  * \param[in] service  The name of the destination service.
     736             :  *
     737             :  * \sa get_server()
     738             :  * \sa set_server()
     739             :  * \sa get_service()
     740             :  */
     741           2 : void message::set_service(std::string const & service)
     742             : {
     743           2 :     if(f_service != service)
     744             :     {
     745             :         // broadcast is a special case that the verify_message_name() does not
     746             :         // support
     747             :         //
     748           4 :         if(service != "*"
     749           2 :         && service != "?"
     750           4 :         && service != ".")
     751             :         {
     752             :             // this name can be empty and it supports lowercase
     753             :             //
     754           2 :             verify_message_name(service, true);
     755             :         }
     756             : 
     757           2 :         f_service = service;
     758           2 :         f_cached_message.clear();
     759             :     }
     760           2 : }
     761             : 
     762             : 
     763             : /** \brief Copy sent information to this message.
     764             :  *
     765             :  * This function copies the sent information found in message
     766             :  * to this message server and service names.
     767             :  *
     768             :  * This is an equivalent to the following two lines of code:
     769             :  *
     770             :  * \code
     771             :  *      reply.set_server(message.get_sent_from_server());
     772             :  *      reply.set_service(message.get_sent_from_service());
     773             :  * \endcode
     774             :  *
     775             :  * \param[in] original_message  The source message you want to reply to.
     776             :  */
     777           1 : void message::reply_to(message const & original_message)
     778             : {
     779           1 :     set_server(original_message.get_sent_from_server());
     780           1 :     set_service(original_message.get_sent_from_service());
     781           1 : }
     782             : 
     783             : 
     784             : /** \brief Get the command being sent.
     785             :  *
     786             :  * Each message is an equivalent to an RPC command being send between
     787             :  * services.
     788             :  *
     789             :  * The command is a string of text, generally one or more words
     790             :  * concatenated (no space allowed) such as STOP and LOCKENTERING.
     791             :  *
     792             :  * \note
     793             :  * The command string may still be empty if it was not yet assigned.
     794             :  *
     795             :  * \return The command of this message.
     796             :  */
     797          30 : std::string const & message::get_command() const
     798             : {
     799          30 :     return f_command;
     800             : }
     801             : 
     802             : 
     803             : /** \brief Set the message command.
     804             :  *
     805             :  * This function is used to define the RPC-like command of this message.
     806             :  *
     807             :  * The name of the command gets verified using the verify_message_name() function.
     808             :  * It cannot be empty and all letters have to be uppercase.
     809             :  *
     810             :  * \param[in] command  The command to send to a connection.
     811             :  *
     812             :  * \sa verify_message_name()
     813             :  */
     814           9 : void message::set_command(std::string const & command)
     815             : {
     816             :     // this name cannot be empty and it does not support lowercase
     817             :     // characters either
     818             :     //
     819           9 :     verify_message_name(command, false, false);
     820             : 
     821           9 :     if(f_command != command)
     822             :     {
     823           9 :         f_command = command;
     824           9 :         f_cached_message.clear();
     825             :     }
     826           9 : }
     827             : 
     828             : 
     829             : /** \brief Retrieve the message version this library was compiled with.
     830             :  *
     831             :  * This function returns the MESSAGE_VERSION that this library was
     832             :  * compiled with. Since we offer a shared object (.so) library, it
     833             :  * could be different from the version your application was compiled
     834             :  * with. If that's the case, your application may want to at least
     835             :  * warn the user about the discrepancy.
     836             :  *
     837             :  * \return The MESSAGE_VERSION at the time this library was compiled.
     838             :  */
     839           2 : message_version_t message::get_message_version() const
     840             : {
     841           2 :     return MESSAGE_VERSION;
     842             : }
     843             : 
     844             : 
     845             : /** \brief Check the version parameter.
     846             :  *
     847             :  * This function retrieves the version parameter which has to exist and
     848             :  * be an integer. If this is not the case, then an exception is raised.
     849             :  *
     850             :  * If the version is defined, it gets checked against this library's
     851             :  * compile time MESSAGE_VERSION variable. If equal, then the function
     852             :  * returns true. Otherwise, it returns false.
     853             :  *
     854             :  * In most cases, the very first message that you send with your service
     855             :  * should be such that it includes the version the libeventdispatcher
     856             :  * your program is linked against. In other words, you want to call
     857             :  * the add_version_parameter() in your sender and call this function
     858             :  * in your receiver.
     859             :  *
     860             :  * Make sure to design a way to disconnect cleanly so the other
     861             :  * party knows that the communication is interrupted because the
     862             :  * versions do not match. This allows your application to prevent
     863             :  * re-connecting over and over again when it knows it will fail
     864             :  * each time.
     865             :  *
     866             :  * \exception event_dispatcher_invalid_message
     867             :  * If you call this function and no version parameter was added to
     868             :  * the message, then this exception is raised.
     869             :  *
     870             :  * \return true if the version is present and valid.
     871             :  */
     872           1 : bool message::check_version_parameter() const
     873             : {
     874           1 :     return get_integer_parameter(MESSAGE_VERSION_NAME) == MESSAGE_VERSION;
     875             : }
     876             : 
     877             : 
     878             : /** \brief Add version parameter.
     879             :  *
     880             :  * Add a parameter named `"version"` with the current version of the
     881             :  * message protocol.
     882             :  *
     883             :  * In the snapcommunicator tool, this is sent over with the CONNECT
     884             :  * message. It allows the snapcommunicator to make sure they will
     885             :  * properly understand each others.
     886             :  */
     887           1 : void message::add_version_parameter()
     888             : {
     889           1 :     add_parameter(MESSAGE_VERSION_NAME, MESSAGE_VERSION);
     890           1 : }
     891             : 
     892             : 
     893             : /** \brief Add a string parameter to the message.
     894             :  *
     895             :  * Messages can include parameters (variables) such as a URI or a word.
     896             :  *
     897             :  * The value is not limited, although it probably should be limited to
     898             :  * standard text as these messages are sent as text. Especially, we
     899             :  * manage the '\0' character as the end of the message.
     900             :  *
     901             :  * The parameter name is verified by the verify_message_name() function.
     902             :  *
     903             :  * \param[in] name  The name of the parameter.
     904             :  * \param[in] value  The value of this parameter.
     905             :  *
     906             :  * \sa verify_message_name()
     907             :  */
     908           3 : void message::add_parameter(std::string const & name, std::string const & value)
     909             : {
     910           3 :     verify_message_name(name);
     911             : 
     912           3 :     f_parameters[name] = value;
     913           3 :     f_cached_message.clear();
     914           3 : }
     915             : 
     916             : 
     917             : /** \brief Add an integer parameter to the message.
     918             :  *
     919             :  * Messages can include parameters (variables) such as a URI or a word.
     920             :  *
     921             :  * The value is not limited, although it probably should be limited to
     922             :  * standard text as these messages are sent as text.
     923             :  *
     924             :  * The parameter name is verified by the verify_message_name() function.
     925             :  *
     926             :  * \param[in] name  The name of the parameter.
     927             :  * \param[in] value  The value of this parameter.
     928             :  *
     929             :  * \sa verify_message_name()
     930             :  */
     931           4 : void message::add_parameter(std::string const & name, int32_t value)
     932             : {
     933           4 :     verify_message_name(name);
     934             : 
     935           4 :     f_parameters[name] = std::to_string(value);
     936           4 :     f_cached_message.clear();
     937           4 : }
     938             : 
     939             : 
     940             : /** \brief Add an integer parameter to the message.
     941             :  *
     942             :  * Messages can include parameters (variables) such as a URI or a word.
     943             :  *
     944             :  * The value is not limited, although it probably should be limited to
     945             :  * standard text as these messages are sent as text.
     946             :  *
     947             :  * The parameter name is verified by the verify_message_name() function.
     948             :  *
     949             :  * \param[in] name  The name of the parameter.
     950             :  * \param[in] value  The value of this parameter.
     951             :  *
     952             :  * \sa verify_message_name()
     953             :  */
     954           1 : void message::add_parameter(std::string const & name, uint32_t value)
     955             : {
     956           1 :     verify_message_name(name);
     957             : 
     958           1 :     f_parameters[name] = std::to_string(value);
     959           1 :     f_cached_message.clear();
     960           1 : }
     961             : 
     962             : 
     963             : /** \brief Add an integer parameter to the message.
     964             :  *
     965             :  * Messages can include parameters (variables) such as a URI or a word.
     966             :  *
     967             :  * The value is not limited, although it probably should be limited to
     968             :  * standard text as these messages are sent as text.
     969             :  *
     970             :  * The parameter name is verified by the verify_message_name() function.
     971             :  *
     972             :  * \param[in] name  The name of the parameter.
     973             :  * \param[in] value  The value of this parameter.
     974             :  *
     975             :  * \sa verify_message_name()
     976             :  */
     977           1 : void message::add_parameter(std::string const & name, long long value)
     978             : {
     979           1 :     verify_message_name(name);
     980             : 
     981           1 :     f_parameters[name] = std::to_string(value);
     982           1 :     f_cached_message.clear();
     983           1 : }
     984             : 
     985             : 
     986             : /** \brief Add an integer parameter to the message.
     987             :  *
     988             :  * Messages can include parameters (variables) such as a URI or a word.
     989             :  *
     990             :  * The value is not limited, although it probably should be limited to
     991             :  * standard text as these messages are sent as text.
     992             :  *
     993             :  * The parameter name is verified by the verify_message_name() function.
     994             :  *
     995             :  * \param[in] name  The name of the parameter.
     996             :  * \param[in] value  The value of this parameter.
     997             :  *
     998             :  * \sa verify_message_name()
     999             :  */
    1000           1 : void message::add_parameter(std::string const & name, unsigned long long value)
    1001             : {
    1002           1 :     verify_message_name(name);
    1003             : 
    1004           1 :     f_parameters[name] = std::to_string(value);
    1005           1 :     f_cached_message.clear();
    1006           1 : }
    1007             : 
    1008             : 
    1009             : /** \brief Add an integer parameter to the message.
    1010             :  *
    1011             :  * Messages can include parameters (variables) such as a URI or a word.
    1012             :  *
    1013             :  * The value is not limited, although it probably should be limited to
    1014             :  * standard text as these messages are sent as text.
    1015             :  *
    1016             :  * The parameter name is verified by the verify_message_name() function.
    1017             :  *
    1018             :  * \param[in] name  The name of the parameter.
    1019             :  * \param[in] value  The value of this parameter.
    1020             :  *
    1021             :  * \sa verify_message_name()
    1022             :  */
    1023           1 : void message::add_parameter(std::string const & name, int64_t value)
    1024             : {
    1025           1 :     verify_message_name(name);
    1026             : 
    1027           1 :     f_parameters[name] = std::to_string(value);
    1028           1 :     f_cached_message.clear();
    1029           1 : }
    1030             : 
    1031             : 
    1032             : /** \brief Add an integer parameter to the message.
    1033             :  *
    1034             :  * Messages can include parameters (variables) such as a URI or a word.
    1035             :  *
    1036             :  * The value is not limited, although it probably should be limited to
    1037             :  * standard text as these messages are sent as text.
    1038             :  *
    1039             :  * The parameter name is verified by the verify_message_name() function.
    1040             :  *
    1041             :  * \param[in] name  The name of the parameter.
    1042             :  * \param[in] value  The value of this parameter.
    1043             :  *
    1044             :  * \sa verify_message_name()
    1045             :  */
    1046           1 : void message::add_parameter(std::string const & name, uint64_t value)
    1047             : {
    1048           1 :     verify_message_name(name);
    1049             : 
    1050           1 :     f_parameters[name] = std::to_string(value);
    1051           1 :     f_cached_message.clear();
    1052           1 : }
    1053             : 
    1054             : 
    1055             : /** \brief Check whether a parameter is defined in this message.
    1056             :  *
    1057             :  * This function checks whether a parameter is defined in a message. If
    1058             :  * so it returns true. This is important because the get_parameter()
    1059             :  * functions throw if the parameter is not available (i.e. which is
    1060             :  * what is used for mandatory parameters.)
    1061             :  *
    1062             :  * The parameter name is verified by the verify_message_name() function.
    1063             :  *
    1064             :  * \param[in] name  The name of the parameter.
    1065             :  *
    1066             :  * \return true if that parameter exists.
    1067             :  *
    1068             :  * \sa verify_message_name()
    1069             :  */
    1070          40 : bool message::has_parameter(std::string const & name) const
    1071             : {
    1072          40 :     verify_message_name(name);
    1073             : 
    1074          40 :     return f_parameters.find(name) != f_parameters.end();
    1075             : }
    1076             : 
    1077             : 
    1078             : /** \brief Retrieve a parameter as a string from this message.
    1079             :  *
    1080             :  * This function retrieves the named parameter from this message as a string,
    1081             :  * which is the default.
    1082             :  *
    1083             :  * The name must be valid as defined by the verify_message_name() function.
    1084             :  *
    1085             :  * \note
    1086             :  * This function returns a copy of the parameter so if you later change
    1087             :  * the value of that parameter, what has been returned does not change
    1088             :  * under your feet.
    1089             :  *
    1090             :  * \exception event_dispatcher_invalid_message
    1091             :  * This exception is raised whenever the parameter is not defined or
    1092             :  * if the parameter \p name is not considered valid.
    1093             :  *
    1094             :  * \param[in] name  The name of the parameter.
    1095             :  *
    1096             :  * \return A copy of the parameter value.
    1097             :  *
    1098             :  * \sa verify_message_name()
    1099             :  */
    1100          15 : std::string message::get_parameter(std::string const & name) const
    1101             : {
    1102          15 :     verify_message_name(name);
    1103             : 
    1104          15 :     auto const it(f_parameters.find(name));
    1105          15 :     if(it != f_parameters.end())
    1106             :     {
    1107          30 :         return it->second;
    1108             :     }
    1109             : 
    1110             :     throw event_dispatcher_invalid_message(
    1111             :               "message::get_parameter(): parameter \""
    1112           0 :             + name
    1113           0 :             + "\" of command \""
    1114           0 :             + f_command
    1115           0 :             + "\" not defined, try has_parameter() before calling"
    1116           0 :               " the get_parameter() function.");
    1117             : }
    1118             : 
    1119             : 
    1120             : /** \brief Retrieve a parameter as an integer from this message.
    1121             :  *
    1122             :  * This function retrieves the named parameter from this message as a string,
    1123             :  * which is the default.
    1124             :  *
    1125             :  * The name must be valid as defined by the verify_message_name() function.
    1126             :  *
    1127             :  * \exception event_dispatcher_invalid_message
    1128             :  * This exception is raised whenever the parameter is not a valid integer,
    1129             :  * it is not set, or the parameter name is not considered valid.
    1130             :  *
    1131             :  * \param[in] name  The name of the parameter.
    1132             :  *
    1133             :  * \return The parameter converted to an integer.
    1134             :  *
    1135             :  * \sa verify_message_name()
    1136             :  */
    1137          11 : std::int64_t message::get_integer_parameter(std::string const & name) const
    1138             : {
    1139          11 :     verify_message_name(name);
    1140             : 
    1141          11 :     auto const it(f_parameters.find(name));
    1142          11 :     if(it != f_parameters.end())
    1143             :     {
    1144          11 :         std::int64_t r;
    1145          11 :         if(!advgetopt::validator_integer::convert_string(it->second, r))
    1146             :         {
    1147             :             throw event_dispatcher_invalid_message(
    1148             :                       "message::get_integer_parameter(): command \""
    1149           0 :                     + f_command
    1150           0 :                     + "\" expected integer for \""
    1151           0 :                     + name
    1152           0 :                     + "\" but \""
    1153           0 :                     + it->second
    1154           0 :                     + "\" could not be converted.");
    1155             :         }
    1156          22 :         return r;
    1157             :     }
    1158             : 
    1159             :     throw event_dispatcher_invalid_message(
    1160             :                   "message::get_integer_parameter(): parameter \""
    1161           0 :                 + name
    1162           0 :                 + "\" of command \""
    1163           0 :                 + f_command
    1164           0 :                 + "\" not defined, try has_parameter() before calling"
    1165           0 :                   " the get_integer_parameter() function.");
    1166             : }
    1167             : 
    1168             : 
    1169             : /** \brief Retrieve the list of parameters from this message.
    1170             :  *
    1171             :  * This function returns a constant reference to the list of parameters
    1172             :  * defined in this message.
    1173             :  *
    1174             :  * This can be useful if you allow for variable lists of parameters, but
    1175             :  * generally the get_parameter() and get_integer_parameter() are preferred.
    1176             :  *
    1177             :  * \warning
    1178             :  * This is a direct reference to the list of parameter. If you call the
    1179             :  * add_parameter() function, the new parameter will be visible in that
    1180             :  * new list and an iterator is likely not going to be valid on return
    1181             :  * from that call.
    1182             :  *
    1183             :  * \return A constant reference to the list of message parameters.
    1184             :  *
    1185             :  * \sa get_parameter()
    1186             :  * \sa get_integer_parameter()
    1187             :  */
    1188           2 : message::parameters_t const & message::get_all_parameters() const
    1189             : {
    1190           2 :     return f_parameters;
    1191             : }
    1192             : 
    1193             : 
    1194             : /** \brief Verify various names used with messages.
    1195             :  *
    1196             :  * The messages use names for:
    1197             :  *
    1198             :  * \li commands
    1199             :  * \li services
    1200             :  * \li parameters
    1201             :  *
    1202             :  * All those names must be valid as per this function. They are checked
    1203             :  * on read and on write (i.e. add_parameter() and get_parameter() both
    1204             :  * check the parameter name to make sure you did not mistype it.)
    1205             :  *
    1206             :  * A valid name must start with a letter or an underscore (although
    1207             :  * we suggest you do not start names with underscores; we want to
    1208             :  * have those reserved for low level system like messages,) and
    1209             :  * it can only include letters, digits, and underscores.
    1210             :  *
    1211             :  * The letters are limited to uppercase for commands. Also certain
    1212             :  * names may be empty (See concerned functions for details on that one.)
    1213             :  *
    1214             :  * \note
    1215             :  * The allowed letters are 'a' to 'z' and 'A' to 'Z' only. The allowed
    1216             :  * digits are '0' to '9' only. The underscore is '_' only.
    1217             :  *
    1218             :  * A few valid names:
    1219             :  *
    1220             :  * \li commands: PING, STOP, LOCK, LOCKED, QUITTING, UNKNOWN, LOCKEXITING
    1221             :  * \li services: snapcommunicator, snapserver, snaplock, MyOwnService
    1222             :  * \li parameters: URI, name, IP, TimeOut
    1223             :  *
    1224             :  * At this point all our services use lowercase, but this is not enforced.
    1225             :  * Actually, mixed case or uppercase service names are allowed.
    1226             :  *
    1227             :  * \exception event_dispatcher_invalid_message
    1228             :  * This exception is raised if the name includes characters considered
    1229             :  * invalid.
    1230             :  *
    1231             :  * \param[in] name  The name of the parameter.
    1232             :  * \param[in] can_be_empty  Whether the name can be empty.
    1233             :  * \param[in] can_be_lowercase  Whether the name can include lowercase letters.
    1234             :  */
    1235          97 : void verify_message_name(std::string const & name, bool can_be_empty, bool can_be_lowercase)
    1236             : {
    1237         194 :     if(!can_be_empty
    1238          97 :     && name.empty())
    1239             :     {
    1240           0 :         std::string err("a message name cannot be empty.");
    1241           0 :         SNAP_LOG_FATAL
    1242           0 :             << err
    1243             :             << SNAP_LOG_SEND;
    1244           0 :         throw event_dispatcher_invalid_message(err);
    1245             :     }
    1246             : 
    1247         619 :     for(auto const & c : name)
    1248             :     {
    1249         522 :         if((c < 'a' || c > 'z' || !can_be_lowercase)
    1250          70 :         && (c < 'A' || c > 'Z')
    1251          33 :         && (c < '0' || c > '9')
    1252           3 :         && c != '_')
    1253             :         {
    1254           0 :             std::string err("a message name must be composed of ASCII"
    1255             :                             " 'a'..'z', 'A'..'Z', '0'..'9', or '_'"
    1256           0 :                             " only (also a command must be uppercase only,) \"");
    1257           0 :             err += name;
    1258           0 :             err += "\" is not valid.";
    1259           0 :             SNAP_LOG_FATAL
    1260           0 :                 << err
    1261             :                 << SNAP_LOG_SEND;
    1262           0 :             throw event_dispatcher_invalid_message(err);
    1263             :         }
    1264             :     }
    1265             : 
    1266         194 :     if(!name.empty()
    1267          97 :     && name[0] >= '0' && name[0] <= '9')
    1268             :     {
    1269           0 :         std::string err("parameter name cannot start with a digit, \"");
    1270           0 :         err += name;
    1271           0 :         err += "\" is not valid.";
    1272           0 :         SNAP_LOG_FATAL
    1273           0 :             << err
    1274             :             << SNAP_LOG_SEND;
    1275           0 :         throw event_dispatcher_invalid_message(err);
    1276             :     }
    1277          97 : }
    1278             : 
    1279             : 
    1280             : 
    1281             : 
    1282           6 : } // namespace ed
    1283             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13