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