Initial cmake conversion of the libt2n project except the example projects. The unit...
[libt2n] / src / command_client.cpp
CommitLineData
19facd85
TJ
1/*
2Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4The software in this package is distributed under the GNU General
5Public License version 2 (with a special exception described below).
6
7A copy of GNU General Public License (GPL) is included in this distribution,
8in the file COPYING.GPL.
9
10As a special exception, if other files instantiate templates or use macros
11or inline functions from this file, or you compile this file and link it
12with other works to produce a work based on this file, this file
13does not by itself cause the resulting work to be covered
14by the GNU General Public License.
15
16However the source code for this file must still be made available
17in accordance with section (3) of the GNU General Public License.
18
19This exception does not invalidate any other reasons why a work based
20on this file might be covered by the GNU General Public License.
21*/
7087e187
GE
22
23#include <string>
24#include <sstream>
25
26#include <boost/archive/binary_oarchive.hpp>
27#include <boost/archive/binary_iarchive.hpp>
28#include <boost/archive/xml_oarchive.hpp>
29#include <boost/archive/xml_iarchive.hpp>
30#include <boost/serialization/serialization.hpp>
7087e187 31
8104c8f7
GE
32#include <boost/bind.hpp>
33
7087e187
GE
34#include "command_client.hxx"
35
8104c8f7 36#include <config.h>
8104c8f7 37
7087e187
GE
38using namespace std;
39
40namespace libt2n
41{
42
696c95c2
TJ
43/**
44 * Constructor
45 * @param _c connection for this command. Ownership of the pointer is outside.
46 * @param _command_timeout_usec timeout until the command has to be completed
47 * @param _hello_timeout_usec timeout until hello has to be received
48 */
fb3345ad 49command_client::command_client(client_connection* _c, long long _command_timeout_usec, long long _hello_timeout_usec)
8104c8f7 50 : c(_c)
696c95c2 51 , constructorException(NULL)
8104c8f7 52{
45a2ebc9
GE
53 command_timeout_usec=_command_timeout_usec;
54 hello_timeout_usec=_hello_timeout_usec;
55
8104c8f7 56 // for reconnects
fb3345ad 57 c->add_callback(new_connection,bind(&command_client::read_hello, boost::ref(*this)));
8104c8f7 58
fb3345ad 59 // don't expect hello from an always closed connection (like dummy_client_connection)
b5922184
GE
60 if (!is_connection_closed())
61 {
62 try
63 {
64 read_hello();
65 }
66 catch (t2n_communication_error &e)
67 {
68 c->close();
69
70 // store a copy of the exception that you can find out details about the error later
696c95c2 71 constructorException = e.clone();
b5922184 72 }
b5922184 73 }
8104c8f7
GE
74}
75
696c95c2
TJ
76/**
77 * Destructor
78 */
79command_client::~command_client()
80{
81 if (constructorException)
82 {
83 delete constructorException;
84 constructorException = NULL;
85 }
86}
87
af84dfb5 88/** @brief replace the connection currently in use with a new one
fb3345ad 89 @param _c pointer to the new connection
af84dfb5
GE
90
91 @note the old connection must still be valid when this method is called,
92 it can safely be deleted after this method returned
93
94 @note all callbacks registered on the old connection will be copied over
95 to the new one
96*/
fb3345ad 97void command_client::replace_connection(client_connection* _c)
af84dfb5
GE
98{
99 // copy all callbacks registered on the old connection
100 for(callback_event_type e=static_cast<callback_event_type>(0);
101 e < __events_end;
102 e=static_cast<callback_event_type>(static_cast<int>(e)+1))
103 {
fb3345ad 104 list<boost::function<void ()> > evcb=c->get_callback_list(e);
af84dfb5
GE
105
106 for (list<boost::function<void ()> >::iterator i=evcb.begin(); i != evcb.end(); i++)
fb3345ad 107 _c->add_callback(e,*i);
af84dfb5
GE
108 }
109
110 // replace the connection
111 c=_c;
112
113 read_hello();
114}
115
487afb79
GE
116/** @brief return a complete packet
117 @param usec_timeout maximum microseconds to wait until the packet is complete
118 @retval packet data as std::string
119
120 @note throws a t2n_transfer_error if the timeout is exceeded
121*/
45a2ebc9 122std::string command_client::read_packet(const long long &usec_timeout)
8104c8f7 123{
8104c8f7 124 string resultpacket;
45a2ebc9
GE
125 bool got_packet=false;
126 long long my_timeout=usec_timeout;
fb3345ad
GE
127 while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0 && !c->is_closed())
128 c->fill_buffer(my_timeout,&my_timeout);
8104c8f7 129
45a2ebc9
GE
130 if (!got_packet)
131 throw t2n_transfer_error("timeout exceeded");
132
133 return resultpacket;
134}
135
487afb79
GE
136/** @brief read and check the hello message at the beginning of a connection
137
138 @note throws exceptions if something went wrong
139*/
45a2ebc9
GE
140void command_client::read_hello()
141{
b2ba0928
GE
142 string resultpacket;
143 bool got_packet=false;
144 long long my_timeout=hello_timeout_usec;
fb3345ad 145 while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0 && !c->is_closed())
b2ba0928 146 {
fb3345ad 147 c->fill_buffer(my_timeout,&my_timeout);
b2ba0928 148
fb3345ad 149 c->peek_packet(resultpacket);
b2ba0928
GE
150 check_hello(resultpacket); // will throw before timeout if wrong data received
151 }
152
153 if (!got_packet)
154 throw t2n_transfer_error("timeout exceeded");
155
156 if (!check_hello(resultpacket))
157 throw t2n_version_mismatch("illegal hello received (incomplete): "+resultpacket);
158}
159
487afb79
GE
160/** @brief check if a hello message is valid
161 @param hellostr std::string with the message to check
162 @retval true if the hello is good and complete
163
164 @note you can check incomplete hellos. you will get a false return value
165 but no exception. throws exceptions as soon as something is wrong.
166*/
167bool command_client::check_hello(const std::string& hellostr)
b2ba0928
GE
168{
169 istringstream hello(hellostr);
8104c8f7 170
b2ba0928
GE
171 char chk;
172
173 if (hello.read(&chk,1))
174 {
175 if (chk != 'T')
176 throw t2n_version_mismatch("illegal hello received (T2N)");
177 }
178 else
179 return false;
180
181 if (hello.read(&chk,1))
182 {
183 if (chk != '2')
184 throw t2n_version_mismatch("illegal hello received (T2N)");
185 }
186 else
187 return false;
188
189 if (hello.read(&chk,1))
190 {
191 if (chk != 'N')
192 throw t2n_version_mismatch("illegal hello received (T2N)");
193 }
194 else
195 return false;
196
197 if (hello.read(&chk,1))
198 {
199 if (chk != 'v')
200 throw t2n_version_mismatch("illegal hello received (T2N)");
201 }
202 else
203 return false;
8104c8f7
GE
204
205 int prot_version;
b2ba0928
GE
206 if (hello >> prot_version)
207 {
208 if (prot_version != PROTOCOL_VERSION)
209 throw t2n_version_mismatch("not compatible with the server protocol version");
210 }
211 else
212 return false;
213
214 if (hello.read(&chk,1))
215 {
216 if (chk != ';')
217 throw t2n_version_mismatch("illegal hello received (1. ;)");
218 }
219 else
220 return false;
8104c8f7 221
b2ba0928
GE
222 unsigned int hbo;
223 if (hello.read((char*)&hbo,sizeof(hbo)))
224 {
225 if (hbo != 1)
226 throw t2n_version_mismatch("host byte order not matching");
227 }
228 else
229 return false;
8104c8f7 230
b2ba0928
GE
231 if (hello.read(&chk,1))
232 {
233 if (chk != ';')
234 throw t2n_version_mismatch("illegal hello received (2. ;)");
235 }
236 else
237 return false;
8104c8f7 238
b2ba0928 239 return true;
8104c8f7
GE
240}
241
487afb79
GE
242/** @brief send a command to the server and store the result
243 @param cmd pointer to a command-object
244 @param[out] res result container to store the result in
245
246 @note you can check incomplete hellos. you will get a false return value
247 but no exception. throws exceptions as soon as something is wrong.
248*/
7087e187
GE
249void command_client::send_command(command* cmd, result_container &res)
250{
251 ostringstream ofs;
252 command_container cc(cmd);
253 boost::archive::binary_oarchive oa(ofs);
254
b5922184
GE
255 if (is_connection_closed())
256 throw t2n_transfer_error("connection to server is closed");
257
45a2ebc9
GE
258 try
259 {
260 oa << cc;
261 }
262 catch(boost::archive::archive_exception &e)
263 {
264 ostringstream msg;
265 msg << "archive_exception while serializing on client-side, code " << e.code << " (" << e.what() << ")";
266 throw t2n_serialization_error(msg.str());
267 }
7087e187 268
d535333f 269 std::ostream* ostr;
fb3345ad 270 if ((ostr=c->get_logstream(fulldebug))!=NULL)
d535333f
GE
271 {
272 (*ostr) << "sending command, decoded data: " << std::endl;
273 boost::archive::xml_oarchive xo(*ostr);
274 xo << BOOST_SERIALIZATION_NVP(cc);
696c95c2 275 }
d535333f 276
fb3345ad 277 c->write(ofs.str());
7087e187 278
45a2ebc9 279 istringstream ifs(read_packet(command_timeout_usec));
7087e187
GE
280 boost::archive::binary_iarchive ia(ifs);
281
45a2ebc9
GE
282 try
283 {
284 ia >> res;
285 }
286 catch(boost::archive::archive_exception &e)
287 {
288 ostringstream msg;
289 msg << "archive_exception while deserializing on client-side, code " << e.code << " (" << e.what() << ")";
290 throw t2n_serialization_error(msg.str());
291 }
d535333f 292
fb3345ad 293 if ((ostr=c->get_logstream(fulldebug))!=NULL)
d535333f
GE
294 {
295 (*ostr) << "received result, decoded data: " << std::endl;
296 boost::archive::xml_oarchive xo(*ostr);
297 xo << BOOST_SERIALIZATION_NVP(res);
298 }
7087e187
GE
299}
300
301}