LCOV - code coverage report
Current view: top level - eventdispatcher - message.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 118 276 42.8 %
Date: 2019-08-10 01:48:51 Functions: 28 30 93.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12