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-08 02:52:36 Functions: 28 30 93.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12