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