- added OpenSSL dependency to build for password hash support
[barry.git] / src / packet.cc
blob7970b002e6e38f0cc14d5d1ea572368afbb170fb
1 ///
2 /// \file packet.cc
3 /// Low level protocol packet builder class.
4 /// Has knowledge of specific protocol commands in order
5 /// to hide protocol details behind an API.
6 ///
8 /*
9 Copyright (C) 2005-2007, Net Direct Inc. (http://www.netdirect.ca/)
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "packet.h"
25 #include "controller.h"
26 #include "protocol.h"
27 #include "protostructs.h"
28 #include "data.h"
29 #include "endian.h"
30 #include "parser.h"
31 #include "builder.h"
32 #include "error.h"
34 #define __DEBUG_MODE__
35 #include "debug.h"
38 namespace Barry {
40 //////////////////////////////////////////////////////////////////////////////
41 // ZeroPacket class
43 ZeroPacket::ZeroPacket(Data &send, Data &receive)
44 : Packet(send, receive)
48 ZeroPacket::~ZeroPacket()
53 // GetAttribute
55 /// Builds a command packet for the initial socket-0 handshakes
56 /// that fetch certain (some unknown) attributes. The attributes
57 /// appear to exist in an object/attribute sequence, so that's
58 /// how we address them here.
59 ///
60 void ZeroPacket::GetAttribute(unsigned int object, unsigned int attribute)
62 size_t size = SB_SOCKET_PACKET_HEADER_SIZE + ATTRIBUTE_FETCH_COMMAND_SIZE;
63 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(size));
64 Protocol::Packet &packet = *cpack;
66 packet.socket = 0;
67 packet.size = htobs(size);
68 packet.command = SB_COMMAND_FETCH_ATTRIBUTE;
69 packet.u.socket.socket = htobs(0x00ff); // default non-socket request
70 packet.u.socket.sequence = 0; // filled in by Socket class
71 packet.u.socket.u.fetch.object = htobs(object);
72 packet.u.socket.u.fetch.attribute = htobs(attribute);
74 m_send.ReleaseBuffer(size);
77 unsigned int ZeroPacket::ObjectID() const
79 Protocol::CheckSize(m_receive, SB_SOCKET_PACKET_HEADER_SIZE);
80 MAKE_PACKET(rpack, m_receive);
81 return btohs(rpack->u.socket.u.fetch.object);
84 unsigned int ZeroPacket::AttributeID() const
86 Protocol::CheckSize(m_receive, SB_SOCKET_PACKET_HEADER_SIZE);
87 MAKE_PACKET(rpack, m_receive);
88 return btohs(rpack->u.socket.u.fetch.attribute);
93 //////////////////////////////////////////////////////////////////////////////
94 // DBPacket class
96 DBPacket::DBPacket(Controller &con, Data &send, Data &receive)
97 : Packet(send, receive)
98 , m_con(con)
99 , m_last_dbop(0)
103 DBPacket::~DBPacket()
108 // ClearDatabase
110 /// Builds a command packet for the CLEAR_DATABASE command code, placing
111 /// the data in the send buffer.
113 void DBPacket::ClearDatabase(unsigned int dbId)
115 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(9));
116 Protocol::Packet &packet = *cpack;
118 packet.socket = htobs(m_con.m_socket.GetSocket());
119 packet.size = htobs(9);
120 packet.command = SB_COMMAND_DB_DATA;
121 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
122 packet.u.db.u.command.operation = SB_DBOP_CLEAR_DATABASE;
123 packet.u.db.u.command.databaseId = htobs(dbId);
125 m_send.ReleaseBuffer(9);
127 m_last_dbop = SB_DBOP_CLEAR_DATABASE;
131 // GetDBDB
133 /// Builds a command packet for the GET_DBDB command code, placing the
134 /// data in m_send.
136 void DBPacket::GetDBDB()
138 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(7));
139 Protocol::Packet &packet = *cpack;
141 packet.socket = htobs(m_con.m_socket.GetSocket());
142 packet.size = htobs(7);
143 packet.command = SB_COMMAND_DB_DATA;
144 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
145 // packet.u.db.u.command.operation = SB_DBOP_GET_DBDB;
146 packet.u.db.u.command.operation = SB_DBOP_OLD_GET_DBDB;
148 m_send.ReleaseBuffer(7);
150 m_last_dbop = SB_DBOP_OLD_GET_DBDB;
154 // GetRecordStateTable
156 /// Builds a command packet in the send buffer for the
157 /// GET_RECORD_STATE_TABLE command.
159 void DBPacket::GetRecordStateTable(unsigned int dbId)
161 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(9));
162 Protocol::Packet &packet = *cpack;
164 packet.socket = htobs(m_con.m_socket.GetSocket());
165 packet.size = htobs(9);
166 packet.command = SB_COMMAND_DB_DATA;
167 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
168 packet.u.db.u.command.operation = SB_DBOP_GET_RECORD_STATE_TABLE;
169 packet.u.db.u.command.databaseId = htobs(dbId);
171 m_send.ReleaseBuffer(9);
173 m_last_dbop = SB_DBOP_GET_RECORD_STATE_TABLE;
177 // SetRecordFlags
179 /// Builds a command packet in the send buffer for the SET_RECORD_FLAGS
180 /// command code.
182 /// FIXME - this API call is incomplete, since there are unknown flags
183 /// in the SetRecordFlags protocol packet. Currently it is only
184 /// used to set all flags to zero.
186 void DBPacket::SetRecordFlags(unsigned int dbId, unsigned int stateTableIndex,
187 uint8_t flag1)
189 size_t size = SB_PACKET_COMMAND_HEADER_SIZE + DBC_RECORD_FLAGS_SIZE;
190 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(size));
191 Protocol::Packet &packet = *cpack;
193 packet.socket = htobs(m_con.m_socket.GetSocket());
194 packet.size = htobs(size);
195 packet.command = SB_COMMAND_DB_DATA;
196 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
197 packet.u.db.u.command.operation = SB_DBOP_SET_RECORD_FLAGS;
198 packet.u.db.u.command.databaseId = htobs(dbId);
199 packet.u.db.u.command.u.flags.unknown = flag1;
200 packet.u.db.u.command.u.flags.index = htobs(stateTableIndex);
201 memset(packet.u.db.u.command.u.flags.unknown2, 0, sizeof(packet.u.db.u.command.u.flags.unknown2));
203 m_send.ReleaseBuffer(size);
205 m_last_dbop = SB_DBOP_SET_RECORD_FLAGS;
209 // DeleteRecordByIndex
211 /// Builds a command packet in the send buffer for the DELETE_RECORD_BY_INDEX
212 /// command code.
214 void DBPacket::DeleteRecordByIndex(unsigned int dbId, unsigned int stateTableIndex)
216 size_t size = SB_PACKET_COMMAND_HEADER_SIZE + DBC_RECORD_HEADER_SIZE;
217 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(size));
218 Protocol::Packet &packet = *cpack;
220 packet.socket = htobs(m_con.m_socket.GetSocket());
221 packet.size = htobs(size);
222 packet.command = SB_COMMAND_DB_DATA;
223 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
224 packet.u.db.u.command.operation = SB_DBOP_DELETE_RECORD_BY_INDEX;
225 packet.u.db.u.command.databaseId = htobs(dbId);
226 packet.u.db.u.command.u.record.recordIndex = htobs(stateTableIndex);
228 m_send.ReleaseBuffer(size);
230 m_last_dbop = SB_DBOP_DELETE_RECORD_BY_INDEX;
234 // GetRecordByIndex
236 /// Builds a command packet in the send buffer for the GET_RECORD_BY_INDEX
237 /// command code.
239 void DBPacket::GetRecordByIndex(unsigned int dbId, unsigned int stateTableIndex)
241 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(11));
242 Protocol::Packet &packet = *cpack;
244 packet.socket = htobs(m_con.m_socket.GetSocket());
245 packet.size = htobs(11);
246 packet.command = SB_COMMAND_DB_DATA;
247 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
248 packet.u.db.u.command.operation = SB_DBOP_GET_RECORD_BY_INDEX;
249 packet.u.db.u.command.databaseId = htobs(dbId);
250 packet.u.db.u.command.u.record.recordIndex = htobs(stateTableIndex);
252 m_send.ReleaseBuffer(11);
254 m_last_dbop = SB_DBOP_GET_RECORD_BY_INDEX;
258 // SetRecordByIndex
260 /// Builds a command packet in the m_send buffer for the SET_RECORD_BY_INDEX
261 /// command code.
263 /// \return bool
264 /// - true means success
265 /// - false means no data available from Builder object
267 bool DBPacket::SetRecordByIndex(unsigned int dbId, unsigned int stateTableIndex,
268 Builder &build)
270 // get new data if available
271 if( !build.Retrieve(dbId) )
272 return false;
274 // build packet data
275 size_t header_size = SB_PACKET_COMMAND_HEADER_SIZE + DBC_INDEXED_UPLOAD_HEADER_SIZE;
276 build.BuildFields(m_send, header_size);
277 size_t total_size = m_send.GetSize();
279 // fill in the header values
280 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(total_size));
281 Protocol::Packet &packet = *cpack;
283 packet.socket = htobs(m_con.m_socket.GetSocket());
284 packet.size = htobs(total_size);
285 packet.command = SB_COMMAND_DB_DATA;
286 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
287 packet.u.db.u.command.operation = SB_DBOP_SET_RECORD_BY_INDEX;
288 packet.u.db.u.command.databaseId = htobs(dbId);
289 packet.u.db.u.command.u.index_upload.unknown = 0;
290 packet.u.db.u.command.u.index_upload.index = htobs(stateTableIndex);
292 m_send.ReleaseBuffer(total_size);
294 m_last_dbop = SB_DBOP_SET_RECORD_BY_INDEX;
295 return true;
299 // GetRecords
301 /// Builds a command packet in the send buffer for the GET_RECORDS
302 /// command code.
304 void DBPacket::GetRecords(unsigned int dbId)
306 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(9));
307 Protocol::Packet &packet = *cpack;
309 packet.socket = htobs(m_con.m_socket.GetSocket());
310 packet.size = htobs(9);
311 packet.command = SB_COMMAND_DB_DATA;
312 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
313 packet.u.db.u.command.operation = SB_DBOP_OLD_GET_RECORDS;
314 packet.u.db.u.command.databaseId = htobs(dbId);
316 m_send.ReleaseBuffer(9);
318 m_last_dbop = SB_DBOP_OLD_GET_RECORDS;
322 // SetRecord
324 /// Builds a command packet in the m_send buffer for the SET_RECORD command
325 /// code.
327 /// \return bool
328 /// - true means success
329 /// - false means no data available from Builder object
331 bool DBPacket::SetRecord(unsigned int dbId, Builder &build)
333 // get new data if available
334 if( !build.Retrieve(dbId) )
335 return false;
337 // build packet data
338 size_t header_size = SB_PACKET_COMMAND_HEADER_SIZE + DBC_TAGGED_UPLOAD_HEADER_SIZE;
339 build.BuildHeader(m_send, header_size);
340 build.BuildFields(m_send, header_size);
341 size_t total_size = m_send.GetSize();
343 // fill in the header values
344 MAKE_PACKETPTR_BUF(cpack, m_send.GetBuffer(total_size));
345 Protocol::Packet &packet = *cpack;
347 packet.socket = htobs(m_con.m_socket.GetSocket());
348 packet.size = htobs(total_size);
349 packet.command = SB_COMMAND_DB_DATA;
350 packet.u.db.tableCmd = m_con.GetCommand(Controller::DatabaseAccess);
351 packet.u.db.u.command.operation = SB_DBOP_SET_RECORD;
352 packet.u.db.u.command.databaseId = htobs(dbId);
353 packet.u.db.u.command.u.tag_upload.rectype = build.GetRecType();
354 packet.u.db.u.command.u.tag_upload.uniqueId = htobl(build.GetUniqueId());
355 packet.u.db.u.command.u.tag_upload.unknown2 = 1; // unknown observed value
357 m_send.ReleaseBuffer(total_size);
359 m_last_dbop = SB_DBOP_SET_RECORD;
360 return true;
365 // Command
367 /// Returns the command value of the receive packet. If receive isn't
368 /// large enough, throws Error.
370 unsigned int DBPacket::Command() const
372 Protocol::CheckSize(m_receive);
373 MAKE_PACKET(rpack, m_receive);
374 return rpack->command;
377 // throws FIXME if packet doesn't support it
378 unsigned int DBPacket::ReturnCode() const
380 if( Command() == SB_COMMAND_DB_DONE ) {
381 Protocol::CheckSize(m_receive, SB_PACKET_DBACCESS_HEADER_SIZE + SB_DBACCESS_RETURN_CODE_SIZE);
382 MAKE_PACKET(rpack, m_receive);
383 return rpack->u.db.u.return_code;
385 else {
386 throw Error("Attempting to extract a return code from the wrong response packet type");
391 // DBOperation
393 /// Returns the database operation code from the receive packet, assuming
394 /// that receive contains a response packet. If receive isn't large
395 /// enough, throws Error.
397 unsigned int DBPacket::DBOperation() const
399 Protocol::CheckSize(m_receive, SB_PACKET_RESPONSE_HEADER_SIZE);
400 MAKE_PACKET(rpack, m_receive);
401 return rpack->u.db.u.response.operation;
405 // Parse
407 /// Parses the data in the receive buffer, and attempts to be smart about it,
408 /// using the last send command as guidance for what to expect in the
409 /// response.
411 /// \returns bool true - packet was recognized and parse was attempted
412 /// false - packet was not recognized
414 bool DBPacket::Parse(Parser &parser)
416 size_t offset = 0;
417 MAKE_PACKET(rpack, m_receive);
419 switch( m_last_dbop )
421 case SB_DBOP_OLD_GET_RECORDS:
422 case SB_DBOP_GET_RECORD_BY_INDEX:
423 parser.Clear();
425 offset = SB_PACKET_RESPONSE_HEADER_SIZE + DBR_OLD_TAGGED_RECORD_HEADER_SIZE;
426 Protocol::CheckSize(m_receive, offset);
427 // FIXME - this may need adjustment for email records... they
428 // don't seem to have uniqueID's
429 parser.SetIds(rpack->u.db.u.response.u.tagged.rectype,
430 btohl(rpack->u.db.u.response.u.tagged.uniqueId));
432 parser.ParseHeader(m_receive, offset);
433 parser.ParseFields(m_receive, offset);
434 parser.Store();
435 return true;
437 default: // unknown command
438 return false;
442 } // namespace Barry