63ff539c1ec483396d609783dc0c550cb24ecd49
[libt2n] / src / command_client.cpp
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>
28
29 #include <boost/bind.hpp>
30
31 #include "command_client.hxx"
32
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36
37 using namespace std;
38
39 namespace libt2n
40 {
41
42 command_client::command_client(client_connection* _c, long long _command_timeout_usec, long long _hello_timeout_usec)
43     : c(_c)
44 {
45     command_timeout_usec=_command_timeout_usec;
46     hello_timeout_usec=_hello_timeout_usec;
47
48     // for reconnects
49     c->add_callback(new_connection,bind(&command_client::read_hello, boost::ref(*this)));
50
51     // don't expect hello from an always closed connection (like dummy_client_connection)
52     if (!is_connection_closed())
53     {
54         try
55         {
56             read_hello();
57         }
58         catch (t2n_communication_error &e)
59         {
60             c->close();
61
62             // store a copy of the exception that you can find out details about the error later
63             std::auto_ptr<t2n_exception> tmp(e.clone());
64             constructorException=tmp;
65         }
66         catch (...)
67         {
68             throw;
69         }
70     }
71 }
72
73 /** @brief replace the connection currently in use with a new one
74
75     @param _c pointer to the new connection
76
77     @note the old connection must still be valid when this method is called,
78           it can safely be deleted after this method returned
79
80     @note all callbacks registered on the old connection will be copied over
81           to the new one
82 */
83 void command_client::replace_connection(client_connection* _c)
84 {
85     // copy all callbacks registered on the old connection
86     for(callback_event_type e=static_cast<callback_event_type>(0);
87         e < __events_end;
88         e=static_cast<callback_event_type>(static_cast<int>(e)+1))
89     {
90         list<boost::function<void ()> > evcb=c->get_callback_list(e);
91
92         for (list<boost::function<void ()> >::iterator i=evcb.begin(); i != evcb.end(); i++)
93             _c->add_callback(e,*i);
94     }
95
96     // replace the connection
97     c=_c;
98
99     read_hello();
100 }
101
102 std::string command_client::read_packet(const long long &usec_timeout)
103 {
104     string resultpacket;
105     bool got_packet=false;
106     long long my_timeout=usec_timeout;
107     while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0  && !c->is_closed())
108         c->fill_buffer(my_timeout,&my_timeout);
109
110     if (!got_packet)
111         throw t2n_transfer_error("timeout exceeded");
112
113     return resultpacket;
114 }
115
116 void command_client::read_hello()
117 {
118     string resultpacket;
119     bool got_packet=false;
120     long long my_timeout=hello_timeout_usec;
121     while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0  && !c->is_closed())
122     {
123         c->fill_buffer(my_timeout,&my_timeout);
124
125         c->peek_packet(resultpacket);
126         check_hello(resultpacket);           // will throw before timeout if wrong data received
127     }
128
129     if (!got_packet)
130         throw t2n_transfer_error("timeout exceeded");
131
132     if (!check_hello(resultpacket))
133         throw t2n_version_mismatch("illegal hello received (incomplete): "+resultpacket);
134 }
135
136 bool command_client::check_hello(const string& hellostr)
137 {
138     istringstream hello(hellostr);
139
140     char chk;
141
142     if (hello.read(&chk,1))
143     {
144         if (chk != 'T')
145             throw t2n_version_mismatch("illegal hello received (T2N)");
146     }
147     else
148         return false;
149
150     if (hello.read(&chk,1))
151     {
152         if (chk != '2')
153             throw t2n_version_mismatch("illegal hello received (T2N)");
154     }
155     else
156         return false;
157
158     if (hello.read(&chk,1))
159     {
160         if (chk != 'N')
161             throw t2n_version_mismatch("illegal hello received (T2N)");
162     }
163     else
164         return false;
165
166     if (hello.read(&chk,1))
167     {
168         if (chk != 'v')
169             throw t2n_version_mismatch("illegal hello received (T2N)");
170     }
171     else
172         return false;
173
174     int prot_version;
175     if (hello >> prot_version)
176     {
177         if (prot_version != PROTOCOL_VERSION)
178             throw t2n_version_mismatch("not compatible with the server protocol version");
179     }
180     else
181         return false;
182
183     if (hello.read(&chk,1))
184     {
185         if (chk != ';')
186             throw t2n_version_mismatch("illegal hello received (1. ;)");
187     }
188     else
189         return false;
190
191     unsigned int hbo;
192     if (hello.read((char*)&hbo,sizeof(hbo)))
193     {
194         if (hbo != 1)
195             throw t2n_version_mismatch("host byte order not matching");
196     }
197     else
198         return false;
199
200     if (hello.read(&chk,1))
201     {
202         if (chk != ';')
203             throw t2n_version_mismatch("illegal hello received (2. ;)");
204     }
205     else
206         return false;
207
208     return true;
209 }
210
211 void command_client::send_command(command* cmd, result_container &res)
212 {
213     ostringstream ofs;
214     command_container cc(cmd);
215     boost::archive::binary_oarchive oa(ofs);
216
217     if (is_connection_closed())
218         throw t2n_transfer_error("connection to server is closed");
219
220     try
221     {
222         oa << cc;
223     }
224     catch(boost::archive::archive_exception &e)
225     {
226         ostringstream msg;
227         msg << "archive_exception while serializing on client-side, code " << e.code << " (" << e.what() << ")";
228         throw t2n_serialization_error(msg.str());
229     }
230     catch(...)
231         { throw; }
232
233     std::ostream* ostr;
234     if ((ostr=c->get_logstream(fulldebug))!=NULL)
235     {
236         (*ostr) << "sending command, decoded data: " << std::endl;
237         boost::archive::xml_oarchive xo(*ostr);
238         xo << BOOST_SERIALIZATION_NVP(cc);
239     }
240
241     c->write(ofs.str());
242
243     istringstream ifs(read_packet(command_timeout_usec));
244     boost::archive::binary_iarchive ia(ifs);
245
246     try
247     {
248         ia >> res;
249     }
250     catch(boost::archive::archive_exception &e)
251     {
252         ostringstream msg;
253         msg << "archive_exception while deserializing on client-side, code " << e.code << " (" << e.what() << ")";
254         throw t2n_serialization_error(msg.str());
255     }
256     catch(...)
257         { throw; }
258
259     if ((ostr=c->get_logstream(fulldebug))!=NULL)
260     {
261         (*ostr) << "received result, decoded data: " << std::endl;
262         boost::archive::xml_oarchive xo(*ostr);
263         xo << BOOST_SERIALIZATION_NVP(res);
264     }
265 }
266
267 }