libt2n: (gerd) fix client-connection-logic, finish wrappers, all tests are working...
[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 (!c->is_closed())
53         read_hello();
54 }
55
56 /** @brief replace the connection currently in use with a new one
57
58     @param _c pointer to the new connection
59
60     @note the old connection must still be valid when this method is called,
61           it can safely be deleted after this method returned
62
63     @note all callbacks registered on the old connection will be copied over
64           to the new one
65 */
66 void command_client::replace_connection(client_connection* _c)
67 {
68     // copy all callbacks registered on the old connection
69     for(callback_event_type e=static_cast<callback_event_type>(0);
70         e < __events_end;
71         e=static_cast<callback_event_type>(static_cast<int>(e)+1))
72     {
73         list<boost::function<void ()> > evcb=c->get_callback_list(e);
74
75         for (list<boost::function<void ()> >::iterator i=evcb.begin(); i != evcb.end(); i++)
76             _c->add_callback(e,*i);
77     }
78
79     // replace the connection
80     c=_c;
81
82     read_hello();
83 }
84
85 std::string command_client::read_packet(const long long &usec_timeout)
86 {
87     string resultpacket;
88     bool got_packet=false;
89     long long my_timeout=usec_timeout;
90     while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0  && !c->is_closed())
91         c->fill_buffer(my_timeout,&my_timeout);
92
93     if (!got_packet)
94         throw t2n_transfer_error("timeout exceeded");
95
96     return resultpacket;
97 }
98
99 void command_client::read_hello()
100 {
101     string resultpacket;
102     bool got_packet=false;
103     long long my_timeout=hello_timeout_usec;
104     while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0  && !c->is_closed())
105     {
106         c->fill_buffer(my_timeout,&my_timeout);
107
108         c->peek_packet(resultpacket);
109         check_hello(resultpacket);           // will throw before timeout if wrong data received
110     }
111
112     if (!got_packet)
113         throw t2n_transfer_error("timeout exceeded");
114
115     if (!check_hello(resultpacket))
116         throw t2n_version_mismatch("illegal hello received (incomplete): "+resultpacket);
117 }
118
119 bool command_client::check_hello(const string& hellostr)
120 {
121     istringstream hello(hellostr);
122
123     char chk;
124
125     if (hello.read(&chk,1))
126     {
127         if (chk != 'T')
128             throw t2n_version_mismatch("illegal hello received (T2N)");
129     }
130     else
131         return false;
132
133     if (hello.read(&chk,1))
134     {
135         if (chk != '2')
136             throw t2n_version_mismatch("illegal hello received (T2N)");
137     }
138     else
139         return false;
140
141     if (hello.read(&chk,1))
142     {
143         if (chk != 'N')
144             throw t2n_version_mismatch("illegal hello received (T2N)");
145     }
146     else
147         return false;
148
149     if (hello.read(&chk,1))
150     {
151         if (chk != 'v')
152             throw t2n_version_mismatch("illegal hello received (T2N)");
153     }
154     else
155         return false;
156
157     int prot_version;
158     if (hello >> prot_version)
159     {
160         if (prot_version != PROTOCOL_VERSION)
161             throw t2n_version_mismatch("not compatible with the server protocol version");
162     }
163     else
164         return false;
165
166     if (hello.read(&chk,1))
167     {
168         if (chk != ';')
169             throw t2n_version_mismatch("illegal hello received (1. ;)");
170     }
171     else
172         return false;
173
174     unsigned int hbo;
175     if (hello.read((char*)&hbo,sizeof(hbo)))
176     {
177         if (hbo != 1)
178             throw t2n_version_mismatch("host byte order not matching");
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 (2. ;)");
187     }
188     else
189         return false;
190
191     return true;
192 }
193
194 void command_client::send_command(command* cmd, result_container &res)
195 {
196     ostringstream ofs;
197     command_container cc(cmd);
198     boost::archive::binary_oarchive oa(ofs);
199
200     try
201     {
202         oa << cc;
203     }
204     catch(boost::archive::archive_exception &e)
205     {
206         ostringstream msg;
207         msg << "archive_exception while serializing on client-side, code " << e.code << " (" << e.what() << ")";
208         throw t2n_serialization_error(msg.str());
209     }
210     catch(...)
211         { throw; }
212
213     std::ostream* ostr;
214     if ((ostr=c->get_logstream(fulldebug))!=NULL)
215     {
216         (*ostr) << "sending command, decoded data: " << std::endl;
217         boost::archive::xml_oarchive xo(*ostr);
218         xo << BOOST_SERIALIZATION_NVP(cc);
219     }
220
221     c->write(ofs.str());
222
223     istringstream ifs(read_packet(command_timeout_usec));
224     boost::archive::binary_iarchive ia(ifs);
225
226     try
227     {
228         ia >> res;
229     }
230     catch(boost::archive::archive_exception &e)
231     {
232         ostringstream msg;
233         msg << "archive_exception while deserializing on client-side, code " << e.code << " (" << e.what() << ")";
234         throw t2n_serialization_error(msg.str());
235     }
236     catch(...)
237         { throw; }
238
239     if ((ostr=c->get_logstream(fulldebug))!=NULL)
240     {
241         (*ostr) << "received result, decoded data: " << std::endl;
242         boost::archive::xml_oarchive xo(*ostr);
243         xo << BOOST_SERIALIZATION_NVP(res);
244     }
245 }
246
247 }